File Coverage

blib/lib/WebService/Livedoor/Auth.pm
Criterion Covered Total %
statement 36 40 90.0
branch 2 6 33.3
condition n/a
subroutine 11 11 100.0
pod n/a
total 49 57 85.9


line stmt bran cond sub pod time code
1             package WebService::Livedoor::Auth;
2              
3 4     4   4107 use warnings;
  4         8  
  4         147  
4 4     4   21 use strict;
  4         8  
  4         208  
5 4     4   21 use Carp;
  4         7  
  4         357  
6 4     4   5524 use URI;
  4         41785  
  4         127  
7 4     4   3783 use Digest::HMAC_SHA1;
  4         38878  
  4         167  
8 4     4   33 use Scalar::Util qw(blessed);
  4         9  
  4         428  
9 4     4   4273 use Params::Validate qw(:all);
  4         50385  
  4         1125  
10 4     4   4993 use LWP::UserAgent;
  4         220420  
  4         145  
11 4     4   42 use base qw(Class::Accessor::Fast Class::ErrorHandler);
  4         8  
  4         3761  
12              
13             our $VERSION = '0.01';
14             our $BASE_URL = 'http://auth.livedoor.com/';
15             our $DEFAULT_TIMEOUT = 60 * 5;
16              
17             __PACKAGE__->mk_accessors(qw(app_key secret ver timeout));
18              
19             # copy from Hatena::API::Auth.
20             BEGIN {
21 4     4   19240 use Carp;
  4         9  
  4         721  
22 4     4   48 our $HAVE_JSON_SYCK;
23 4         8 eval { require JSON::Syck; $HAVE_JSON_SYCK = 1 };
  4         5283  
  0         0  
24 4 50       36 eval { require JSON } unless $HAVE_JSON_SYCK;
  4         12498  
25 4 50       4335 Carp::croak("JSON::Syck or JSON required to use " . __PACKAGE__) if $@;
26             *_parse_json =
27 0           $HAVE_JSON_SYCK ? sub { JSON::Syck::Load($_[1]) }
28 0 0         : sub { JSON::jsonToObj($_[1]) };
  0            
29             }
30              
31             sub new {
32             my $class = shift;
33             my %p = validate(@_, {
34             app_key => { type => SCALAR },
35             secret => { type => SCALAR },
36             ver => { type => SCALAR, optional => 1, default => '1.0' },
37             timeout => { type => SCALAR, optional => 1,
38             default => 60 * 10, regex => qr/^\d+$/, }
39             });
40             my $self = bless \%p, $class;
41             $self;
42             }
43              
44             sub uri_to_login {
45             my $self = shift;
46             my %p = validate(@_, {
47             perms => { optional => 1, type => SCALAR,
48             regex => qr/^(userhash|id)$/, default => 'userhash', },
49             userdata => { optional => 1, type => SCALAR },
50             t => { optional => 1, type => SCALAR, regex => qr/^\d+$/ },
51             });
52             my %query = (
53             v => $self->ver,
54             app_key => $self->app_key,
55             perms => $p{perms},
56             );
57             $query{t} = $p{t} || time;
58             $query{userdata} = $p{userdata} if exists $p{userdata};
59             $query{sig} = $self->calc_sig(\%query);
60             my $uri = URI->new_abs('/login/', $BASE_URL);
61             $uri->query_form(%query);
62             $uri;
63             }
64              
65             sub validate_response {
66             my($self, $q) = @_;
67             $q = _normalize_query($q);
68             if ($q->{sig} eq $self->calc_sig($q)) {
69             if (abs(time - $q->{t}) > $self->timeout) {
70             return $self->error('LOCAL TIMEOUT');
71             }
72             my $user = WebService::Livedoor::Auth::User->new;
73             $user->userdata($q->{userdata});
74             $user->userhash($q->{userhash});
75             $user->token($q->{token});
76             return $user;
77             }
78             else {
79             return $self->error('INVALID SIG');
80             }
81             }
82              
83             sub get_livedoor_id {
84             my($self, $user) = @_;
85             $self->call_auth_rpc($user) or return $self->error($self->errstr);
86             $user->livedoor_id;
87             }
88              
89             sub call_auth_rpc {
90             my($self, $user) = @_;
91             my %query = (
92             app_key => $self->app_key,
93             v => $self->ver,
94             format => 'json',
95             token => $user->token,
96             t => time,
97             );
98             $query{sig} = $self->calc_sig(\%query);
99             my $uri = URI->new_abs('/rpc/auth', $BASE_URL);
100             my $res = $self->ua->post($uri->as_string, \%query);
101             return $self->error($res->status_line) unless $res->is_success;
102             my $json = $self->_parse_json($res->content);
103             if ($json->{error}) {
104             return $self->error($json->{message});
105             }
106             $user->livedoor_id($json->{user}->{livedoor_id});
107             return $user;
108             }
109              
110              
111             sub calc_sig {
112             my($self, $q) = @_;
113             my $context = Digest::HMAC_SHA1->new($self->secret);
114             $q = _normalize_query($q);
115             for my $key(sort { $a cmp $b} keys %{$q}) {
116             next if $key eq 'sig';
117             $context->add($key);
118             $context->add($q->{$key});
119             }
120             return $context->hexdigest;
121             }
122              
123             sub ua {
124             my $self = shift;
125             my $ua = LWP::UserAgent->new;
126             $ua->parse_head(0);
127             $ua->env_proxy;
128             $ua->agent(join '/', ref $self, $VERSION);
129             $ua;
130             }
131              
132             sub _normalize_query {
133             my $q = shift;
134             if (blessed $q && $q->can('param')) {
135             my %q = map {
136             $_ => scalar $q->param($_),
137             } $q->param;
138             return \%q;
139             }
140             $q;
141             }
142              
143             package WebService::Livedoor::Auth::User;
144             use strict;
145             use base qw(Class::Accessor::Fast);
146             __PACKAGE__->mk_accessors(qw(token userhash userdata livedoor_id));
147              
148             1;
149              
150             __END__