245 lines
7.4 KiB
Perl
245 lines
7.4 KiB
Perl
package Lemonldap::NG::Portal::Plugins::GlobalLogout;
|
|
|
|
use strict;
|
|
use Mouse;
|
|
use JSON qw(from_json to_json);
|
|
use Time::Local;
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
|
PE_OK
|
|
PE_ERROR
|
|
PE_NOTOKEN
|
|
PE_TOKENEXPIRED
|
|
PE_SENDRESPONSE
|
|
);
|
|
|
|
our $VERSION = '2.0.10';
|
|
|
|
extends qw(
|
|
Lemonldap::NG::Portal::Main::Plugin
|
|
Lemonldap::NG::Portal::Lib::OtherSessions
|
|
);
|
|
|
|
# INTERFACE
|
|
use constant beforeLogout => 'run';
|
|
|
|
# INITIALIZATION
|
|
has rule => ( is => 'rw', default => sub { 0 } );
|
|
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;
|
|
}
|
|
);
|
|
|
|
sub init {
|
|
my ($self) = @_;
|
|
$self->addAuthRoute( globallogout => 'globalLogout', [ 'POST', 'GET' ] );
|
|
|
|
# Parse activation rule
|
|
$self->rule(
|
|
$self->p->buildRule( $self->conf->{globalLogoutRule}, 'globalLogout' )
|
|
);
|
|
return 0 unless $self->rule;
|
|
|
|
return 1;
|
|
}
|
|
|
|
# RUNNING METHODS
|
|
# Look for user active SSO sessions and suggest to close them
|
|
sub run {
|
|
my ( $self, $req ) = @_;
|
|
my $user = $req->{userData}->{ $self->conf->{whatToTrace} };
|
|
|
|
# Check activation rule
|
|
unless ( $self->rule->( $req, $req->userData ) ) {
|
|
$self->userLogger->info("GlobaLogout not allowed for $user");
|
|
return PE_OK;
|
|
}
|
|
|
|
# Looking for active sessions
|
|
my $sessions = $self->activeSessions($req);
|
|
my $nbr = @{$sessions};
|
|
$self->logger->debug("GlobalLogout: $nbr session(s) found") if $nbr;
|
|
return PE_OK unless ( $nbr > 1 );
|
|
|
|
# Force GlobalLogout if timer is disabled
|
|
unless ( $self->conf->{globalLogoutTimer} ) {
|
|
$self->logger->debug("GlobalLogout: timer disabled");
|
|
$self->userLogger->info("GlobalLogout: force global logout for $user");
|
|
$nbr = $self->removeOtherActiveSessions( $req, $sessions );
|
|
$self->userLogger->info("$nbr remaining session(s) removed");
|
|
|
|
return PE_OK;
|
|
}
|
|
|
|
# Prepare token
|
|
my $token = $self->ott->createToken( {
|
|
user => $user,
|
|
sessions => to_json($sessions)
|
|
}
|
|
);
|
|
|
|
# Prepare form
|
|
$self->logger->debug("Prepare global logout confirmation");
|
|
my $tmp = $self->p->sendHtml(
|
|
$req,
|
|
'globallogout',
|
|
params => {
|
|
PORTAL => $self->conf->{portal},
|
|
MAIN_LOGO => $self->conf->{portalMainLogo},
|
|
SKIN => $self->p->getSkin($req),
|
|
LANGS => $self->conf->{showLanguages},
|
|
SESSIONS => $sessions,
|
|
TOKEN => $token,
|
|
LOGIN => $user,
|
|
CUSTOMPRM => $self->conf->{globalLogoutCustomParam}
|
|
}
|
|
);
|
|
$req->response($tmp);
|
|
|
|
return PE_SENDRESPONSE;
|
|
}
|
|
|
|
sub globalLogout {
|
|
my ( $self, $req ) = @_;
|
|
my $res = PE_OK;
|
|
my $count = 0;
|
|
|
|
if ( $req->param('all') ) {
|
|
if ( my $token = $req->param('token') ) {
|
|
if ( $token = $self->ott->getToken($token) ) {
|
|
|
|
# Read active sessions from token
|
|
my $sessions = eval { from_json( $token->{sessions} ) };
|
|
if ($@) {
|
|
$self->logger->error(
|
|
"GlobalLogout: bad encoding in OTT ($@)");
|
|
$res = PE_ERROR;
|
|
}
|
|
my $as;
|
|
my $user = $token->{user};
|
|
my $req_user =
|
|
$req->{userData}->{ $self->{conf}->{whatToTrace} };
|
|
if ( $req_user eq $user ) {
|
|
foreach (@$sessions) {
|
|
unless ( $as = $self->p->getApacheSession( $_->{id} ) )
|
|
{
|
|
$self->userLogger->info(
|
|
"GlobalLogout: session $_->{id} expired");
|
|
next;
|
|
}
|
|
unless ( $req->{userData}->{_session_id} eq $_->{id} ) {
|
|
$self->userLogger->info(
|
|
"Remove \"$user\" session: $_->{id}");
|
|
$as->remove;
|
|
$count++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->warn(
|
|
"GlobalLogout called with an invalid token: $req_user is NOT $user"
|
|
);
|
|
$res = PE_TOKENEXPIRED;
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->error(
|
|
"GlobalLogout called with an expired token");
|
|
$res = PE_TOKENEXPIRED;
|
|
}
|
|
}
|
|
else {
|
|
$self->userLogger->error('GlobalLogout called without token');
|
|
$res = PE_NOTOKEN;
|
|
}
|
|
}
|
|
|
|
return $self->p->do( $req, [ sub { $res } ] ) if $res;
|
|
$self->userLogger->info("$count remaining session(s) removed");
|
|
return $self->p->do( $req, [ 'authLogout', 'deleteSession' ] );
|
|
}
|
|
|
|
sub activeSessions {
|
|
my ( $self, $req ) = @_;
|
|
my $activeSessions = [];
|
|
my $sessions = {};
|
|
my $user = $req->{userData}->{ $self->conf->{whatToTrace} };
|
|
my $customParam = $self->conf->{globalLogoutCustomParam} || '';
|
|
|
|
# Try to retrieve sessions from sessions DB
|
|
$self->logger->debug('Try to retrieve sessions from DB');
|
|
my $moduleOptions = $self->conf->{globalStorageOptions} || {};
|
|
$moduleOptions->{backend} = $self->conf->{globalStorage};
|
|
$self->logger->debug("Looking for \"$user\" sessions...");
|
|
$sessions =
|
|
$self->module->searchOn( $moduleOptions, $self->conf->{whatToTrace},
|
|
$user );
|
|
|
|
$self->logger->debug('Remove non-SSO session(s)...');
|
|
my $other = 0;
|
|
foreach ( keys %$sessions ) {
|
|
unless ( $sessions->{$_}->{_session_kind} eq 'SSO' ) {
|
|
delete $sessions->{$_};
|
|
$other++;
|
|
}
|
|
}
|
|
$self->logger->info("$other non-SSO session(s) removed")
|
|
if $other;
|
|
|
|
$self->logger->debug('Build an array ref with sessions info...');
|
|
@$activeSessions =
|
|
map {
|
|
my $epoch;
|
|
my $regex = '^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$';
|
|
$_->{startTime} =~ /$regex/;
|
|
$epoch = timelocal( $6, $5, $4, $3, $2 - 1, $1 );
|
|
$_->{startTime} = $epoch;
|
|
if ( $_->{updateTime} ) {
|
|
$_->{updateTime} =~ /$regex/;
|
|
$epoch = timelocal( $6, $5, $4, $3, $2 - 1, $1 );
|
|
$_->{updateTime} = $epoch;
|
|
}
|
|
$_;
|
|
}
|
|
sort { $b->{startTime} cmp $a->{startTime} } map { {
|
|
id => $_,
|
|
customParam => $sessions->{$_}->{$customParam},
|
|
ipAddr => $sessions->{$_}->{ipAddr},
|
|
authLevel => $sessions->{$_}->{authenticationLevel},
|
|
startTime => $sessions->{$_}->{_startTime},
|
|
updateTime => $sessions->{$_}->{_updateTime}
|
|
};
|
|
} keys %$sessions;
|
|
|
|
return $activeSessions;
|
|
}
|
|
|
|
sub removeOtherActiveSessions {
|
|
my ( $self, $req, $sessions ) = @_;
|
|
my $count = 0;
|
|
my $as;
|
|
|
|
foreach (@$sessions) {
|
|
unless ( $as = $self->p->getApacheSession( $_->{id} ) ) {
|
|
$self->userLogger->info("GlobalLogout: session $_->{id} expired");
|
|
next;
|
|
}
|
|
unless ( $req->{userData}->{_session_id} eq $_->{id} ) {
|
|
$self->userLogger->info(
|
|
"Remove \"$req->{userData}->{ $self->conf->{whatToTrace} }\" session: $_->{id}"
|
|
);
|
|
$as->remove;
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
1;
|