Revision 246
Date:
2012/09/18 18:31:16
Author:
ahitrov
Revision Log:
Google+ auth
Files:
Legend:
Added
Removed
Modified
utf8/plugins/session/comps/www/oauth/google.html
1
<script type="text/javascript">
2
<!--
3
opener.location.reload(true);
4
close();
5
//-->
6
</script>
7
<pre><% Dumper($google_connect) %></pre>
8
<%doc>
9
10
use LWP::UserAgent;
11
use JSON::XS;
12
use URI;
13
use Encode;
14
use URI;
15
use URI::QueryParam;
16
my $JSON = JSON::XS->new->utf8;
17
18
Manual redirect:
19
use session::AUTH::Google;
20
my $site = $state->development ? 'www' : 'www';
21
my $google_connect = session::AUTH::Google->new(
22
google_redirect_uri => 'http://'.$site.'/oauth/google.html',
23
);
24
25
26
</%doc>
27
<%once>
28
29
my $site = $state->development ? '' : '';
30
use session::AUTH::Google;
31
32
</%once>
33
<%args>
34
35
$code => undef
36
37
</%args>
38
<%init>
39
40
my $res;
41
my $info;
42
43
my $google_connect = session::AUTH::Google->new;
44
my $auth_url = $google_connect->authorize_url;
45
if ( $code ) {
46
my $local_session = $google_connect->authenticate( code => $code );
47
if ( ref $local_session && exists $local_session->{id} ) {
48
my $profile = $keeper->{users}->get_profile( id => $local_session->{id} ) if exists $keeper->{users};
49
if ( ref $profile ) {
50
unless ( exists $local_session->{avatar} ) {
51
my $avatar = $profile->get_image('avatar');
52
$session->{avatar} = ref $avatar && exists $avatar->{filename} ? $avatar->{mini}{'54x54'}{filename} : undef;
53
$keeper->{session}->store_value (
54
name => $profile->name_full,
55
last_name => $profile->name_family,
56
first_name => $profile->name_part,
57
avatar => $session->{avatar},
58
);
59
} else {
60
$keeper->{session}->store_value (
61
name => $profile->name_full,
62
last_name => $profile->name_family,
63
first_name => $profile->name_part,
64
);
65
}
66
}
67
}
68
} elsif ( $auth_url ) {
69
$m->redirect($auth_url->as_string);
70
} else {
71
&abort404 unless $DEBUG;
72
}
73
74
</%init>
utf8/plugins/session/config.proto
77
77
REWRITE += MAILRU_APP_ID MAILRU_APP_SECRET MAILRU_REDIRECT_URL MAILRU_USER_POST_URL
78
78
79
79
80
### AUTH::Google
81
######################################
82
GOOGLE_APP_ID =
83
GOOGLE_APP_SECRET =
84
GOOGLE_REDIRECT_URL =
85
GOOGLE_USER_POST_URL =
86
87
REWRITE += GOOGLE_APP_ID GOOGLE_APP_SECRET GOOGLE_REDIRECT_URL GOOGLE_USER_POST_URL
88
89
# Optional. For email request:
90
GOOGLE_SCOPE = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
91
REWRITE += GOOGLE_SCOPE
92
80
93
CONNECTION_TIMEOUT = 3
81
94
95
82
96
PROJECT_REQUIRED += Crypt-SSLeay
83
utf8/plugins/session/lib/session/AUTH/Google.pm
1
package session::AUTH::Google;
2
3
use strict;
4
use warnings;
5
use LWP::UserAgent;
6
use JSON::XS;
7
use Data::Dumper;
8
use URI;
9
use URI::QueryParam;
10
use Encode;
11
use Digest::MD5 qw/ md5_hex /;
12
use Contenido::Globals;
13
14
use vars qw($VERSION);
15
$VERSION = '4.1';
16
17
=for rem
18
facebook:
19
auto_create_user: 1
20
app_id: string
21
app_secret: 32 hex digits
22
authorize_url: https://accounts.google.com/o/oauth2/auth
23
access_token_url: https://accounts.google.com/o/oauth2/token
24
user_info_url: https://www.googleapis.com/oauth2/v1/userinfo
25
user_post_url: ~
26
state: is passed back to your app as a parameter of the redirect_uri when the user completed the authentication
27
store:
28
class: "+Comments::Authentication::Store"
29
type: facebook
30
31
=cut
32
33
our $JSON = JSON::XS->new->utf8;
34
35
=for rem SCHEMA
36
37
$m->redirect ( $g_connect->authorize_url( redirect_uri => ... )->as_string );
38
39
40
=cut
41
42
43
sub new {
44
my ($class, %config) = @_;
45
my $self = bless {}, $class;
46
47
$self->{google_authorize_url} = 'https://accounts.google.com/o/oauth2/auth';
48
$self->{google_access_token_url} = 'https://accounts.google.com/o/oauth2/token';
49
$self->{google_user_info_url} = 'https://www.googleapis.com/oauth2/v2/userinfo';
50
# $self->{scope} = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
51
$self->{scope} = $config{scope} || $state->{session}{google_scope} || 'https://www.googleapis.com/auth/userinfo.profile';
52
53
for (qw(google_app_id google_app_secret)) {
54
$self->{$_} = $config{$_} || $state->{session}{$_} || return undef;
55
}
56
$self->{timeout} = $state->{session}{connection_timeout} || 3;
57
for (qw(google_user_post_url google_redirect_uri)) {
58
$self->{$_} = $config{$_} || $state->{session}{$_};
59
}
60
return $self;
61
}
62
63
sub authorize_url {
64
my $self = shift;
65
my (%args) = @_;
66
my $go = URI->new( $self->{google_authorize_url} );
67
$go->query_param( client_id => $self->{google_app_id} );
68
# $go->query_param( state => $args{state} ) if $args{state};
69
$go->query_param( response_type => "code" );
70
$go->query_param( scope => $self->{scope} );
71
$args{redirect_uri} ||= $self->{google_redirect_uri};
72
for ( keys %args ) {
73
$go->query_param( $_ => $args{$_} );
74
}
75
warn Dumper($go) if $DEBUG;
76
return $go;
77
}
78
79
sub authenticate {
80
my ( $self, %authinfo ) = @_;
81
warn "Google.authenticate" if $DEBUG;
82
83
my $local_session = $session || $keeper->{session}->get_session;
84
my $redirect_uri = $self->{google_redirect_uri};
85
86
my $access_token = $local_session->{google_access_token} || $local_session->{google_refresh_token};
87
my $expires = $local_session->{google_expires};
88
if ($access_token and $expires > time) {
89
warn "Already have access_token" if $DEBUG;
90
} else {
91
undef $access_token;
92
}
93
my $code = $authinfo{'code'};
94
unless ( $code ) {
95
warn "Call to authenticate without code";
96
return undef;
97
}
98
my $ua = LWP::UserAgent->new;
99
$ua->timeout($self->{timeout});
100
unless ($access_token) {
101
my $req = URI->new( $self->{google_access_token_url});
102
my %post_params = (
103
code => $code,
104
client_id => $self->{google_app_id},
105
client_secret => $self->{google_app_secret},
106
redirect_uri => $redirect_uri,
107
grant_type => 'authorization_code',
108
);
109
warn "Post: $req".Dumper(\%post_params) if $DEBUG;
110
my $res = $ua->post($req, \%post_params);
111
unless ($res->code == 200) {
112
warn "access_token request failed: ".$res->status_line;
113
return undef;
114
}
115
my $info = $JSON->decode($res->content);
116
unless ( ref $info eq 'HASH' && ($access_token = $info->{access_token}) ) {
117
warn "No access token in response: ".$res->content."\n";
118
return undef;
119
}
120
$keeper->{session}->store_value(
121
google_access_token => $access_token,
122
google_refresh_token => $info->{refresh_token},
123
);
124
$local_session->{google_access_token} = $access_token;
125
$local_session->{google_refresh_token} = $info->{refresh_token} if $info->{refresh_token};
126
if( my $expires = $info->{expires_in} ) {
127
$local_session->{google_expires} = time + $expires;
128
$keeper->{session}->store_value( google_expires => $local_session->{google_expires} );
129
} else {
130
#$c->user_session->{'expires'} = time + 3600*24;
131
}
132
warn "Google: requested access token: $access_token" if $DEBUG;
133
} else {
134
warn "Google: have access token" if $DEBUG;
135
}
136
137
my $req = URI->new( $self->{google_user_info_url} );
138
$req->query_param( access_token => $access_token );
139
$ua->credentials("googleapis.com:80", "Authorization", "Bearer", $access_token);
140
141
warn "Fetching user $req" if $DEBUG;
142
my $res = $ua->get($req);
143
unless ($res->code == 200) {
144
warn "user request failed: ".$res->status_line;
145
return undef;
146
}
147
my $info;
148
unless ( $info = eval { $JSON->decode($res->content) } ) {
149
warn "user '".$res->content."' decode failed: $@";
150
return undef;
151
}
152
153
foreach my $key ( qw(name family_name given_name) ) {
154
$info->{$key} = Encode::encode('utf-8', $info->{$key});
155
}
156
warn "Userhash = ".Dumper($info) if $DEBUG;
157
158
my @plugins = split (/[\ |\t]+/, $state->{plugins});
159
my $name = $info->{name};
160
if ( grep { $_ eq 'users' } @plugins ) {
161
my $user = $keeper->{users}->get_profile( email => $info->{email} ) if $info->{email};
162
$user ||= $keeper->{users}->get_profile( login => 'google:'.$info->{id} );
163
unless ( ref $user ) {
164
my $user_class = $state->{users}->profile_document_class;
165
$user = $user_class->new( $keeper );
166
$user->login( $info->{email} || 'google:'.$info->{id} );
167
$user->name( $name );
168
$user->status( 1 );
169
$user->type( 0 );
170
$user->login_method('google');
171
if ( $info->{locale} ) {
172
$user->country( $info->{locale} );
173
}
174
if ( $info->{birthday} && $info->{birthday} =~ /(\d{2})\.(\d{2})\.(\d{4})/ ) {
175
$user->dtime( "$3-$2-$1" );
176
}
177
$user->email( $info->{email} || undef );
178
179
my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
180
if ( ref $prop_ava && $info->{picture} ) {
181
my $avatar = $user->_store_image( $info->{picture}, attr => 'avatar' );
182
$user->avatar( $user->_serialize($avatar) );
183
}
184
185
$user->store;
186
} else {
187
my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
188
if ( ref $prop_ava ) {
189
my $avatar = $user->get_image( 'avatar' );
190
if ( $info->{picture} && !(ref $avatar && exists $avatar->{filename}) ) {
191
my $avatar = $user->_store_image( $info->{picture}, attr => 'avatar' );
192
$user->avatar( $user->_serialize($avatar) );
193
$user->store;
194
}
195
}
196
}
197
my %data = (
198
id => $user->id,
199
name => $user->name,
200
login => $user->login,
201
email => $user->email,
202
status => $user->status,
203
type => $user->type,
204
ltime => time,
205
avatar => $info->{picture},
206
);
207
$keeper->{session}->store_value ( %data );
208
while ( my ( $key, $value ) = each %data ) {
209
$local_session->{$key} = $value;
210
}
211
} else {
212
my %data = (
213
id => $info->{id},
214
name => $name,
215
gender => $info->{gender} ? ($info->{gender} eq 'male' ? 'm' : $info->{gender} eq 'female' ? 'f' : undef) : undef,
216
email => $info->{email},
217
login => $info->{email} || 'google:'.$info->{id},
218
status => 1,
219
type => 0,
220
auth_by => 'google',
221
ltime => time,
222
);
223
if ( $user->{picture} ) {
224
$data{avatar} = $info->{picture};
225
}
226
$keeper->{session}->store_value ( %data );
227
while ( my ( $key, $value ) = each %data ) {
228
$local_session->{$key} = $value;
229
}
230
}
231
return $local_session;
232
}
233
234
1;
utf8/plugins/session/lib/session/Init.pm
8
8
use session::AUTH::FaceBook;
9
9
use session::AUTH::VKontakte;
10
10
use session::AUTH::Mailru;
11
use session::AUTH::Google;
11
12
12
13
# загрузка всех необходимых плагину классов
13
14
# session::SQL::SomeTable
utf8/plugins/session/lib/session/State.pm.proto
56
56
$self->{mailru_redirect_uri} = '@MAILRU_REDIRECT_URL@';
57
57
$self->{mailru_user_post_url} = '@MAILRU_USER_POST_URL@';
58
58
59
$self->{google_app_id} = '@GOOGLE_APP_ID@';
60
$self->{google_app_secret} = '@GOOGLE_APP_SECRET@';
61
$self->{google_redirect_uri} = '@GOOGLE_REDIRECT_URL@';
62
$self->{google_user_post_url} = '@GOOGLE_USER_POST_URL@';
63
$self->{google_scope} = '@GOOGLE_SCOPE@';
64
59
65
$self->_init_();
60
66
$self;
61
67
}
Небольшая справка по веткам
cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.
koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.
utf8 – актуальная ветка, заточенная под UTF-8.
Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.