Line # Revision Author
1 191 ahitrov package session::AUTH::FaceBook;
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 facebook:
18 auto_create_user: 1
19 app_id: 122117614500563
20 app_key: 3da06301715b0efc5c873535c56c2c33
21 app_secret: 656bd1369486b902e9bf831a9a08132b
22 authorize_url: https://graph.facebook.com/oauth/authorize
23 access_token_url: https://graph.facebook.com/oauth/access_token
24 user_info_url: https://graph.facebook.com/me
25 user_post_url: ~
26 store:
27 class: "+Comments::Authentication::Store"
28 type: facebook
29
30 =cut
31
32 our $JSON = JSON::XS->new->utf8;
33
34 =for rem SCHEMA
35
36 $m->redirect ( $fb_connect->fb_authorize_url( redirect_uri => ... ) );
37
38
39 =cut
40
41
42 sub new {
43 my ($class, %config) = @_;
44 my $self = bless {}, $class;
45 for (qw(facebook_app_id facebook_app_key facebook_app_secret facebook_authorize_url facebook_access_token_url facebook_user_info_url)) {
46 $self->{$_} = $config{$_} || $state->{session}->{$_} || return undef;
47 }
48 $self->{timeout} = $state->{session}->{connection_timeout} || 3;
49 for (qw(facebook_user_post_url facebook_redirect_uri)) {
50 $self->{$_} = $config{$_} || $state->{session}->{$_};
51 }
52 return $self;
53 }
54
55 sub fb_authorize_url {
56 my $self = shift;
57 my (%args) = @_;
58 my $go = URI->new( $self->{facebook_authorize_url} );
59 warn Dumper($go);
60 $go->query_param( client_id => $self->{facebook_app_key} );
61 $go->query_param( scope => "publish_stream" );
62 $args{redirect_uri} ||= $self->{facebook_redirect_uri};
63 for ( keys %args ) {
64 $go->query_param( $_ => $args{$_} );
65 }
66 $keeper->{session}->store_value( facebook_redirect_url => $self->{facebook_redirect_uri} );
67 return $go;
68 }
69
70 sub authenticate {
71 my ( $self, %authinfo ) = @_;
72 warn "FB.authenticate" if $DEBUG;
73 # TODO: we need callback url
74 #warn "user_session=".dumper( $c->user_session )." ";
75 my $local_session = $session || $keeper->{session}->get_session;
76 my $redirect_uri = $local_session->{facebook_redirect_url};
77
78 my $access_token = $local_session->{facebook_access_token};
79 my $expires = $local_session->{facebook_expires};
80 if ($access_token and $expires > time) {
81 warn "Already have access_token" if $DEBUG;
82 } else {
83 undef $access_token;
84 }
85 my $code = $authinfo{'code'};
86 unless ( $code ) {
87 warn "Call to authenticate without code";
88 return undef;
89 }
90 my $ua = LWP::UserAgent->new;
91 $ua->timeout($self->{timeout});
92 unless ($access_token) {
93 my $req = URI->new( $self->{facebook_access_token_url});
94 $req->query_param( client_id => $self->{facebook_app_id} );
95 $req->query_param( redirect_uri => $redirect_uri );
96 $req->query_param( client_secret=> $self->{facebook_app_secret} );
97 $req->query_param( code => $code);
98 warn "Get $req";
99 my $res = $ua->get($req);
100 unless ($res->code == 200) {
101 warn "access_token request failed: ".$res->status_line;
102 return undef;
103 }
104 my %res = eval { URI->new("?".$res->content)->query_form };
105 warn Dumper(\%res);
106 unless ($access_token = $res{access_token}) {
107 warn "No access token in response: ".$res->content;
108 return undef;
109 }
110 $keeper->{session}->store_value( facebook_access_token => $access_token );
111 $local_session->{facebook_access_token} = $access_token;
112 if( my $expires = $res{expires} ) {
113 $local_session->{facebook_expires} = time + $expires;
114 $keeper->{session}->store_value( facebook_expires => $local_session->{facebook_expires} );
115 } else {
116 #$c->user_session->{'expires'} = time + 3600*24;
117 }
118 warn "FB: requested access token";
119 } else {
120 warn "FB: have access token";
121 }
122
123 my $req = URI->new( $self->{facebook_user_info_url} );
124 $req->query_param( access_token => $access_token );
125
126 warn "Fetching user $req";
127 my $res = $ua->get($req);
128 unless ($res->code == 200) {
129 warn "user request failed: ".$res->status_line;
130 return undef;
131 }
132 my $info;
133 unless ( $info = eval { JSON::XS->new->utf8->decode($res->content) } ) {
134 warn "user '".$res->content."' decode failed: $@";
135 return undef;
136 }
137 warn "Userhash = ".Dumper($info);
138 #warn "facebook: user=$info->{name} / $info->{id} / $info->{gender}";
139
140 my @plugins = split (/[\ |\t]+/, $state->{plugins});
141 if ( grep { $_ eq 'users' } @plugins ) {
142 my $user = $keeper->{users}->get_profile( login => 'facebook:'.$info->{id} );
143 unless ( ref $user ) {
144 my $user_class = $state->{users}->profile_document_class;
145 $user = $user_class->new( $keeper );
146 $user->login( 'facebook:'.$info->{id} );
147 my $name = Encode::encode('utf-8', $info->{name});
148 Encode::from_to( $name, 'utf-8', 'koi8-r' );
149 $user->name( $name );
150 $user->status( 1 );
151 $user->type( 0 );
152 $user->login_method('facebook');
153 $user->country( $info->{locale} );
154 $user->email( undef );
155
156 my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
157 if ( ref $prop_ava ) {
158 my $avatar = $user->_store_image( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', attr => 'avatar' );
159 local $Data::Dumper::Indent = 0;
160 $user->avatar( Data::Dumper::Dumper($avatar) );
161 }
162
163 $user->store;
164 } else {
165 my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
166 if ( ref $prop_ava ) {
167 my $avatar = $user->get_image( 'avatar' );
168 unless ( ref $avatar && exists $avatar->{filename} ) {
169 my $avatar = $user->_store_image( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', attr => 'avatar' );
170 local $Data::Dumper::Indent = 0;
171 $user->avatar( Data::Dumper::Dumper($avatar) );
172 $user->store;
173 }
174 }
175 }
176 my %data = (
177 id => $user->id,
178 name => $user->name,
179 login => $user->login,
180 status => $user->status,
181 type => $user->type,
182 ltime => time,
183 avatar => 'https://graph.facebook.com/'.$info->{username}.'/picture',
184 );
185 $keeper->{session}->store_value ( %data );
186 while ( my ( $key, $value ) = each %data ) {
187 $local_session->{$key} = $value;
188 }
189 }
190 return $local_session;
191 }
192
193 1;

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

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

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

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

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