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