Other session plugins (#595)
This commit is contained in:
parent
97b8b40cc5
commit
87bb55cb00
|
@ -0,0 +1,60 @@
|
|||
package Lemonldap::NG::Portal::Lib::OtherSessions;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
||||
has module =>
|
||||
( is => 'rw', default => 'Lemonldap::NG::Common::Apache::Session' );
|
||||
|
||||
has moduleOpts => (
|
||||
is => 'rw',
|
||||
default => sub {
|
||||
my %opts = %{ $_[0]->{conf}->{globalStorageOptions} || {} };
|
||||
$opts{backend} = $_[0]->{conf}->{globalStorage};
|
||||
return \%opts;
|
||||
}
|
||||
);
|
||||
|
||||
# Build an HTML array to display sessions
|
||||
# @param $sessions Array ref of hash ref containing sessions datas
|
||||
# @param $title Title of the array
|
||||
# @param $displayUser To display "User" column
|
||||
# @param $displaError To display "Error" column
|
||||
# @return HTML string
|
||||
sub mkSessionArray {
|
||||
my ( $self, $sessions, $title, $displayUser, $displayError ) = @_;
|
||||
|
||||
return "" unless ( ref $sessions eq "ARRAY" and @$sessions );
|
||||
|
||||
my $tmp = $title ? qq'<h3 trspan="$title"></h3>' : "";
|
||||
$tmp .= "<table class=\"info\"><tbody>";
|
||||
|
||||
$tmp .= "<tr>";
|
||||
$tmp .= '<th trspan="user"></th>'
|
||||
if ($displayUser);
|
||||
$tmp .= '<th trspan="date"></th>';
|
||||
$tmp .= '<th trspan="ipAddr"></th>';
|
||||
$tmp .= "<th>" . $self->{sessionDataToRemember}->{$_} . "</th>"
|
||||
foreach ( keys %{ $self->{sessionDataToRemember} } );
|
||||
$tmp .= '<th trspan="errorMsg"></th>'
|
||||
if ($displayError);
|
||||
$tmp .= '</tr>';
|
||||
|
||||
foreach my $session (@$sessions) {
|
||||
$tmp .= "<tr>";
|
||||
$tmp .= "<td>$session->{user}</td>" if ($displayUser);
|
||||
$tmp .=
|
||||
"<td><script type=\"text/javascript\">var _date=new Date($session->{_utime}*1000);document.write(_date.toLocaleString());</script></td>";
|
||||
$tmp .= "<td>$session->{ipAddr}</td>";
|
||||
$tmp .= "<td>" . ( $session->{$_} || "" ) . "</td>"
|
||||
foreach ( keys %{ $self->{sessionDataToRemember} } );
|
||||
$tmp .= "<td>$session->{error}</td>" if ($displayError);
|
||||
$tmp .= "</tr>";
|
||||
}
|
||||
$tmp .= '</tbody></table>';
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
1;
|
|
@ -21,6 +21,7 @@ our @pList = (
|
|||
u2fActivation => '::Plugins::U2F',
|
||||
u2fSelfRegistration => '::Register::U2F',
|
||||
notification => '::Plugins::Notifications',
|
||||
checkLogins => '::Plugins::History',
|
||||
);
|
||||
|
||||
##@method list enabledPlugins
|
||||
|
@ -28,11 +29,12 @@ our @pList = (
|
|||
#@return list of enabled plugins
|
||||
sub enabledPlugins {
|
||||
my ($self) = @_;
|
||||
my $conf = $self->conf;
|
||||
my @res;
|
||||
|
||||
# Search for Issuer* modules enabled
|
||||
foreach my $key (qw(SAML OpenID CAS OpenIDConnect Get)) {
|
||||
if ( $self->conf->{"issuerDB${key}Activation"} ) {
|
||||
if ( $conf->{"issuerDB${key}Activation"} ) {
|
||||
$self->logger->debug("Issuer${key} enabled");
|
||||
push @res, "::Issuer::$key";
|
||||
}
|
||||
|
@ -40,31 +42,37 @@ sub enabledPlugins {
|
|||
|
||||
# Load static plugin list
|
||||
for ( my $i = 0 ; $i < @pList ; $i += 2 ) {
|
||||
push @res, $pList[ $i + 1 ] if ( $self->conf->{ $pList[$i] } );
|
||||
push @res, $pList[ $i + 1 ] if ( $conf->{ $pList[$i] } );
|
||||
}
|
||||
|
||||
# Load single session
|
||||
push @res, '::Plugins::SingleSession'
|
||||
if ( $conf->{singleSession}
|
||||
or $conf->{singleIP}
|
||||
or $conf->{singleUserByIP}
|
||||
or $conf->{notifyOther} );
|
||||
|
||||
# Check if SOAP is enabled
|
||||
push @res, '::Plugins::SOAPServer'
|
||||
if ( $self->conf->{soapSessionServer}
|
||||
or $self->conf->{soapConfigServer} );
|
||||
if ( $conf->{soapSessionServer}
|
||||
or $conf->{soapConfigServer} );
|
||||
|
||||
# Add REST (check is done by it)
|
||||
push @res, '::Plugins::RESTServer';
|
||||
|
||||
if ( my $p = $self->conf->{passwordDB} ) {
|
||||
if ( my $p = $conf->{passwordDB} ) {
|
||||
push @res, "::Password::$p" if ( $p ne 'Null' );
|
||||
}
|
||||
|
||||
# Check if register is enabled
|
||||
push @res, '::Plugins::Register'
|
||||
if ( $self->conf->{registerDB} and $self->conf->{registerDB} ne 'Null' );
|
||||
if ( $conf->{registerDB} and $conf->{registerDB} ne 'Null' );
|
||||
|
||||
# Check if custom plugins are required
|
||||
# TODO: change this name
|
||||
if ( $self->conf->{customPlugins} ) {
|
||||
$self->logger->debug(
|
||||
'Custom plugins: ' . $self->conf->{customPlugins} );
|
||||
push @res, grep ( /\w/, split( /,\s*/, $self->conf->{customPlugins} ) );
|
||||
if ( $conf->{customPlugins} ) {
|
||||
$self->logger->debug( 'Custom plugins: ' . $conf->{customPlugins} );
|
||||
push @res, grep ( /\w/, split( /,\s*/, $conf->{customPlugins} ) );
|
||||
}
|
||||
return @res;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package Lemonldap::NG::Portal::Plugins::History;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin',
|
||||
'Lemonldap::NG::Portal::Lib::OtherSessions';
|
||||
|
||||
sub afterDatas { 'run' }
|
||||
|
||||
sub run {
|
||||
my ( $self, $req ) = @_;
|
||||
$req->info(
|
||||
(
|
||||
$req->sessionInfo->{loginHistory}->{successLogin}
|
||||
? $self->mkSessionArray(
|
||||
$req->sessionInfo->{loginHistory}->{successLogin},
|
||||
'lastLogins', 0, 0 )
|
||||
: ""
|
||||
)
|
||||
. (
|
||||
$req->sessionInfo->{loginHistory}->{failedLogin}
|
||||
? $self->mkSessionArray(
|
||||
$req->sessionInfo->{loginHistory}->{failedLogin},
|
||||
'lastFailedLogins', 0, 1 )
|
||||
: ""
|
||||
)
|
||||
);
|
||||
PE_OK;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,97 @@
|
|||
package Lemonldap::NG::Portal::Plugins::SingleSession;
|
||||
|
||||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
|
||||
|
||||
our $VERSION = '2.0.0';
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin',
|
||||
'Lemonldap::NG::Portal::Lib::OtherSessions';
|
||||
|
||||
sub afterDatas { 'run' }
|
||||
|
||||
sub init { 1 }
|
||||
|
||||
sub run {
|
||||
my ( $self, $req ) = @_;
|
||||
my $deleted = [];
|
||||
my $otherSessions = [];
|
||||
|
||||
my $moduleOptions = $self->conf->{globalStorageOptions} || {};
|
||||
$moduleOptions->{backend} = $self->conf->{globalStorage};
|
||||
|
||||
my $sessions = $self->module->searchOn(
|
||||
$moduleOptions,
|
||||
$self->conf->{whatToTrace},
|
||||
$req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
||||
);
|
||||
foreach my $id ( keys %$sessions ) {
|
||||
next if ( $req->id eq $id );
|
||||
my $session = $self->p->getApacheSession($id) or next;
|
||||
if (
|
||||
$self->conf->{singleSession}
|
||||
or ( $self->conf->{singleIP}
|
||||
and $req->{sessionInfo}->{ipAddr} ne $session->data->{ipAddr} )
|
||||
)
|
||||
{
|
||||
push @$deleted, $self->_sumUpSession( $session->data );
|
||||
$self->p->_deleteSession( $req, $session, 1 );
|
||||
}
|
||||
else {
|
||||
push @$otherSessions, $self->_sumUpSession( $session->data );
|
||||
}
|
||||
}
|
||||
if ( $self->conf->{singleUserByIP} ) {
|
||||
my $sessions =
|
||||
$self->module->searchOn( $moduleOptions, 'ipAddr',
|
||||
$req->sessionInfo->ipAddr );
|
||||
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} } )
|
||||
{
|
||||
push @$deleted, $self->_sumUpSession( $session->data );
|
||||
$self->p->_deleteSession( $req, $session, 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
$req->info( $self->mkSessionArray( $deleted, 'sessionsDeleted', 1 ) )
|
||||
if ( $self->conf->{notifyDeleted} and @$deleted );
|
||||
$req->info( $self->mkSessionArray( $otherSessions, 'otherSessions', 1 )
|
||||
. $self->_mkRemoveOtherLink() )
|
||||
if ( $self->conf->{notifyOther} and @$otherSessions );
|
||||
|
||||
PE_OK;
|
||||
}
|
||||
|
||||
# put main session data into a hash ref
|
||||
# @param hashref $session The session to sum up
|
||||
# @return hashref
|
||||
sub _sumUpSession {
|
||||
my ( $self, $session, $withoutUser ) = @_;
|
||||
my $res =
|
||||
$withoutUser
|
||||
? {}
|
||||
: { user => $session->{ $self->conf->{whatToTrace} } };
|
||||
$res->{$_} = $session->{$_}
|
||||
foreach ( "_utime", "ipAddr",
|
||||
keys %{ $self->conf->{sessionDataToRemember} } );
|
||||
return $res;
|
||||
}
|
||||
|
||||
# Build the removeOther link
|
||||
# Last part of URL is built trough javascript
|
||||
# @return removeOther link in HTML code
|
||||
sub _mkRemoveOtherLink {
|
||||
my $self = shift;
|
||||
|
||||
my $link = $self->conf->{portal} . "?removeOther=1";
|
||||
|
||||
# TODO: remove this
|
||||
return
|
||||
qq'<p class="removeOther"><a href="$link" onclick="_go=0" trspan="removeOtherSessions></a></p>';
|
||||
}
|
||||
|
||||
1;
|
|
@ -83,17 +83,12 @@
|
|||
"PE81":"Invalid authentication attempt",
|
||||
"PE82":"Exceeded authentication timeout",
|
||||
"PE83":"U2F verification failed",
|
||||
"PM3":"The following sessions have been closed",
|
||||
"PM4":"Other active sessions",
|
||||
"PM5":"Remove other sessions",
|
||||
"PM8":"Select your Identity Provider",
|
||||
"PM10":"Remember my choice",
|
||||
"PM11":"Logout from service providers...",
|
||||
"PM12":"Redirection in progress...",
|
||||
"PM13":"Go back to service provider",
|
||||
"PM17":"Update Common Domain Cookie",
|
||||
"PM22":"Your last logins",
|
||||
"PM23":"Your last failed logins",
|
||||
"accept":"Accept",
|
||||
"accessDenied":"You have no access authorization for this application",
|
||||
"accountCreated":"Your account has been created, your temporary password has been sent to your mail address.",
|
||||
|
@ -162,6 +157,7 @@
|
|||
"openidRpns":"Parameter %s requested for federation isn't available",
|
||||
"openSessionSpace":"This space allow you to open a SSO session. This will help you to securely access to all applications authorized by your profil.",
|
||||
"openSSOSession":"Open your SSO session",
|
||||
"otherSessions":"Other active sessions",
|
||||
"password": "Password",
|
||||
"ppGrace": "authentications remaining, change your password!",
|
||||
"pwdChanged":"Your password was changed.",
|
||||
|
@ -178,6 +174,7 @@
|
|||
"register": "Register",
|
||||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Remember my choice",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"requestIssuedFromIP":"The request was issued from IP",
|
||||
"resendConfirmMail":"Resend confirmation mail?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
|
@ -186,6 +183,7 @@
|
|||
"sendPwd":"Send me a new password",
|
||||
"serverError":"Error occurs on the server",
|
||||
"serviceProvidedBy":"Service provided by",
|
||||
"sessionsDeleted":"The following sessions have been closed",
|
||||
"SSOSessionInactive":"SSO session inactive",
|
||||
"submit":"Submit",
|
||||
"touchU2fDevice": "Please touch the flashing U2F device now.",
|
||||
|
|
|
@ -83,17 +83,12 @@
|
|||
"PE81":"Tentative d'authentification invalide",
|
||||
"PE82":"Délai d'authentification dépassé",
|
||||
"PE83":"La vérification U2F a échoué",
|
||||
"PM3":"Les sessions suivantes ont été fermées",
|
||||
"PM4":"Autres sessions ouvertes",
|
||||
"PM5":"Fermer les autres sessions",
|
||||
"PM8":"Choisissez votre fournisseur d'identité",
|
||||
"PM10":"Se souvenir de mon choix",
|
||||
"PM11":"Déconnexion des services...",
|
||||
"PM12":"Redirection en cours...",
|
||||
"PM13":"Retourner sur le fournisseur de service",
|
||||
"PM17":"Mise à jour du cookie de domaine commun",
|
||||
"PM22":"Vos dernières connexions",
|
||||
"PM23":"Vos dernières connexions refusées",
|
||||
"accept":"Accepter",
|
||||
"accessDenied":"Vous n'avez pas les droits d'accès à cette application",
|
||||
"accountCreated":"Votre compte a été créé, un mot de passe temporaire a été envoyé à votre adresse mail.",
|
||||
|
@ -162,6 +157,7 @@
|
|||
"openidRpns":"Le paramètre %s exigé pour la fédération n'est pas disponible",
|
||||
"openSessionSpace":"Cet espace vous permet d'ouvrir une session SSO. Celle-ci vous aidera à accéder de manière totalement sécurisée à l'ensemble des applications autorisées par votre profil utilisateur.",
|
||||
"openSSOSession":"Ouvrir une session SSO",
|
||||
"otherSessions":"Autres sessions ouvertes",
|
||||
"password": "Mot-de-passe",
|
||||
"ppGrace": "authentifications restantes, changez votre mot de passe !",
|
||||
"pwdChange":"Changement de mot de passe",
|
||||
|
@ -178,6 +174,7 @@
|
|||
"register": "Enregistrer",
|
||||
"registerRequestAlreadyIssued":"Une demande de création pour ce compte a déjà été faite le ",
|
||||
"rememberChoice":"Se souvenir de mon choix",
|
||||
"removeOtherSessions":"Fermer les autres sessions",
|
||||
"requestIssuedFromIP":"La demande provient de l'IP",
|
||||
"resendConfirmMail":"Renvoyer le mail de confirmation ?",
|
||||
"resentConfirm":"Voulez-vous que le message de confirmation soit renvoyé ?",
|
||||
|
@ -186,6 +183,7 @@
|
|||
"sendPwd":"Envoyez-moi un nouveau mot de passe",
|
||||
"serverError":"Une erreur est survenue sur le serveur",
|
||||
"serviceProvidedBy":"Ce service est fourni par",
|
||||
"sessionsDeleted":"Les sessions suivantes ont été fermées",
|
||||
"SSOSessionInactive":"Session SSO inactive",
|
||||
"submit":"Envoyer",
|
||||
"touchU2fDevice": "Poser votre doigt sur le périphérique U2F",
|
||||
|
|
56
lemonldap-ng-portal/t/62-singleSession.t
Normal file
56
lemonldap-ng-portal/t/62-singleSession.t
Normal file
|
@ -0,0 +1,56 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
}
|
||||
|
||||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new(
|
||||
{
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
singleSession => 1,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
expectOK($res);
|
||||
my $id1 = expectCookie($res);
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
expectOK($res);
|
||||
my $id2 = expectCookie($res);
|
||||
|
||||
ok($res=$client->_get('/',cookie=>"lemonldap=$id2"),'Use id 2');
|
||||
count(1);
|
||||
expectOK($res);
|
||||
|
||||
ok($res=$client->_get('/',cookie=>"lemonldap=$id1"),'Use id 1');
|
||||
count(1);
|
||||
expectReject($res);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
Loading…
Reference in New Issue
Block a user