Mercurial > pub > Impl
diff lib/IMPL/Web/Handler/SecureCookie.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/IMPL/Web/Handler/SecureCookie.pm Fri Sep 04 19:40:23 2015 +0300 @@ -0,0 +1,201 @@ +package IMPL::Web::Handler::SecureCookie; +use strict; + + +use Digest::MD5 qw(md5_hex); +use IMPL::Const qw(:prop); +use IMPL::Security::Auth qw(:Const GenSSID); +use IMPL::declare { + require => { + SecurityContext => 'IMPL::Security::Context', + User => 'IMPL::Security::Principal', + AuthSimple => 'IMPL::Security::Auth::Simple', + Exception => 'IMPL::Exception', + OperationException => '-IMPL::InvalidOperationException', + HttpResponse => '-IMPL::Web::HttpResponse' + }, + base => { + 'IMPL::Object' => undef, + 'IMPL::Object::Autofill' => '@_', + 'IMPL::Object::Serializable' => undef + }, + props => [ + salt => PROP_RO, + _security => PROP_RW, + _cookies => PROP_RW + ] +}; + +sub CTOR { + my ($this) = @_; + + $this->salt('DeadBeef') unless $this->salt; +} + +sub ValidateCookie { + my ($this,$sid,$cookie,$sign) = @_; + + return 1 if $sid and $cookie and $sign and $sign eq md5_hex($this->salt,$sid,$cookie,$this->salt); + + return 0; +} + +sub AuthCookie { + my ($this,$sid,$cookie,$sign, $context) = @_; + + if (eval { $context->auth->isa(AuthSimple) }) { + my ($result,$challenge) = $context->auth->DoAuth($cookie); + return $result; + } + + return AUTH_FAIL; +} + +sub Invoke { + my ($this,$action,$nextHandler) = @_; + + return unless $nextHandler; + + my $context; + $this->_security($action->security); + + + my $sid = $action->cookie('sid',qr/(\w+)/); + my $cookie = $action->cookie('sdata',qr/(\w+)/); + my $sign = $action->cookie('sign',qw/(\w+)/); + + if ( $this->ValidateCookie($sid,$cookie,$sign) ) { + # TODO: add a DeferredProxy to deffer a request to a data source + if ( $context = $this->_security->sessions->GetById($sid) ) { + if ( eval { $context->auth->isa(AuthSimple) } ) { + my ($result,$challenge) = $context->auth->DoAuth($cookie); + + $context->authority($this); + + if ($result == AUTH_FAIL) { + $context = undef; + } + } else { + undef $context; + } + } + + } + + $context ||= SecurityContext->new(principal => User->nobody, authority => $this); + + my $httpResponse = eval { $context->Impersonate($nextHandler,$action); }; + my $e = $@; + + die $e if $e; + + die OperationException->new("A HttpResponse instance is expected") + unless ref $httpResponse && eval { $httpResponse->isa(HttpResponse) }; + + return $this->_WriteResponse($httpResponse); +} + +sub InitSession { + my ($this,$user,$roles,$auth,$challenge) = @_; + + my ($status,$answer); + + if ($auth) { + ($status,$answer) = $auth->DoAuth($challenge); + } else { + $status = AUTH_SUCCESS; + } + + die OperationException->new("This provider doesn't support multiround auth") + if ($status == AUTH_INCOMPLETE || $answer); + + if ($status == AUTH_SUCCESS) { + my $sid = GenSSID(); + my $cookie = GenSSID(); + + $this->_cookies({ + sid => $sid, + sdata => $cookie + }); + + my $context = $this->_security->sessions->Create({ + sessionId => $sid, + principal => $user, + auth => AuthSimple->Create(password => $cookie), + authority => $this, + rolesAssigned => $roles + }); + + $context->Apply(); + + } + + return $status; +} + +sub CloseSession { + my ($this) = @_; + if(my $session = SecurityContext->current) { + $this->_cookies({ + sid => undef, + sdata => undef + }) + } +} + +sub _WriteResponse { + my ($this,$response) = @_; + + if (my $data = $this->_cookies) { + + my $sign = $data->{sid} && md5_hex( + $this->salt, + $data->{sid}, + $data->{sdata}, + $this->salt + ); + + $response->cookies->{sid} = $data->{sid}; + $response->cookies->{sdata} = $data->{sdata}; + $response->cookies->{sign} = $sign; + } + + return $response; +} + +1; + +__END__ + +=pod + +=head1 NAME + +C<IMPL::Web::Handler::SecureCookie> + +=head1 DESCRIPTION + +Возобновляет сессию пользователя на основе информации переданной через Cookie. + +Использует механизм подписи информации для проверки верности входных данных перед +началом каких-либо действий. + +Данный обработчик возвращает результат выполнения следдующего обработчика. + + + +=head1 MEMBERS + +=head2 C<[get,set] salt> + +Скаляр, использующийся для подписи данных. + + +=head2 C<InitSession($user,$roles,$auth,$challenge)> + +Инициирует сессию, поскольку данный модуль отвечает за взаимодействие с клиентом +при проверки аутентификации, ему передаются данные аутентификации для +продолжения обмена данными с клиентом. Если создается новая сессия, по +инициативе веб-приложения, то C<$auth> должно быть пусто. + +=cut