244 lines
7.6 KiB
Perl
244 lines
7.6 KiB
Perl
# Plugin to enable "stay connected on this device" feature
|
|
|
|
package Lemonldap::NG::Portal::Plugins::StayConnected;
|
|
|
|
use strict;
|
|
use Mouse;
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
|
PE_OK
|
|
PE_SENDRESPONSE
|
|
);
|
|
|
|
our $VERSION = '2.0.10';
|
|
|
|
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|
|
|
# INTERFACE
|
|
|
|
use constant endAuth => 'newDevice';
|
|
use constant beforeAuth => 'check';
|
|
use constant beforeLogout => 'logout';
|
|
|
|
# INITIALIZATION
|
|
|
|
has ott => (
|
|
is => 'rw',
|
|
lazy => 1,
|
|
default => sub {
|
|
my $ott =
|
|
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
|
$ott->timeout( $_[0]->conf->{formTimeout} );
|
|
return $ott;
|
|
}
|
|
);
|
|
has cookieName => (
|
|
is => 'rw',
|
|
lazy => 1,
|
|
default => sub {
|
|
$_[0]->conf->{stayConnectedCookieName} || 'llngconnection';
|
|
}
|
|
);
|
|
|
|
# Default timeout: 1 month
|
|
has timeout => (
|
|
is => 'rw',
|
|
lazy => 1,
|
|
default => sub {
|
|
$_[0]->conf->{stayConnectedTimeout} || 2592000;
|
|
}
|
|
);
|
|
|
|
sub init {
|
|
my ($self) = @_;
|
|
$self->addAuthRoute( registerbrowser => 'storeBrowser', ['POST'] );
|
|
|
|
return 1;
|
|
}
|
|
|
|
# RUNNING METHODS
|
|
|
|
# Registration: detect if user wants to stay connected.
|
|
# Then ask for browser fingerprint
|
|
sub newDevice {
|
|
my ( $self, $req ) = @_;
|
|
|
|
my $checkLogins = $req->param('checkLogins');
|
|
$self->logger->debug("StayConnected: checkLogins set") if $checkLogins;
|
|
|
|
if ( $req->param('stayconnected') ) {
|
|
my $token = $self->ott->createToken( {
|
|
name => $req->sessionInfo->{ $self->conf->{whatToTrace} },
|
|
(
|
|
$checkLogins
|
|
? ( history => $req->sessionInfo->{_loginHistory} )
|
|
: ()
|
|
)
|
|
}
|
|
);
|
|
$req->response(
|
|
$self->p->sendHtml(
|
|
$req,
|
|
'../common/registerBrowser',
|
|
params => {
|
|
URL => $req->urldc,
|
|
TOKEN => $token,
|
|
ACTION => '/registerbrowser',
|
|
CHECKLOGINS => $checkLogins
|
|
}
|
|
)
|
|
);
|
|
return PE_SENDRESPONSE;
|
|
}
|
|
return PE_OK;
|
|
}
|
|
|
|
# Store data in a long-time session
|
|
sub storeBrowser {
|
|
my ( $self, $req ) = @_;
|
|
$req->urldc( $req->param('url') );
|
|
$req->mustRedirect(1);
|
|
if ( my $token = $req->param('token') ) {
|
|
if ( my $tmp = $self->ott->getToken($token) ) {
|
|
my $uid = $req->userData->{ $self->conf->{whatToTrace} };
|
|
if ( $tmp->{name} eq $uid ) {
|
|
if ( my $fg = $req->param('fg') ) {
|
|
my $ps = Lemonldap::NG::Common::Session->new(
|
|
storageModule => $self->conf->{globalStorage},
|
|
storageModuleOptions =>
|
|
$self->conf->{globalStorageOptions},
|
|
kind => "SSO",
|
|
info => {
|
|
_utime => time + $self->timeout(),
|
|
_session_uid => $uid,
|
|
_connectedSince => time,
|
|
dataKeep => $req->data->{dataToKeep},
|
|
fingerprint => $fg,
|
|
},
|
|
);
|
|
|
|
# Cookie available 30 days
|
|
$req->addCookie(
|
|
$self->p->cookie(
|
|
name => $self->cookieName(),
|
|
value => $ps->id,
|
|
max_age => $self->timeout(),
|
|
secure => $self->conf->{securedCookie},
|
|
)
|
|
);
|
|
$req->sessionInfo->{_loginHistory} = $tmp->{history}
|
|
if exists $tmp->{history};
|
|
}
|
|
else {
|
|
$self->logger->warn("Browser hasn't return fingerprint");
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->error(
|
|
"StayConnected: mismatch UID: $tmp->{name} / $uid");
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->error(
|
|
"StayConnected called with an expired token");
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->error('StayConnected called without token');
|
|
}
|
|
|
|
# Return persistent connection cookie
|
|
return $self->p->do( $req, [ @{ $self->p->endAuth }, sub { PE_OK } ] );
|
|
}
|
|
|
|
# Check for:
|
|
# - persistent connection cookie
|
|
# - valid session
|
|
# - uniq id is kept
|
|
# Then delete authentication methods from "steps" array.
|
|
sub check {
|
|
my ( $self, $req ) = @_;
|
|
if ( my $cid = $req->cookies->{ $self->cookieName() } ) {
|
|
my $ps = Lemonldap::NG::Common::Session->new(
|
|
storageModule => $self->conf->{globalStorage},
|
|
storageModuleOptions => $self->conf->{globalStorageOptions},
|
|
kind => "SSO",
|
|
id => $cid,
|
|
);
|
|
if ( $ps
|
|
and my $uid = $ps->data->{_session_uid}
|
|
and time() < $ps->data->{_utime} )
|
|
{
|
|
$self->logger->debug('Persistent connection found');
|
|
if ( my $fg = $req->param('fg')
|
|
and my $token = $req->param('token') )
|
|
{
|
|
if ( my $prm = $self->ott->getToken($token) ) {
|
|
$req->data->{dataKeep} = $ps->data->{dataKeep};
|
|
$self->logger->debug('Persistent connection found');
|
|
if ( $fg eq $ps->data->{fingerprint} ) {
|
|
$req->user($uid);
|
|
my @steps =
|
|
grep {
|
|
!ref $_
|
|
and $_ !~ /^(?:extractFormInfo|authenticate)$/
|
|
} @{ $req->steps };
|
|
$req->steps( \@steps );
|
|
$self->userLogger->notice(
|
|
"$uid connected by StayConnected cookie");
|
|
return PE_OK;
|
|
}
|
|
else {
|
|
$self->userLogger->warn("Fingerprint changed for $uid");
|
|
$ps->remove;
|
|
$self->logout($req);
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->notice(
|
|
"StayConnected: expired token for $uid");
|
|
}
|
|
}
|
|
else {
|
|
my $token = $self->ott->createToken( $req->parameters );
|
|
$req->response(
|
|
$self->p->sendHtml(
|
|
$req,
|
|
'../common/registerBrowser',
|
|
params => {
|
|
TOKEN => $token,
|
|
ACTION => '#',
|
|
}
|
|
)
|
|
);
|
|
return PE_SENDRESPONSE;
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->notice('Persistent connection expired');
|
|
unless ( $ps->{error} ) {
|
|
$self->logger->debug(
|
|
'Persistent connection session id = ' . $ps->{id} );
|
|
$self->logger->debug( 'Persistent connection session _utime = '
|
|
. $ps->data->{_utime} );
|
|
$ps->remove;
|
|
}
|
|
}
|
|
}
|
|
return PE_OK;
|
|
}
|
|
|
|
sub logout {
|
|
my ( $self, $req ) = @_;
|
|
$req->addCookie(
|
|
$self->p->cookie(
|
|
name => $self->cookieName(),
|
|
value => 0,
|
|
expires => 'Wed, 21 Oct 2015 00:00:00 GMT',
|
|
secure => $self->conf->{securedCookie},
|
|
)
|
|
);
|
|
return PE_OK;
|
|
}
|
|
|
|
1;
|