view lib/IMPL/Web/Handler/SecureCookie.pm @ 409:f7eeafbd33da ref20150831

sync
author cin
date Sun, 13 Sep 2015 19:30:49 +0300
parents c6e90e02dd17
children
line wrap: on
line source

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