2017-02-17 08:40:18 +01:00
|
|
|
package Lemonldap::NG::Portal::Plugins::SingleSession;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Mouse;
|
2020-04-25 00:43:56 +02:00
|
|
|
use MIME::Base64;
|
|
|
|
use JSON qw(from_json to_json);
|
2020-04-25 01:02:25 +02:00
|
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
|
|
|
PE_OK
|
|
|
|
PE_ERROR
|
|
|
|
PE_NOTOKEN
|
2020-10-09 22:26:00 +02:00
|
|
|
PE_TOKENEXPIRED
|
2020-04-25 01:02:25 +02:00
|
|
|
);
|
2017-02-17 08:40:18 +01:00
|
|
|
|
2020-10-09 22:26:00 +02:00
|
|
|
our $VERSION = '2.0.10';
|
2017-02-17 08:40:18 +01:00
|
|
|
|
2020-10-09 22:26:00 +02:00
|
|
|
extends qw(
|
|
|
|
Lemonldap::NG::Portal::Main::Plugin
|
|
|
|
Lemonldap::NG::Portal::Lib::OtherSessions
|
|
|
|
);
|
2017-02-17 08:40:18 +01:00
|
|
|
|
2018-09-05 09:19:01 +02:00
|
|
|
use constant endAuth => 'run';
|
2017-02-17 08:40:18 +01:00
|
|
|
|
2020-04-21 15:49:31 +02:00
|
|
|
has singleIPRule => ( is => 'rw' );
|
|
|
|
has singleSessionRule => ( is => 'rw' );
|
|
|
|
has singleUserByIPRule => ( is => 'rw' );
|
2020-04-25 00:43:56 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
);
|
2020-04-21 15:49:31 +02:00
|
|
|
|
|
|
|
sub init {
|
|
|
|
my ($self) = @_;
|
2020-04-25 00:43:56 +02:00
|
|
|
$self->addAuthRoute( removeOther => 'removeOther', ['GET'] );
|
2020-04-21 15:49:31 +02:00
|
|
|
|
|
|
|
# Build triggering rules from configuration
|
|
|
|
$self->singleIPRule(
|
|
|
|
$self->p->buildRule( $self->conf->{singleIP}, 'singleIP' ) );
|
|
|
|
return 0 unless $self->singleIPRule;
|
|
|
|
|
|
|
|
$self->singleSessionRule(
|
|
|
|
$self->p->buildRule( $self->conf->{singleSession}, 'singleSession' ) );
|
|
|
|
return 0 unless $self->singleSessionRule;
|
|
|
|
|
|
|
|
$self->singleUserByIPRule(
|
|
|
|
$self->p->buildRule( $self->conf->{singleUserByIP}, 'singleUserByIP' )
|
|
|
|
);
|
|
|
|
return 0 unless $self->singleUserByIPRule;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2017-02-17 08:40:18 +01:00
|
|
|
|
|
|
|
sub run {
|
|
|
|
my ( $self, $req ) = @_;
|
2020-04-25 11:41:08 +02:00
|
|
|
my ( $linkedSessionId, $token, $html ) = ( '', '', '' );
|
2019-05-28 17:32:06 +02:00
|
|
|
my $deleted = [];
|
|
|
|
my $otherSessions = [];
|
2020-04-25 00:43:56 +02:00
|
|
|
my @otherSessionsId = ();
|
2020-04-25 11:41:08 +02:00
|
|
|
|
|
|
|
my $moduleOptions = $self->conf->{globalStorageOptions} || {};
|
2017-02-17 08:40:18 +01:00
|
|
|
$moduleOptions->{backend} = $self->conf->{globalStorage};
|
|
|
|
|
|
|
|
my $sessions = $self->module->searchOn(
|
|
|
|
$moduleOptions,
|
|
|
|
$self->conf->{whatToTrace},
|
|
|
|
$req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
|
|
|
);
|
2019-05-27 22:41:51 +02:00
|
|
|
|
2019-05-28 17:32:06 +02:00
|
|
|
if ( $self->conf->{securedCookie} == 2 ) {
|
2019-05-27 22:41:51 +02:00
|
|
|
$self->logger->debug("Looking for double sessions...");
|
2019-05-28 17:32:06 +02:00
|
|
|
$linkedSessionId = $sessions->{ $req->id }->{_httpSession};
|
|
|
|
my $msg =
|
|
|
|
$linkedSessionId
|
|
|
|
? "Linked session found -> $linkedSessionId / " . $req->id
|
|
|
|
: "NO linked session found!";
|
|
|
|
$self->logger->debug($msg);
|
2019-05-27 22:41:51 +02:00
|
|
|
}
|
|
|
|
|
2017-02-17 08:40:18 +01:00
|
|
|
foreach my $id ( keys %$sessions ) {
|
|
|
|
next if ( $req->id eq $id );
|
2019-05-28 17:32:06 +02:00
|
|
|
next if ( $linkedSessionId and $id eq $linkedSessionId );
|
2017-02-17 08:40:18 +01:00
|
|
|
my $session = $self->p->getApacheSession($id) or next;
|
|
|
|
if (
|
2020-04-21 15:49:31 +02:00
|
|
|
$self->singleSessionRule->( $req, $req->sessionInfo )
|
|
|
|
or ( $self->singleIPRule->( $req, $req->sessionInfo )
|
2017-02-17 08:40:18 +01:00
|
|
|
and $req->{sessionInfo}->{ipAddr} ne $session->data->{ipAddr} )
|
|
|
|
)
|
|
|
|
{
|
2017-02-19 08:17:48 +01:00
|
|
|
push @$deleted, $self->p->_sumUpSession( $session->data );
|
2017-02-17 08:40:18 +01:00
|
|
|
$self->p->_deleteSession( $req, $session, 1 );
|
|
|
|
}
|
|
|
|
else {
|
2020-04-25 00:43:56 +02:00
|
|
|
push @$otherSessions, $self->p->_sumUpSession( $session->data );
|
|
|
|
push @otherSessionsId, $id;
|
2017-02-17 08:40:18 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-21 15:49:31 +02:00
|
|
|
|
2020-04-25 00:43:56 +02:00
|
|
|
$token = $self->ott->createToken( {
|
|
|
|
user => $req->{sessionInfo}->{ $self->conf->{whatToTrace} },
|
|
|
|
sessions => to_json( \@otherSessionsId )
|
|
|
|
}
|
|
|
|
) if @otherSessionsId;
|
|
|
|
|
2020-04-21 15:49:31 +02:00
|
|
|
if ( $self->singleUserByIPRule->( $req, $req->sessionInfo ) ) {
|
2017-02-17 08:40:18 +01:00
|
|
|
my $sessions =
|
|
|
|
$self->module->searchOn( $moduleOptions, 'ipAddr',
|
2020-04-21 00:03:02 +02:00
|
|
|
$req->sessionInfo->{ipAddr} );
|
2017-02-17 08:40:18 +01:00
|
|
|
foreach my $id ( keys %$sessions ) {
|
|
|
|
next if ( $req->id eq $id );
|
|
|
|
my $session = $self->p->getApacheSession($id) or next;
|
|
|
|
unless ( $req->{sessionInfo}->{ $self->conf->{whatToTrace} } eq
|
|
|
|
$session->data->{ $self->conf->{whatToTrace} } )
|
|
|
|
{
|
2017-02-19 08:17:48 +01:00
|
|
|
push @$deleted, $self->p->_sumUpSession( $session->data );
|
2017-02-17 08:40:18 +01:00
|
|
|
$self->p->_deleteSession( $req, $session, 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-25 11:41:08 +02:00
|
|
|
|
|
|
|
$html = $self->p->mkSessionArray( $req, $deleted, 'sessionsDeleted', 1 )
|
2017-02-17 08:40:18 +01:00
|
|
|
if ( $self->conf->{notifyDeleted} and @$deleted );
|
2020-04-25 11:41:08 +02:00
|
|
|
$html .=
|
|
|
|
$self->p->mkSessionArray( $req, $otherSessions, 'otherSessions', 1 )
|
2020-04-25 14:48:27 +02:00
|
|
|
. $self->_mkRemoveOtherLink( $req, $token )
|
2017-02-17 08:40:18 +01:00
|
|
|
if ( $self->conf->{notifyOther} and @$otherSessions );
|
|
|
|
|
2020-04-25 11:41:08 +02:00
|
|
|
$req->info($html);
|
2020-02-25 22:01:04 +01:00
|
|
|
return PE_OK;
|
2017-02-17 08:40:18 +01:00
|
|
|
}
|
|
|
|
|
2020-04-25 00:43:56 +02:00
|
|
|
sub removeOther {
|
|
|
|
my ( $self, $req ) = @_;
|
2020-05-24 00:04:33 +02:00
|
|
|
my $res = PE_OK;
|
2020-04-26 11:24:31 +02:00
|
|
|
my $count = 0;
|
2020-04-25 00:43:56 +02:00
|
|
|
$req->{urldc} = decode_base64( $req->param('url') );
|
2020-04-26 11:24:31 +02:00
|
|
|
|
2020-04-25 00:43:56 +02:00
|
|
|
if ( my $token = $req->param('token') ) {
|
|
|
|
if ( $token = $self->ott->getToken($token) ) {
|
|
|
|
|
|
|
|
# Read sessions from token
|
|
|
|
my $sessions = eval { from_json( $token->{sessions} ) };
|
|
|
|
if ($@) {
|
|
|
|
$self->logger->error("Bad encoding in OTT: $@");
|
|
|
|
$res = PE_ERROR;
|
|
|
|
}
|
|
|
|
my $as;
|
2020-04-26 11:24:31 +02:00
|
|
|
foreach (@$sessions) {
|
|
|
|
unless ( $as = $self->p->getApacheSession($_) ) {
|
2020-04-25 00:43:56 +02:00
|
|
|
$self->userLogger->info(
|
2020-04-26 11:24:31 +02:00
|
|
|
"SingleSession: session $_ expired");
|
2020-04-25 00:43:56 +02:00
|
|
|
next;
|
|
|
|
}
|
|
|
|
my $user = $token->{user};
|
|
|
|
if ( $req->{userData}->{ $self->{conf}->{whatToTrace} } eq
|
|
|
|
$user )
|
|
|
|
{
|
2020-04-26 11:24:31 +02:00
|
|
|
$self->userLogger->info("Remove \"$user\" session: $_");
|
2020-04-25 00:43:56 +02:00
|
|
|
$self->p->_deleteSession( $req, $as, 1 );
|
2020-04-26 11:24:31 +02:00
|
|
|
$count++;
|
2020-04-25 00:43:56 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->userLogger->warn(
|
2020-08-28 21:53:19 +02:00
|
|
|
"SingleSession called with an invalid token");
|
2020-04-25 00:43:56 +02:00
|
|
|
$res = PE_TOKENEXPIRED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->userLogger->error(
|
|
|
|
"SingleSession called with an expired token");
|
|
|
|
$res = PE_TOKENEXPIRED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->userLogger->error('SingleSession called without token');
|
|
|
|
$res = PE_NOTOKEN;
|
|
|
|
}
|
2020-04-25 01:02:25 +02:00
|
|
|
|
2020-04-25 00:43:56 +02:00
|
|
|
return $self->p->do( $req, [ sub { $res } ] ) if $res;
|
2020-11-19 21:51:58 +01:00
|
|
|
$self->userLogger->info("$count remaining session(s) removed");
|
2020-04-25 00:43:56 +02:00
|
|
|
$req->mustRedirect(1);
|
|
|
|
return $self->p->autoRedirect($req);
|
|
|
|
}
|
|
|
|
|
2017-02-17 08:40:18 +01:00
|
|
|
# Build the removeOther link
|
|
|
|
# Last part of URL is built trough javascript
|
|
|
|
# @return removeOther link in HTML code
|
|
|
|
sub _mkRemoveOtherLink {
|
2020-04-25 00:43:56 +02:00
|
|
|
my ( $self, $req, $token ) = @_;
|
2017-02-17 08:40:18 +01:00
|
|
|
|
2017-10-10 13:04:40 +02:00
|
|
|
return $self->loadTemplate(
|
2019-06-28 13:40:56 +02:00
|
|
|
$req,
|
2017-10-10 13:04:40 +02:00
|
|
|
'removeOther',
|
|
|
|
params => {
|
2020-04-25 00:43:56 +02:00
|
|
|
link => $self->conf->{portal} . "removeOther?token=$token"
|
2017-10-10 13:04:40 +02:00
|
|
|
}
|
|
|
|
);
|
2017-02-17 08:40:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
1;
|