Revision 197
- Date:
- 2012/03/15 18:28:50
- Files:
-
- /utf8/plugins/session
- /utf8/plugins/session/comps
- /utf8/plugins/session/comps/contenido
- /utf8/plugins/session/comps/contenido/session
- /utf8/plugins/session/comps/contenido/session/dhandler (Diff) (Checkout)
- /utf8/plugins/session/comps/contenido/session/index.html (Diff) (Checkout)
- /utf8/plugins/session/config.proto (Diff) (Checkout)
- /utf8/plugins/session/lib
- /utf8/plugins/session/lib/session
- /utf8/plugins/session/lib/session/AUTH
- /utf8/plugins/session/lib/session/AUTH/FaceBook.pm (Diff) (Checkout)
- /utf8/plugins/session/lib/session/Apache.pm (Diff) (Checkout)
- /utf8/plugins/session/lib/session/Init.pm (Diff) (Checkout)
- /utf8/plugins/session/lib/session/Keeper.pm (Diff) (Checkout)
- /utf8/plugins/session/lib/session/State.pm.proto (Diff) (Checkout)
- /utf8/plugins/session/sql
- /utf8/plugins/session/sql/TOAST
- /utf8/plugins/session/sql/TOAST/session.sql (Diff) (Checkout)
Legend:
- Added
- Removed
- Modified
-
utf8/plugins/session/comps/contenido/session/dhandler
1 % if (@call) 2 % { 3 <& @call &> 4 % } 5 <%INIT> 6 7 use vars qw( $keeper $request $project ); 8 $r->content_type('text/html'); 9 10 my @path = split('/', $m->dhandler_arg()); 11 my @call = (); 12 13 if (length($path[0]) < 1) { $path[0] = 'index.html' }; 14 @call = (join('/',@path), %ARGS); 15 16 if (! $m->comp_exists($call[0])) 17 { 18 $m->clear_buffer(); 19 $m->abort(404); 20 } 21 22 </%INIT> -
utf8/plugins/session/comps/contenido/session/index.html
1 <& "/contenido/components/header.msn", style => 'index' &> 2 3 <div style="text-align:center; padding:180px 20px;"> 4 % if ( $keeper->{session}->state->storage eq 'POSTGRES' ) { 5 <form action="./index.html" method="POST" 6 onsubmit="return confirm('Вы действительно собираетесь грохнуть все пользовательские сессии?\nЭто приведет к принудительному выходу пользователей из системы!')"> 7 <input type="submit" class="input_btn" name="clear" value="Удалить все пользовательские сессии"> 8 9 </form> 10 % } 11 </div> 12 13 </body> 14 </html> 15 <%args> 16 17 $clear => undef 18 19 </%args> 20 <%init> 21 22 if ( $clear && $keeper->{session}->state->storage eq 'POSTGRES' ) { 23 warn "delete from sessions\n"; 24 my $req = $keeper->SQL->do('delete from sessions', {}) || $keeper->t_abort(); 25 $m->redirect ('./'); 26 } 27 28 </%init> -
utf8/plugins/session/config.proto
1 ############################################################################# 2 # 3 # Параметры данного шаблона необходимо ВРУЧНУЮ добавить в config.mk проекта 4 # и привести в соответствие с требованиями проекта 5 # 6 ############################################################################# 7 8 PLUGINS += session 9 PROJECT_REQUIRED += Apache-Session 10 PROJECT_REQUIRED += JSON-XS 11 PROJECT_REQUIRED += P-WebFetcher 12 13 ### Необязательный параметр, default = lsid 14 SESSION_COOKIE_NAME = lsid 15 REWRITE += SESSION_COOKIE_NAME 16 17 ifeq (${DEVELOPMENT}, YES) 18 19 SESSION_DOMAIN = 20 SESSION_STORAGE = POSTGRES 21 SESSION_LIFETIME = 24 22 SESSION_EXPIRES = +10d 23 24 else 25 26 SESSION_DOMAIN = 27 SESSION_STORAGE = POSTGRES 28 SESSION_LIFETIME = 24 29 SESSION_EXPIRES = +10d 30 31 endif 32 33 REWRITE += SESSION_STORAGE SESSION_DOMAIN SESSION_LIFETIME SESSION_EXPIRES 34 35 ######################################################################## 36 # 37 # SESSION_DOMAIN 38 # Домен, на котором отвечает проект. Можно не указывать 39 # SESSION_STORAGE 40 # Контейнер для хранения сессий. Варианты: POSTGRES и FILE 41 # SESSION_LIFETIME 42 # Время жизни сессий до автоочистки. Задается в часах, используется 43 # при включении в crontab проекта скрипта из services 44 # SESSION_EXPIRES 45 # Время жизни куки сессии. Задается в формате Apache 46 # 47 ######################################################################## 48 49 50 ### AUTH::FaceBook 51 ###################################### 52 FACEBOOK_APP_ID = 53 FACEBOOK_APP_KEY = 54 FACEBOOK_APP_SECRET = 55 FACEBOOK_AUTHOTIZE_URL = https://graph.facebook.com/oauth/authorize 56 FACEBOOK_ACCESS_TOKEN_URL = https://graph.facebook.com/oauth/access_token 57 FACEBOOK_USER_INFO_URL = https://graph.facebook.com/me 58 FACEBOOK_REDIRECT_URL = 59 FACEBOOK_USER_POST_URL = 60 61 REWRITE += FACEBOOK_AUTHOTIZE_URL FACEBOOK_ACCESS_TOKEN_URL FACEBOOK_USER_INFO_URL 62 63 CONNECTION_TIMEOUT = 3 64 65 PROJECT_REQUIRED += Crypt-SSLeay 66 -
utf8/plugins/session/lib/session/Apache.pm
1 package session::Apache; 2 3 use strict; 4 use warnings 'all'; 5 6 use session::State; 7 use Contenido::Globals; 8 9 10 sub child_init { 11 # встраиваем keeper плагина в keeper проекта 12 $keeper->{session} = session::Keeper->new($state->session); 13 } 14 15 sub request_init { 16 } 17 18 sub child_exit { 19 } 20 21 1; -
utf8/plugins/session/lib/session/AUTH/FaceBook.pm
1 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 $keeper->{session}->delete_key( 'facebook_redirect_url' ); 141 delete $local_session->{facebook_redirect_url}; 142 143 my @plugins = split (/[\ |\t]+/, $state->{plugins}); 144 if ( grep { $_ eq 'users' } @plugins ) { 145 my $user = $keeper->{users}->get_profile( login => 'facebook:'.$info->{id} ); 146 unless ( ref $user ) { 147 my $user_class = $state->{users}->profile_document_class; 148 $user = $user_class->new( $keeper ); 149 $user->login( 'facebook:'.$info->{id} ); 150 my $name = Encode::encode('utf-8', $info->{name}); 151 $user->name( $name ); 152 $user->status( 1 ); 153 $user->type( 0 ); 154 $user->login_method('facebook'); 155 $user->country( $info->{locale} ); 156 $user->email( undef ); 157 $user->store; 158 } 159 my %data = ( 160 id => $user->id, 161 name => $user->name, 162 login => $user->login, 163 status => $user->status, 164 type => $user->type, 165 ltime => time, 166 ); 167 $keeper->{session}->store_value ( %data ); 168 while ( my ( $key, $value ) = each %data ) { 169 $local_session->{$key} = $value; 170 } 171 } 172 return $local_session; 173 } 174 175 1; -
utf8/plugins/session/lib/session/Init.pm
1 package session::Init; 2 3 use strict; 4 use warnings 'all'; 5 6 use session::Apache; 7 use session::Keeper; 8 use session::AUTH::FaceBook; 9 10 # загрузка всех необходимых плагину классов 11 # session::SQL::SomeTable 12 # session::SomeClass 13 Contenido::Init::load_classes(qw( 14 )); 15 16 sub init { 17 0; 18 } 19 20 1; -
utf8/plugins/session/lib/session/Keeper.pm
1 package session::Keeper; 2 3 use strict; 4 use warnings 'all'; 5 use base qw(Contenido::Keeper); 6 7 use Apache::Cookie; 8 use Apache::Session::File; 9 use Apache::Session::Postgres; 10 use Contenido::Globals; 11 use Data::Dumper; 12 13 14 sub logon { 15 my $self = shift; 16 my %opts = @_; 17 18 return if !($opts{login} || $opts{email}) && !$opts{passwd}; 19 20 my $res; 21 my @plugins = split (/[\ |\t]+/, $state->{plugins}); 22 if ( grep { $_ eq 'users' } @plugins ) { 23 #### Авторизация через плагин users 24 ######################################### 25 $res = $keeper->{users}->login ( 26 $opts{login} ? (login => $opts{login}) : (), 27 $opts{email} ? (email => lc($opts{email})) : (), 28 passwd => $opts{passwd}, 29 ); 30 return unless $res; 31 } else { 32 #### Авторизация иным способом 33 34 35 36 } 37 if ( ref $res ) { 38 my %data = ( 39 id => $res->id, 40 name => $res->name, 41 email => $res->email, 42 login => $res->login, 43 status => $res->status, 44 ltime => time, 45 ); 46 $self->store_value ( %data ); 47 } 48 return $self->get_session(); 49 } 50 51 52 sub logoff { 53 my $self = shift; 54 my %opts = @_; 55 56 my $sid = _get_session_id (); 57 my $session = _get_session_object ( $sid ); 58 return unless ref $session; 59 60 my $session_id = $session->{_session_id}; 61 if (!$sid || $sid ne $session_id) { 62 warn "LOGOFF: New or deprecated session. Old sid = '$sid', new sid = '$session_id'" if $DEBUG; 63 _store_session_id ($session_id) 64 } else { 65 my @clear = qw( id email login name nick type status ltime ); 66 push @clear, @{ $opts{clear} } if exists $opts{clear} && ref $opts{clear} eq 'ARRAY' && @{ $opts{clear} }; 67 foreach my $key ( @clear ) { 68 delete $session->{$key}; 69 } 70 } 71 untie %$session; 72 return 1; 73 } 74 75 76 sub get_value { 77 78 my ($self, $name) = @_; 79 my $sid = _get_session_id (); 80 my $session = _get_session_object ( $sid ); 81 return unless ref $session; 82 83 my $session_id = $session->{_session_id}; 84 my $value = $session->{$name}; 85 if (!$sid || $sid ne $session_id) { 86 warn "GET_VALUE: New or deprecated session. Old sid = '$sid', new sid = '$session_id'" if $DEBUG; 87 _store_session_id ($session_id); 88 } 89 untie %$session; 90 return $value; 91 } 92 93 94 sub store_value { 95 96 my ($self, %opts) = @_; 97 my $sid = _get_session_id (); 98 my $session = _get_session_object ( $sid ); 99 return unless ref $session; 100 101 foreach my $key ( keys %opts ) { 102 $session->{$key} = $opts{$key}; 103 } 104 105 my $session_id = $session->{_session_id}; 106 if (!$sid || $sid ne $session_id) { 107 warn "STORE_VALUE: New or deprecated session. Old sid = '$sid', new sid = '$session_id'" if $DEBUG; 108 _store_session_id ($session_id); 109 } 110 untie %$session; 111 return 1; 112 } 113 114 115 sub delete_key { 116 117 my ($self, $key) = @_; 118 return unless $key; 119 120 my $sid = _get_session_id (); 121 my $session = _get_session_object ( $sid ); 122 return unless ref $session; 123 124 my $session_id = $session->{_session_id}; 125 if (!$sid || $sid ne $session_id) { 126 warn "DELETE_VALUE: New or deprecated session. Old sid = '$sid', new sid = '$session_id'" if $DEBUG; 127 _store_session_id ($session_id); 128 } else { 129 delete $session->{$key} if exists $session->{$key}; 130 } 131 untie %$session; 132 return 1; 133 } 134 135 136 sub get_session { 137 138 my $self = shift; 139 140 my $sid = _get_session_id () || ''; 141 my $session = _get_session_object ($sid); 142 return unless ref $session; 143 144 my $session_id = $session->{_session_id}; 145 my %ret = %$session; 146 if (!$sid || $sid ne $session_id) { 147 warn "\nGET_SESSION: New or deprecated session. Old sid = '$sid', new sid = '$session_id'\n" if $DEBUG; 148 _store_session_id ($session_id); 149 } 150 untie %$session; 151 152 return \%ret; 153 } 154 155 156 ## Внутренние функции 157 ###################################################################################### 158 sub _store_session_id { 159 160 my $sid = shift; 161 return unless $sid; 162 my $cookie = Apache::Cookie->new ($request->r(), 163 -domain => $state->{session}->domain, 164 -name => $state->{session}->cookie, 165 -expires=> $state->{session}->expires, 166 -value => $sid, 167 -path => '/', 168 ); 169 $cookie->bake(); 170 171 } 172 173 174 sub _get_session_id { 175 176 my %cookies = Apache::Cookie->fetch; 177 warn Dumper(\%cookies) if $DEBUG; 178 my $cookie = $cookies{$state->{session}->cookie}; 179 180 # Вытаскиваем SID из куки 181 my $sid = $cookie->value() || '' if $cookie; 182 warn "\nSession_id = $sid\n" if $DEBUG; 183 184 return $sid; 185 } 186 187 188 sub _get_session_object { 189 190 my $sid = shift; 191 192 my %session; 193 my $now = time; 194 if ( $state->{session}->storage eq 'POSTGRES' ) { 195 eval { 196 tie %session, 'Apache::Session::Postgres', $sid, { 197 Handle => $keeper->SQL, 198 }; 199 }; 200 } else { 201 eval { 202 tie %session, 'Apache::Session::File', $sid, { 203 Directory => $state->session->session_dir, 204 }; 205 }; 206 } 207 if ($@) { 208 warn "Session data is not accessible: $@"; 209 undef $sid; 210 } elsif ( $state->{session}->lifetime ) { 211 unless ( exists $session{_timestamp} ) { 212 $session{_timestamp} = $now; 213 } elsif ( ($now - $session{_timestamp}) > $state->{session}->lifetime ) { 214 undef $sid; 215 } elsif ( ($now - $session{_timestamp}) > $state->{session}->checkout ) { 216 $session{_timestamp} = $now; 217 } 218 } 219 unless ( $sid ) { 220 if ( $state->{session}->storage eq 'POSTGRES' ) { 221 eval { 222 tie %session, 'Apache::Session::Postgres', undef, { 223 Handle => $keeper->SQL, 224 }; 225 }; 226 } else { 227 eval { 228 tie %session, 'Apache::Session::File', undef, { 229 Directory => $state->session->session_dir, 230 }; 231 }; 232 } 233 $session{_timestamp} = $now; 234 } 235 236 return \%session; 237 } 238 239 240 sub _drop_session_object { 241 242 my (%session) = @_; 243 244 untie %session; 245 246 } 247 248 1; -
utf8/plugins/session/lib/session/State.pm.proto
1 package session::State; 2 3 use strict; 4 use warnings 'all'; 5 use vars qw($AUTOLOAD); 6 7 8 sub new { 9 my ($proto) = @_; 10 my $class = ref($proto) || $proto; 11 my $self = {}; 12 bless $self, $class; 13 14 # зашитая конфигурация плагина 15 $self->{db_type} = 'none'; 16 $self->{storage} = '@SESSION_STORAGE@' || 'FILE'; ## Значения: FILE POSTGRES MEMCACHED 17 $self->{session_dir} = '@SESSIONS@'; 18 $self->{session_directory} = '@SESSIONS@'; 19 20 $self->{domain} = '@SESSION_DOMAIN@'; 21 $self->{cookie} = 'lsid'; 22 $self->{expires} = '@SESSION_EXPIRES@' || ''; 23 24 $self->{lifetime} = '@SESSION_LIFETIME@'; 25 $self->{lifetime} *= 3600; 26 $self->{checkout} = $self->{lifetime} - int ($self->{lifetime} / 2); 27 28 $self->{db_keepalive} = 0; 29 $self->{db_host} = ''; 30 $self->{db_name} = ''; 31 $self->{db_user} = ''; 32 $self->{db_password} = ''; 33 $self->{db_port} = ''; 34 35 $self->{data_directory} = ''; 36 $self->{images_directory} = ''; 37 $self->{binary_directory} = ''; 38 $self->{preview} = ''; 39 $self->{debug} = ''; 40 $self->{store_method} = ''; 41 $self->{cascade} = ''; 42 $self->{memcached_enable} = ''; 43 44 $self->{facebook_app_id} = '@FACEBOOK_APP_ID@'; 45 $self->{facebook_app_key} = '@FACEBOOK_APP_KEY@'; 46 $self->{facebook_app_secret} = '@FACEBOOK_APP_SECRET@'; 47 $self->{facebook_authorize_url} = '@FACEBOOK_AUTHORIZE_URL@'; 48 $self->{facebook_access_token_url} = '@FACEBOOK_ACCESS_TOKEN_URL@'; 49 $self->{facebook_user_info_url} = '@FACEBOOK_USER_INFO_URL@'; 50 $self->{facebook_redirect_uri} = '@FACEBOOK_REDIRECT_URL@'; 51 $self->{facebook_user_post_url} = '@FACEBOOK_USER_POST_URL@'; 52 53 $self->_init_(); 54 $self; 55 } 56 57 sub info { 58 my $self = shift; 59 return unless ref $self; 60 61 for (sort keys %{$self->{attributes}}) { 62 my $la = length $_; 63 warn "\t$_".("\t" x (2-int($la/8))).": $self->{$_}\n"; 64 } 65 } 66 67 sub _init_ { 68 my $self = shift; 69 70 # зашитая конфигурация плагина 71 $self->{attributes}->{$_} = 'SCALAR' for qw( 72 db_type 73 session_dir 74 session_directory 75 domain 76 cookie 77 expires 78 storage 79 lifetime 80 checkout 81 db_keepalive 82 db_host 83 db_port 84 db_name 85 db_user 86 db_password 87 data_directory images_directory binary_directory preview debug store_method cascade memcached_enable 88 ); 89 } 90 91 sub AUTOLOAD { 92 my $self = shift; 93 my $attribute = $AUTOLOAD; 94 95 $attribute =~ s/.*:://; 96 return unless $attribute =~ /[^A-Z]/; # Отключаем методы типа DESTROY 97 98 if (!exists $self->{attributes}->{$attribute}) { 99 warn "Contenido Error (session::State): Вызов метода, для которого не существует обрабатываемого свойства: ->$attribute()\n"; 100 return; 101 } 102 103 $self->{$attribute} = shift @_ if $#_>=0; 104 $self->{$attribute}; 105 } 106 107 1; -
utf8/plugins/session/sql/TOAST/session.sql
1 CREATE TABLE sessions ( 2 id char(32) not null primary key, 3 dtime timestamp not null default now(), 4 a_session text 5 );