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 – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.