Line # Revision Author
1 191 ahitrov package session::AUTH::VKontakte;
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 Contenido::Globals;
12
13 use vars qw($VERSION);
14 $VERSION = '4.1';
15
16 =for rem
17 vkontakte:
18 auto_create_user: 1
19 app_id: 122117614500563
20 app_secret: 656bd1369486b902e9bf831a9a08132b
21 authorize_url: http://api.vkontakte.ru/oauth/authorize
22 access_token_url: https://api.vkontakte.ru/oauth/access_token
23 user_info_url: https://api.vkontakte.ru/method/getProfiles
24 user_post_url: ~
25 =cut
26
27 our $JSON = JSON::XS->new->utf8;
28
29 =for rem SCHEMA
30
31 $m->redirect ( $fb_connect->fb_authorize_url( redirect_uri => ... ) );
32
33
34 =cut
35
36 sub new {
37 my ($class, %config) = @_;
38 my $self = bless {}, $class;
39 for (qw( vk_app_id vk_app_secret vk_authorize_url vk_access_token_url vk_user_info_url)) {
40 $self->{$_} = $config{$_} || $state->{session}->{$_} || return undef;
41 }
42 $self->{timeout} = $state->{session}->{connection_timeout} || 3;
43 for (qw(vk_user_post_url vk_redirect_uri)) {
44 $self->{$_} = $config{$_} || $state->{session}->{$_};
45 }
46 return $self;
47 }
48
49 sub authorize_url {
50 my $self = shift;
51 my (%args) = @_;
52 my $go = URI->new( $self->{vk_authorize_url} );
53 $go->query_param( client_id => $self->{vk_app_id} );
54 $go->query_param( scope => '' );
55 $go->query_param( response_type => 'code' );
56 $args{redirect_uri} ||= $self->{vk_redirect_uri};
57 for ( keys %args ) {
58 $go->query_param( $_ => $args{$_} );
59 }
60 $keeper->{session}->store_value( vk_redirect_url => $self->{vk_redirect_uri} );
61 return $go;
62 }
63
64 sub authenticate {
65 my ( $self, %authinfo ) = @_;
66 warn "VK.authenticate" if $DEBUG;
67 # TODO: we need callback url
68 #warn "user_session=".dumper( $c->user_session )." ";
69
70 my $local_session = $session || $keeper->{session}->get_session;
71 my $redirect_uri = $local_session->{vk_redirect_url};
72
73 my $access_token = $local_session->{vk_access_token};
74 my $vk_user_id = $local_session->{vk_user_id};
75 my $expires = $local_session->{vk_expires};
76 if ($access_token and $expires > time) {
77 warn "Already have access_token" if $DEBUG;
78 } else {
79 undef $access_token;
80 }
81 my $code = $authinfo{'code'};
82 unless ( $code ) {
83 warn "Call to authenticate without code\n";
84 return undef;
85 }
86 my $ua = LWP::UserAgent->new;
87 $ua->timeout($self->{timeout});
88
89 unless ($access_token) {
90 my $req = URI->new( $self->{vk_access_token_url});
91 $req->query_param( client_id => $self->{vk_app_id} );
92 $req->query_param( client_secret => $self->{vk_app_secret} );
93 $req->query_param( code => $code );
94 my $res = $ua->get($req);
95 unless ($res->code == 200) {
96 warn "VK: Access_token request failed: ".$res->status_line."\n";
97 return undef;
98 }
99 my $info = $JSON->decode($res->content);
100 unless ( ref $info eq 'HASH' && ($access_token = $info->{access_token}) ) {
101 warn "No access token in response: ".$res->content."\n";
102 return undef;
103 }
104 $keeper->{session}->store_value( vk_access_token => $access_token );
105 $local_session->{vk_access_token} = $access_token;
106 $keeper->{session}->store_value( vk_user_id => $info->{user_id} );
107 $local_session->{vk_user_id} = $info->{user_id};
108 if ( my $expires = $info->{expires_in} ) {
109 $local_session->{vk_expires} = time + $expires;
110 $keeper->{session}->store_value( vk_expires => $local_session->{vk_expires} );
111 } else {
112 #$c->user_session->{'expires'} = time + 3600*24;
113 }
114 warn "VK: requested access token";
115 } else {
116 warn "VK: have access token";
117 }
118
119 my $req = URI->new( $self->{vk_user_info_url} );
120 $req->query_param( uid => $local_session->{vk_user_id} );
121 $req->query_param( fields => 'uid,first_name,last_name,nickname,domain,sex,bdate,city,country,timezone,photo,photo_medium,photo_big' );
122 $req->query_param( access_token => $access_token );
123
124 warn "VK: Fetching user $req\n" if $DEBUG;
125 my $res = $ua->get($req);
126 unless ($res->code == 200) {
127 warn "VK: user request failed: ".$res->status_line."\n";
128 return undef;
129 }
130
131 my $info;
132 unless ( $info = eval { $JSON->decode($res->content) } ) {
133 warn "user '".$res->content."' decode failed: $@\n";
134 return undef;
135 }
136 warn Dumper($info) if $DEBUG;
137 return undef unless exists $info->{response} && ref $info->{response} eq 'ARRAY' && @{$info->{response}};
138 my $user_info = $info->{response}[0];
139 foreach my $key ( qw(nickname last_name first_name) ) {
140 $user_info->{$key} = Encode::encode('utf-8', $user_info->{$key});
141 Encode::from_to( $user_info->{$key}, 'utf-8', 'koi8-r' );
142 }
143
144 my @plugins = split (/[\ |\t]+/, $state->{plugins});
145 my $name = $user_info->{first_name}.' '.$user_info->{last_name};
146 if ( grep { $_ eq 'users' } @plugins ) {
147 my $user = $keeper->{users}->get_profile( login => 'vkontakte:'.$user_info->{uid} );
148 unless ( ref $user ) {
149 my $user_class = $state->{users}->profile_document_class;
150 $user = $user_class->new( $keeper );
151 $user->login( 'vkontakte:'.$user_info->{uid} );
152 $user->name( $user_info->{last_name}.', '.$user_info->{first_name} );
153 $user->nickname( $user_info->{nickname} );
154 $user->status( 1 );
155 $user->type( 0 );
156 $user->login_method('vkontakte');
157 $user->country( $user_info->{country} );
158 $user->email( undef );
159
160 my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
161 if ( ref $prop_ava ) {
162 my $avatar = $user->_store_image( $user_info->{photo_big}, attr => 'avatar' );
163 local $Data::Dumper::Indent = 0;
164 $user->avatar( Data::Dumper::Dumper($avatar) );
165 }
166
167 $user->store;
168 } else {
169 my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
170 if ( ref $prop_ava ) {
171 my $avatar = $user->get_image( 'avatar' );
172 unless ( ref $avatar && exists $avatar->{filename} ) {
173 my $avatar = $user->_store_image( $user_info->{photo_big}, attr => 'avatar' );
174 local $Data::Dumper::Indent = 0;
175 $user->avatar( Data::Dumper::Dumper($avatar) );
176 $user->store;
177 }
178 }
179 }
180 my %data = (
181 id => $user->id,
182 name => $name,
183 login => $user->login,
184 status => $user->status,
185 type => $user->type,
186 auth_by => 'vkontakte',
187 ltime => time,
188 );
189 if ( $user_info->{photo} ) {
190 $data{avatar} = $user_info->{photo};
191 }
192 $keeper->{session}->store_value ( %data );
193 while ( my ( $key, $value ) = each %data ) {
194 $local_session->{$key} = $value;
195 }
196
197 } else {
198 my %data = (
199 id => $user_info->{uid},
200 name => $name,
201 nick => $user_info->{nickname} || $name,
202 login => 'vkontakte:'.$user_info->{uid},
203 status => 1,
204 type => 0,
205 auth_by => 'vkontakte',
206 ltime => time,
207 );
208 if ( $user_info->{photo} ) {
209 $data{avatar} = $user_info->{photo};
210 }
211 $keeper->{session}->store_value ( %data );
212 while ( my ( $key, $value ) = each %data ) {
213 $local_session->{$key} = $value;
214 }
215 }
216 return $local_session;
217 }
218
219 1;

Небольшая справка по веткам

cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.

koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.

utf8 – актуальная ветка, заточенная под UTF-8.

Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.