File Coverage

blib/lib/Plack/Middleware/HatenaOAuth.pm
Criterion Covered Total %
statement 63 63 100.0
branch 12 16 75.0
condition 3 8 37.5
subroutine 16 16 100.0
pod 2 2 100.0
total 96 105 91.4


line stmt bran cond sub pod time code
1             package Plack::Middleware::HatenaOAuth;
2 3     3   120498 use strict;
  3         7  
  3         71  
3 3     3   12 use warnings;
  3         7  
  3         100  
4              
5             our $VERSION = '0.02';
6              
7 3     3   348 use parent 'Plack::Middleware';
  3         243  
  3         14  
8 3     3   11375 use Plack::Util::Accessor qw(consumer_key consumer_secret consumer login_path);
  3         6  
  3         14  
9 3     3   563 use Plack::Request;
  3         59003  
  3         82  
10 3     3   1062 use Plack::Session;
  3         1362  
  3         75  
11              
12 3     3   1297 use OAuth::Lite::Consumer;
  3         130655  
  3         23  
13 3     3   1771 use JSON::XS;
  3         7512  
  3         191  
14              
15             use constant +{
16 3         1985 SITE => q{https://www.hatena.com},
17             REQUEST_TOKEN_PATH => q{/oauth/initiate},
18             ACCESS_TOKEN_PATH => q{/oauth/token},
19             AUTHORIZE_PATH => q{https://www.hatena.ne.jp/oauth/authorize},
20             USER_INFO_URL => q{https://n.hatena.ne.jp/applications/my.json},
21 3     3   18 };
  3         7  
22              
23             sub prepare_app {
24 2     2 1 196 my ($self) = @_;
25 2 50 33     8 die join(
26             "\n",
27             'No consumer_key or consumer_secret specified.',
28             'Get one by following the instructions on http://developer.hatena.ne.jp/en/documents/auth/apis/oauth/consumer',
29             ) unless $self->consumer_key and $self->consumer_secret;
30              
31             $self->consumer(OAuth::Lite::Consumer->new(
32             consumer_key => $self->consumer_key,
33             consumer_secret => $self->consumer_secret,
34             site => SITE,
35             request_token_path => REQUEST_TOKEN_PATH,
36             access_token_path => ACCESS_TOKEN_PATH,
37             authorize_path => AUTHORIZE_PATH,
38 2 50       101 ($self->{ua} ? (ua => $self->{ua}) : ()),
39             ));
40             }
41              
42             sub _get_request_token {
43 3     3   919 my ($self, $callback_url) = @_;
44 3         10 return $self->consumer->get_request_token(
45             callback_url => $callback_url,
46             scope => 'read_public',
47             );
48             }
49              
50             sub _get_access_token {
51 2     2   19 my ($self, $verifier, $request_token) = @_;
52 2         6 return $self->consumer->get_access_token(
53             token => $request_token,
54             verifier => $verifier,
55             );
56             }
57              
58             sub _get_user_info {
59 1     1   2 my ($self, $access_token) = @_;
60 1         3 my $res = $self->consumer->request(
61             method => 'POST',
62             url => USER_INFO_URL,
63             token => $access_token,
64             );
65 1 50       2217 $res->is_success or return;
66 1   33     9 return eval { decode_json($res->decoded_content || $res->content) };
  1         3  
67             }
68              
69             sub _error {
70 2     2   5485 my ($self, $code, $message) = @_;
71             return [
72 2         23 $code,
73             [ 'Content-Type' => 'text/plain' ],
74             [ $message ],
75             ];
76             }
77              
78             sub _login_handler {
79 5     5   74 my ($self, $env) = @_;
80 5         26 my $session = Plack::Session->new($env);
81 5         45 my $req = Plack::Request->new($env);
82 5         37 my $res = $req->new_response(200);
83 5         2802 my $consumer = $self->consumer;
84 5         29 my $verifier = $req->parameters->{oauth_verifier};
85              
86 5 100       720 if (!$verifier) {
87 3 100       10 my $request_token = $self->_get_request_token(
88             [ split /\?/, $req->uri, 2]->[0],
89             ) or return $self->_error(500, sprintf(
90             "Could not get an OAuth request token from %s\nMessage: %s",
91             SITE,
92             $consumer->errstr,
93             ));
94              
95 2         6878 $session->set(hatenaoauth_request_token => $request_token);
96 2         892 $session->set(hatenaoauth_location => $req->parameters->{location});
97 2         832 $res->redirect($consumer->url_to_authorize(token => $request_token));
98             } else {
99 2 100       9 my $access_token = $self->_get_access_token(
100             $verifier,
101             $session->get('hatenaoauth_request_token'),
102             ) or return $self->_error(500, sprintf(
103             "Could not get an OAuth access token from %s\nMessage: %s",
104             SITE,
105             $consumer->errstr,
106             ));
107 1         2575 $session->remove('hatenaoauth_request_token');
108              
109 1         23 my $user_info = $self->_get_user_info($access_token);
110 1 50       105 $session->set('hatenaoauth_user_info', $user_info) if $user_info;
111 1   50     13 $res->redirect($session->get('hatenaoauth_location') || '/');
112 1         82 $session->remove('hatenaoauth_location');
113             }
114              
115 3         674 return $res->finalize;
116             }
117              
118             sub call {
119 6     6 1 54374 my ($self, $env) = @_;
120 6         45 my $req = Plack::Request->new($env);
121 6 100       111 return $self->_login_handler($env) if $req->path eq $self->login_path;
122 1         26 return $self->app->($env);
123             }
124              
125             1;
126              
127             __END__