Fix logins history update - Failed and Granted Access with and without SFA (#1501)
This commit is contained in:
parent
1791747281
commit
0ed8dbdde2
|
@ -13,11 +13,11 @@ use strict;
|
||||||
use Mouse;
|
use Mouse;
|
||||||
use JSON qw(from_json to_json);
|
use JSON qw(from_json to_json);
|
||||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||||
PE_ERROR
|
PE_ERROR
|
||||||
PE_NOTOKEN
|
PE_NOTOKEN
|
||||||
PE_OK
|
PE_OK
|
||||||
PE_SENDRESPONSE
|
PE_SENDRESPONSE
|
||||||
PE_TOKENEXPIRED
|
PE_TOKENEXPIRED
|
||||||
);
|
);
|
||||||
|
|
||||||
our $VERSION = '2.0.0';
|
our $VERSION = '2.0.0';
|
||||||
|
@ -35,8 +35,8 @@ has sfReq => ( is => 'rw' );
|
||||||
has ott => (
|
has ott => (
|
||||||
is => 'rw',
|
is => 'rw',
|
||||||
default => sub {
|
default => sub {
|
||||||
my $ott =
|
my $ott = $_[0]->{p}
|
||||||
$_[0]->{p}->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
->loadModule('Lemonldap::NG::Portal::Lib::OneTimeToken');
|
||||||
$ott->timeout( $_[0]->{conf}->{formTimeout} );
|
$ott->timeout( $_[0]->{conf}->{formTimeout} );
|
||||||
return $ott;
|
return $ott;
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,10 @@ sub init {
|
||||||
for my $i ( 0 .. 1 ) {
|
for my $i ( 0 .. 1 ) {
|
||||||
foreach (
|
foreach (
|
||||||
split /,\s*/,
|
split /,\s*/,
|
||||||
$self->conf->{ $i ? 'available2FSelfRegistration' : 'available2F' }
|
$self->conf->{ $i
|
||||||
)
|
? 'available2FSelfRegistration'
|
||||||
|
: 'available2F' }
|
||||||
|
)
|
||||||
{
|
{
|
||||||
my $prefix = lc($_);
|
my $prefix = lc($_);
|
||||||
$prefix =~ s/2f$//i;
|
$prefix =~ s/2f$//i;
|
||||||
|
@ -62,9 +64,10 @@ sub init {
|
||||||
# Unless $rule, skip loading
|
# Unless $rule, skip loading
|
||||||
if ( $self->conf->{$ap} ) {
|
if ( $self->conf->{$ap} ) {
|
||||||
$self->logger->debug("Trying to load $_ 2F");
|
$self->logger->debug("Trying to load $_ 2F");
|
||||||
my $m =
|
my $m
|
||||||
$self->p->loadPlugin( $i ? "::2F::Register::$_" : "::2F::$_" )
|
= $self->p->loadPlugin(
|
||||||
or return 0;
|
$i ? "::2F::Register::$_" : "::2F::$_" )
|
||||||
|
or return 0;
|
||||||
|
|
||||||
# Rule and prefix may be modified by 2F module, reread them
|
# Rule and prefix may be modified by 2F module, reread them
|
||||||
my $rule = $self->conf->{$ap};
|
my $rule = $self->conf->{$ap};
|
||||||
|
@ -74,13 +77,13 @@ sub init {
|
||||||
$rule = $self->p->HANDLER->substitute($rule);
|
$rule = $self->p->HANDLER->substitute($rule);
|
||||||
unless ( $rule = $self->p->HANDLER->buildSub($rule) ) {
|
unless ( $rule = $self->p->HANDLER->buildSub($rule) ) {
|
||||||
$self->error( 'External 2F rule error: '
|
$self->error( 'External 2F rule error: '
|
||||||
. $self->p->HANDLER->tsv->{jail}->error );
|
. $self->p->HANDLER->tsv->{jail}->error );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Store module
|
# Store module
|
||||||
push @{ $self->{ $i ? 'sfRModules' : 'sfModules' } },
|
push @{ $self->{ $i ? 'sfRModules' : 'sfModules' } },
|
||||||
{ p => $prefix, m => $m, r => $rule };
|
{ p => $prefix, m => $m, r => $rule };
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->logger->debug(' -> not enabled');
|
$self->logger->debug(' -> not enabled');
|
||||||
|
@ -94,10 +97,10 @@ sub init {
|
||||||
$self->p->HANDLER->substitute( $self->conf->{sfRequired} )
|
$self->p->HANDLER->substitute( $self->conf->{sfRequired} )
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
$self->error( 'Error in sfRequired rule'
|
$self->error( 'Error in sfRequired rule'
|
||||||
. $self->p->HANDLER->tsv->{jail}->error );
|
. $self->p->HANDLER->tsv->{jail}->error );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,14 +161,14 @@ sub run {
|
||||||
if ( $self->sfReq->( $req, $req->sessionInfo ) ) {
|
if ( $self->sfReq->( $req, $req->sessionInfo ) ) {
|
||||||
$self->logger->debug("2F is required...");
|
$self->logger->debug("2F is required...");
|
||||||
$self->logger->debug(" -> Register 2F");
|
$self->logger->debug(" -> Register 2F");
|
||||||
$req->pdata->{sfRegToken} =
|
$req->pdata->{sfRegToken}
|
||||||
$self->ott->createToken( $req->sessionInfo );
|
= $self->ott->createToken( $req->sessionInfo );
|
||||||
$self->logger->debug("Just one 2F is enabled");
|
$self->logger->debug("Just one 2F is enabled");
|
||||||
$self->logger->debug(" -> Redirect to /2fregisters/");
|
$self->logger->debug(" -> Redirect to /2fregisters/");
|
||||||
$req->response(
|
$req->response(
|
||||||
[
|
[ 302,
|
||||||
302,
|
[ Location => $self->conf->{portal} . '/2fregisters/' ],
|
||||||
[ Location => $self->conf->{portal} . '/2fregisters/' ], []
|
[]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
return PE_SENDRESPONSE;
|
return PE_SENDRESPONSE;
|
||||||
|
@ -176,11 +179,12 @@ sub run {
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->userLogger->info( 'Second factor required for '
|
$self->userLogger->info( 'Second factor required for '
|
||||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||||
|
|
||||||
# Store user data in a token
|
# Store user data in a token
|
||||||
$req->sessionInfo->{_2fRealSession} = $req->id;
|
$req->sessionInfo->{_2fRealSession} = $req->id;
|
||||||
$req->sessionInfo->{_2fUrldc} = $req->urldc;
|
$req->sessionInfo->{_2fUrldc} = $req->urldc;
|
||||||
|
$req->sessionInfo->{_2fUtime} = $req->{sessionInfo}->{_utime};
|
||||||
my $token = $self->ott->createToken( $req->sessionInfo );
|
my $token = $self->ott->createToken( $req->sessionInfo );
|
||||||
delete $req->{authResult};
|
delete $req->{authResult};
|
||||||
|
|
||||||
|
@ -197,9 +201,10 @@ sub run {
|
||||||
$req,
|
$req,
|
||||||
'2fchoice',
|
'2fchoice',
|
||||||
params => {
|
params => {
|
||||||
SKIN => $self->conf->{portalSkin},
|
SKIN => $self->conf->{portalSkin},
|
||||||
TOKEN => $token,
|
TOKEN => $token,
|
||||||
MODULES => [ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
|
MODULES =>
|
||||||
|
[ map { { CODE => $_->prefix, LOGO => $_->logo } } @am ],
|
||||||
CHECKLOGINS => $checkLogins
|
CHECKLOGINS => $checkLogins
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -225,15 +230,16 @@ sub _choice {
|
||||||
|
|
||||||
# Restore session
|
# Restore session
|
||||||
unless ( $token = $req->param('token') ) {
|
unless ( $token = $req->param('token') ) {
|
||||||
$self->userLogger->error( $self->prefix . ' 2F access without token' );
|
$self->userLogger->error(
|
||||||
|
$self->prefix . ' 2F access without token' );
|
||||||
$req->mustRedirect(1);
|
$req->mustRedirect(1);
|
||||||
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
|
return $self->p->do( $req, [ sub {PE_NOTOKEN} ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
my $session;
|
my $session;
|
||||||
unless ( $session = $self->ott->getToken($token) ) {
|
unless ( $session = $self->ott->getToken($token) ) {
|
||||||
$self->userLogger->info('Token expired');
|
$self->userLogger->info('Token expired');
|
||||||
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
|
return $self->p->do( $req, [ sub {PE_TOKENEXPIRED} ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
$req->sessionInfo($session);
|
$req->sessionInfo($session);
|
||||||
|
@ -248,8 +254,7 @@ sub _choice {
|
||||||
$req->authResult($res);
|
$req->authResult($res);
|
||||||
return $self->p->do(
|
return $self->p->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ sub {$res}, 'controlUrl',
|
||||||
sub { $res }, 'controlUrl',
|
|
||||||
'buildCookie', @{ $self->p->endAuth },
|
'buildCookie', @{ $self->p->endAuth },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -264,7 +269,8 @@ sub _redirect {
|
||||||
my $arg = $req->env->{QUERY_STRING};
|
my $arg = $req->env->{QUERY_STRING};
|
||||||
$self->logger->debug('Call sfEngine _redirect method');
|
$self->logger->debug('Call sfEngine _redirect method');
|
||||||
return [
|
return [
|
||||||
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ], []
|
302, [ Location => $self->conf->{portal} . ( $arg ? "?$arg" : '' ) ],
|
||||||
|
[]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,25 +300,26 @@ sub _displayRegister {
|
||||||
'Looking if ' . $m->{m}->prefix . '2F register is available' );
|
'Looking if ' . $m->{m}->prefix . '2F register is available' );
|
||||||
if ( $m->{r}->( $req, $req->userData ) ) {
|
if ( $m->{r}->( $req, $req->userData ) ) {
|
||||||
push @am,
|
push @am,
|
||||||
{
|
{
|
||||||
CODE => $m->{m}->prefix,
|
CODE => $m->{m}->prefix,
|
||||||
URL => '/2fregisters/' . $m->{m}->prefix,
|
URL => '/2fregisters/' . $m->{m}->prefix,
|
||||||
LOGO => $m->{m}->logo,
|
LOGO => $m->{m}->logo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( @am == 1
|
if ( @am == 1
|
||||||
and not( $req->userData->{_2fDevices} or $req->data->{sfRegRequired} ) )
|
and
|
||||||
|
not( $req->userData->{_2fDevices} or $req->data->{sfRegRequired} ) )
|
||||||
{
|
{
|
||||||
return [ 302, [ Location => $self->conf->{portal} . $am[0]->{URL} ],
|
return [ 302, [ Location => $self->conf->{portal} . $am[0]->{URL} ],
|
||||||
[] ];
|
[] ];
|
||||||
}
|
}
|
||||||
|
|
||||||
my $_2fDevices =
|
my $_2fDevices = $req->userData->{_2fDevices}
|
||||||
$req->userData->{_2fDevices}
|
? eval {
|
||||||
? eval { from_json( $req->userData->{_2fDevices},
|
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
|
||||||
{ allow_nonref => 1 } ); }
|
}
|
||||||
: undef;
|
: undef;
|
||||||
|
|
||||||
unless ($_2fDevices) {
|
unless ($_2fDevices) {
|
||||||
$self->logger->debug("No 2F Device found");
|
$self->logger->debug("No 2F Device found");
|
||||||
|
@ -358,11 +365,11 @@ sub register {
|
||||||
$self->logger->debug(' -> OK');
|
$self->logger->debug(' -> OK');
|
||||||
my $name = $m->{m}->prefix;
|
my $name = $m->{m}->prefix;
|
||||||
push @am,
|
push @am,
|
||||||
{
|
{
|
||||||
name => $name,
|
name => $name,
|
||||||
logo => $m->{m}->logo,
|
logo => $m->{m}->logo,
|
||||||
url => "/2fregisters/$name"
|
url => "/2fregisters/$name"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $self->p->sendJSONresponse( $req, \@am );
|
return $self->p->sendJSONresponse( $req, \@am );
|
||||||
|
@ -371,12 +378,12 @@ sub register {
|
||||||
sub restoreSession {
|
sub restoreSession {
|
||||||
my ( $self, $req, @path ) = @_;
|
my ( $self, $req, @path ) = @_;
|
||||||
my $token = $req->pdata->{sfRegToken}
|
my $token = $req->pdata->{sfRegToken}
|
||||||
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
|
or return [ 302, [ Location => $self->conf->{portal} ], [] ];
|
||||||
$req->userData( $self->ott->getToken( $token, 1 ) );
|
$req->userData( $self->ott->getToken( $token, 1 ) );
|
||||||
$req->data->{sfRegRequired} = 1;
|
$req->data->{sfRegRequired} = 1;
|
||||||
return $req->method eq 'POST'
|
return $req->method eq 'POST'
|
||||||
? $self->register( $req, @path )
|
? $self->register( $req, @path )
|
||||||
: $self->_displayRegister( $req, @path );
|
: $self->_displayRegister( $req, @path );
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -84,9 +84,8 @@ sub init {
|
||||||
my ( $self, $args ) = @_;
|
my ( $self, $args ) = @_;
|
||||||
$args ||= {};
|
$args ||= {};
|
||||||
$self->localConfig(
|
$self->localConfig(
|
||||||
{
|
{ %{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
|
||||||
%{ Lemonldap::NG::Common::Conf->new( $args->{configStorage} )
|
->getLocalConf('portal')
|
||||||
->getLocalConf('portal')
|
|
||||||
},
|
},
|
||||||
%$args
|
%$args
|
||||||
}
|
}
|
||||||
|
@ -110,33 +109,33 @@ sub init {
|
||||||
# Handle requests (other path may be declared in enabled plugins)
|
# Handle requests (other path may be declared in enabled plugins)
|
||||||
$self
|
$self
|
||||||
|
|
||||||
# "/" or undeclared paths
|
# "/" or undeclared paths
|
||||||
->addUnauthRoute( '*' => 'login', ['GET'] )
|
->addUnauthRoute( '*' => 'login', ['GET'] )
|
||||||
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
|
->addUnauthRoute( '*' => 'postLogin', ['POST'] )
|
||||||
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
|
->addAuthRoute( '*' => 'authenticatedRequest', ['GET'] )
|
||||||
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
|
->addAuthRoute( '*' => 'postAuthenticatedRequest', ['POST'] )
|
||||||
|
|
||||||
# psgi.js
|
# psgi.js
|
||||||
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||||
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
|
||||||
|
|
||||||
# portal.css
|
# portal.css
|
||||||
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||||
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
|
||||||
|
|
||||||
# lmerror
|
# lmerror
|
||||||
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||||
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
|
||||||
|
|
||||||
# Core REST API
|
# Core REST API
|
||||||
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
|
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
|
||||||
->addAuthRoute( ping => 'authenticated', ['GET'] )
|
->addAuthRoute( ping => 'authenticated', ['GET'] )
|
||||||
|
|
||||||
# Refresh session
|
# Refresh session
|
||||||
->addAuthRoute( refresh => 'refresh', ['GET'] )
|
->addAuthRoute( refresh => 'refresh', ['GET'] )
|
||||||
|
|
||||||
# Logout
|
# Logout
|
||||||
->addAuthRoute( logout => 'logout', ['GET'] );
|
->addAuthRoute( logout => 'logout', ['GET'] );
|
||||||
|
|
||||||
# Default routes must point to routines declared above
|
# Default routes must point to routines declared above
|
||||||
$self->defaultAuthRoute('');
|
$self->defaultAuthRoute('');
|
||||||
|
@ -151,7 +150,7 @@ sub reloadConf {
|
||||||
%{ $self->{conf} } = %{ $self->localConfig };
|
%{ $self->{conf} } = %{ $self->localConfig };
|
||||||
|
|
||||||
# Reinitialize arrays
|
# Reinitialize arrays
|
||||||
foreach ( qw(_macros _groups), @entryPoints) {
|
foreach ( qw(_macros _groups), @entryPoints ) {
|
||||||
$self->{$_} = [];
|
$self->{$_} = [];
|
||||||
}
|
}
|
||||||
$self->spRules( {} );
|
$self->spRules( {} );
|
||||||
|
@ -170,8 +169,8 @@ sub reloadConf {
|
||||||
$self->csp($csp);
|
$self->csp($csp);
|
||||||
|
|
||||||
# Initialize templateDir
|
# Initialize templateDir
|
||||||
$self->{templateDir} =
|
$self->{templateDir}
|
||||||
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
|
= $self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
|
||||||
unless ( -d $self->{templateDir} ) {
|
unless ( -d $self->{templateDir} ) {
|
||||||
$self->error("Template dir $self->{templateDir} doesn't exist");
|
$self->error("Template dir $self->{templateDir} doesn't exist");
|
||||||
return $self->fail;
|
return $self->fail;
|
||||||
|
@ -191,8 +190,8 @@ sub reloadConf {
|
||||||
# Initialize persistent session DB
|
# Initialize persistent session DB
|
||||||
unless ( $self->conf->{persistentStorage} ) {
|
unless ( $self->conf->{persistentStorage} ) {
|
||||||
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
|
$self->conf->{persistentStorage} = $self->conf->{globalStorage};
|
||||||
$self->conf->{persistentStorageOptions} =
|
$self->conf->{persistentStorageOptions}
|
||||||
$self->conf->{globalStorageOptions};
|
= $self->conf->{globalStorageOptions};
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize cookie domain
|
# Initialize cookie domain
|
||||||
|
@ -216,19 +215,19 @@ sub reloadConf {
|
||||||
return $self->fail;
|
return $self->fail;
|
||||||
}
|
}
|
||||||
$mod = $self->conf->{$type}
|
$mod = $self->conf->{$type}
|
||||||
unless ( $self->conf->{$type} eq 'Same' );
|
unless ( $self->conf->{$type} eq 'Same' );
|
||||||
my $module = '::' . ucfirst($type) . '::' . $mod;
|
my $module = '::' . ucfirst($type) . '::' . $mod;
|
||||||
$module =~ s/Authentication/Auth/;
|
$module =~ s/Authentication/Auth/;
|
||||||
|
|
||||||
# Launch and initialize module
|
# Launch and initialize module
|
||||||
return $self->fail
|
return $self->fail
|
||||||
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
|
unless ( $self->{"_$type"} = $self->loadPlugin($module) );
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load second-factor engine
|
# Load second-factor engine
|
||||||
return $self->fail
|
return $self->fail
|
||||||
unless $self->{_sfEngine} =
|
unless $self->{_sfEngine}
|
||||||
$self->loadPlugin( $self->conf->{'sfEngine'} );
|
= $self->loadPlugin( $self->conf->{'sfEngine'} );
|
||||||
|
|
||||||
# Initialize trusted domain regexp
|
# Initialize trusted domain regexp
|
||||||
if ( $self->conf->{trustedDomains}
|
if ( $self->conf->{trustedDomains}
|
||||||
|
@ -251,8 +250,8 @@ sub reloadConf {
|
||||||
# - $domainlabel.$td
|
# - $domainlabel.$td
|
||||||
# $domainlabel is build looking RFC2396
|
# $domainlabel is build looking RFC2396
|
||||||
# (see Regexp::Common::URI::RFC2396)
|
# (see Regexp::Common::URI::RFC2396)
|
||||||
$_ =~
|
$_
|
||||||
s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
|
=~ s/\*\\\./(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9]\\.)*/g;
|
||||||
$re->add("$_");
|
$re->add("$_");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,8 +262,8 @@ sub reloadConf {
|
||||||
$self->logger->debug("Vhost $vhost added in trusted domains");
|
$self->logger->debug("Vhost $vhost added in trusted domains");
|
||||||
$re->add( quotemeta($vhost) );
|
$re->add( quotemeta($vhost) );
|
||||||
$self->conf->{vhostOptions} ||= {};
|
$self->conf->{vhostOptions} ||= {};
|
||||||
if ( my $tmp =
|
if ( my $tmp
|
||||||
$self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
|
= $self->conf->{vhostOptions}->{$vhost}->{vhostAliases} )
|
||||||
{
|
{
|
||||||
foreach my $alias ( split /\s+/, $tmp ) {
|
foreach my $alias ( split /\s+/, $tmp ) {
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
|
@ -282,22 +281,22 @@ sub reloadConf {
|
||||||
$self->{"_$type"} = {};
|
$self->{"_$type"} = {};
|
||||||
if ( $self->conf->{$type} ) {
|
if ( $self->conf->{$type} ) {
|
||||||
for my $name ( sort keys %{ $self->conf->{$type} } ) {
|
for my $name ( sort keys %{ $self->conf->{$type} } ) {
|
||||||
my $sub =
|
my $sub = HANDLER->buildSub(
|
||||||
HANDLER->buildSub(
|
|
||||||
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
|
HANDLER->substitute( $self->conf->{$type}->{$name} ) );
|
||||||
if ($sub) {
|
if ($sub) {
|
||||||
$self->{"_$type"}->{$name} = $sub;
|
$self->{"_$type"}->{$name} = $sub;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->logger->error( "$type $name returns an error: "
|
$self->logger->error( "$type $name returns an error: "
|
||||||
. HANDLER->tsv->{jail}->error );
|
. HANDLER->tsv->{jail}->error );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->{_jsRedirect} =
|
$self->{_jsRedirect}
|
||||||
HANDLER->buildSub( HANDLER->substitute( $self->conf->{jsRedirect} ) )
|
= HANDLER->buildSub(
|
||||||
or $self->logger->error(
|
HANDLER->substitute( $self->conf->{jsRedirect} ) )
|
||||||
|
or $self->logger->error(
|
||||||
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
|
'jsRedirect returns an error: ' . HANDLER->tsv->{jail}->error );
|
||||||
|
|
||||||
# Load plugins
|
# Load plugins
|
||||||
|
@ -333,7 +332,7 @@ sub loadPlugin {
|
||||||
}
|
}
|
||||||
my $obj;
|
my $obj;
|
||||||
return 0
|
return 0
|
||||||
unless ( $obj = $self->loadModule("$plugin") );
|
unless ( $obj = $self->loadModule("$plugin") );
|
||||||
return $self->findEP( $plugin, $obj );
|
return $self->findEP( $plugin, $obj );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +362,7 @@ sub findEP {
|
||||||
if ( $obj->can('spRules') ) {
|
if ( $obj->can('spRules') ) {
|
||||||
foreach my $k ( keys %{ $obj->{spRules} } ) {
|
foreach my $k ( keys %{ $obj->{spRules} } ) {
|
||||||
$self->logger->info(
|
$self->logger->info(
|
||||||
"$k is defined more than one time, it can have some bad effect on Menu display"
|
"$k is defined more than one time, it can have some bad effect on Menu display"
|
||||||
) if ( $self->spRules->{$k} );
|
) if ( $self->spRules->{$k} );
|
||||||
$self->spRules->{$k} = $obj->{spRules}->{$k};
|
$self->spRules->{$k} = $obj->{spRules}->{$k};
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,12 +99,11 @@ sub controlUrl {
|
||||||
);
|
);
|
||||||
|
|
||||||
# XSS attack
|
# XSS attack
|
||||||
if (
|
if ($self->checkXSSAttack(
|
||||||
$self->checkXSSAttack(
|
|
||||||
$req->param('logout') ? 'HTTP Referer' : 'urldc',
|
$req->param('logout') ? 'HTTP Referer' : 'urldc',
|
||||||
$req->{urldc}
|
$req->{urldc}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
delete $req->{urldc};
|
delete $req->{urldc};
|
||||||
return PE_BADURL;
|
return PE_BADURL;
|
||||||
|
@ -113,9 +112,9 @@ sub controlUrl {
|
||||||
# Unprotected hosts
|
# Unprotected hosts
|
||||||
if ( $tmp and !$self->isTrustedUrl($tmp) ) {
|
if ( $tmp and !$self->isTrustedUrl($tmp) ) {
|
||||||
$self->userLogger->error(
|
$self->userLogger->error(
|
||||||
"URL contains a non protected host (param: "
|
"URL contains a non protected host (param: "
|
||||||
. ( $req->param('logout') ? 'HTTP Referer' : 'urldc' )
|
. ( $req->param('logout') ? 'HTTP Referer' : 'urldc' )
|
||||||
. " | value: $tmp)" );
|
. " | value: $tmp)" );
|
||||||
delete $req->{urldc};
|
delete $req->{urldc};
|
||||||
return PE_BADURL;
|
return PE_BADURL;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +165,8 @@ sub deleteSession {
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# Collect logout services and build hidden iFrames
|
# Collect logout services and build hidden iFrames
|
||||||
if ( $req->data->{logoutServices} and %{ $req->data->{logoutServices} } ) {
|
if ( $req->data->{logoutServices} and %{ $req->data->{logoutServices} } )
|
||||||
|
{
|
||||||
|
|
||||||
$self->logger->debug("Create iFrames to forward logout to services");
|
$self->logger->debug("Create iFrames to forward logout to services");
|
||||||
|
|
||||||
|
@ -178,24 +178,25 @@ sub deleteSession {
|
||||||
|
|
||||||
foreach ( keys %{ $req->data->{logoutServices} } ) {
|
foreach ( keys %{ $req->data->{logoutServices} } ) {
|
||||||
my $logoutServiceName = $_;
|
my $logoutServiceName = $_;
|
||||||
my $logoutServiceUrl =
|
my $logoutServiceUrl
|
||||||
$req->data->{logoutServices}->{$logoutServiceName};
|
= $req->data->{logoutServices}->{$logoutServiceName};
|
||||||
|
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Find logout service $logoutServiceName ($logoutServiceUrl)");
|
"Find logout service $logoutServiceName ($logoutServiceUrl)");
|
||||||
|
|
||||||
my $iframe =
|
my $iframe
|
||||||
qq'<iframe src="$logoutServiceUrl" alt="$logoutServiceName"'
|
= qq'<iframe src="$logoutServiceUrl" alt="$logoutServiceName"'
|
||||||
. ' marginwidth="0" marginheight="0" scrolling="no"'
|
. ' marginwidth="0" marginheight="0" scrolling="no"'
|
||||||
. ' class="hiddenFrame" width="0" height="0"'
|
. ' class="hiddenFrame" width="0" height="0"'
|
||||||
. ' frameborder="0"></iframe>';
|
. ' frameborder="0"></iframe>';
|
||||||
|
|
||||||
$req->info($iframe);
|
$req->info($iframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Redirect on logout page if no other target defined
|
# Redirect on logout page if no other target defined
|
||||||
if ( !$req->urldc and !$req->postUrl ) {
|
if ( !$req->urldc and !$req->postUrl ) {
|
||||||
$self->logger->debug('No other target defined, redirect on logout');
|
$self->logger->debug(
|
||||||
|
'No other target defined, redirect on logout');
|
||||||
$req->urldc( $req->script_name . "?logout=1" );
|
$req->urldc( $req->script_name . "?logout=1" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,10 +284,15 @@ sub getUser {
|
||||||
sub authenticate {
|
sub authenticate {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
my $ret = $req->authResult( $self->_authentication->authenticate($req) );
|
my $ret = $req->authResult( $self->_authentication->authenticate($req) );
|
||||||
|
$self->logger->debug(" -> authResult = $ret");
|
||||||
if ( $ret == PE_OK ) {
|
if ( $ret == PE_OK ) {
|
||||||
$req->{sessionInfo}->{_lastAuthnUTime} = time();
|
$req->{sessionInfo}->{_lastAuthnUTime} = time();
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
|
$self->setSessionInfo($req);
|
||||||
|
$self->setPersistentSessionInfo($req);
|
||||||
|
$self->setMacros($req);
|
||||||
|
$self->storeHistory($req);
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
@ -300,7 +306,8 @@ sub setAuthSessionInfo {
|
||||||
if ( $ret == PE_OK
|
if ( $ret == PE_OK
|
||||||
and not( defined $req->sessionInfo->{authenticationLevel} ) )
|
and not( defined $req->sessionInfo->{authenticationLevel} ) )
|
||||||
{
|
{
|
||||||
$self->logger->error('Authentication level is not set by auth module');
|
$self->logger->error(
|
||||||
|
'Authentication level is not set by auth module');
|
||||||
}
|
}
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
@ -320,15 +327,15 @@ sub setSessionInfo {
|
||||||
|
|
||||||
# Date and time
|
# Date and time
|
||||||
if ( $self->conf->{updateSession} ) {
|
if ( $self->conf->{updateSession} ) {
|
||||||
$req->{sessionInfo}->{_updateTime} =
|
$req->{sessionInfo}->{_updateTime}
|
||||||
strftime( "%Y%m%d%H%M%S", localtime() );
|
= strftime( "%Y%m%d%H%M%S", localtime() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$req->{sessionInfo}->{_utime} ||= time();
|
$req->{sessionInfo}->{_utime} ||= time();
|
||||||
$req->{sessionInfo}->{_startTime} =
|
$req->{sessionInfo}->{_startTime}
|
||||||
strftime( "%Y%m%d%H%M%S", localtime() );
|
= strftime( "%Y%m%d%H%M%S", localtime() );
|
||||||
$req->{sessionInfo}->{_lastSeen} = time()
|
$req->{sessionInfo}->{_lastSeen} = time()
|
||||||
if $self->conf->{timeoutActivity};
|
if $self->conf->{timeoutActivity};
|
||||||
}
|
}
|
||||||
|
|
||||||
# Store URL origin in session
|
# Store URL origin in session
|
||||||
|
@ -346,8 +353,8 @@ sub setSessionInfo {
|
||||||
sub setMacros {
|
sub setMacros {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
foreach ( sort keys %{ $self->_macros } ) {
|
foreach ( sort keys %{ $self->_macros } ) {
|
||||||
$req->{sessionInfo}->{$_} =
|
$req->{sessionInfo}->{$_}
|
||||||
$self->_macros->{$_}->( $req, $req->sessionInfo );
|
= $self->_macros->{$_}->( $req, $req->sessionInfo );
|
||||||
}
|
}
|
||||||
PE_OK;
|
PE_OK;
|
||||||
}
|
}
|
||||||
|
@ -387,16 +394,16 @@ sub setLocalGroups {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
foreach ( sort keys %{ $self->_groups } ) {
|
foreach ( sort keys %{ $self->_groups } ) {
|
||||||
if ( $self->_groups->{$_}->( $req, $req->sessionInfo ) ) {
|
if ( $self->_groups->{$_}->( $req, $req->sessionInfo ) ) {
|
||||||
$req->{sessionInfo}->{groups} .=
|
$req->{sessionInfo}->{groups}
|
||||||
$self->conf->{multiValuesSeparator} . $_;
|
.= $self->conf->{multiValuesSeparator} . $_;
|
||||||
$req->{sessionInfo}->{hGroups}->{$_}->{name} = $_;
|
$req->{sessionInfo}->{hGroups}->{$_}->{name} = $_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Clear values separator at the beginning
|
# Clear values separator at the beginning
|
||||||
if ( $req->{sessionInfo}->{groups} ) {
|
if ( $req->{sessionInfo}->{groups} ) {
|
||||||
$req->{sessionInfo}->{groups} =~
|
$req->{sessionInfo}->{groups}
|
||||||
s/^$self->{conf}->{multiValuesSeparator}//o;
|
=~ s/^$self->{conf}->{multiValuesSeparator}//o;
|
||||||
}
|
}
|
||||||
PE_OK;
|
PE_OK;
|
||||||
}
|
}
|
||||||
|
@ -420,8 +427,8 @@ sub store {
|
||||||
|
|
||||||
# Compute unsecure cookie value if needed
|
# Compute unsecure cookie value if needed
|
||||||
if ( $self->conf->{securedCookie} == 3 ) {
|
if ( $self->conf->{securedCookie} == 3 ) {
|
||||||
$req->{sessionInfo}->{_httpSession} =
|
$req->{sessionInfo}->{_httpSession}
|
||||||
$self->conf->{cipher}->encryptHex( $req->{id}, "http" );
|
= $self->conf->{cipher}->encryptHex( $req->{id}, "http" );
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fill session
|
# Fill session
|
||||||
|
|
|
@ -17,11 +17,11 @@ use strict;
|
||||||
use URI::Escape;
|
use URI::Escape;
|
||||||
|
|
||||||
# List constants
|
# List constants
|
||||||
sub authProcess { qw(extractFormInfo getUser authenticate) }
|
sub authProcess {qw(extractFormInfo getUser authenticate)}
|
||||||
|
|
||||||
sub sessionData {
|
sub sessionData {
|
||||||
qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo
|
qw(setAuthSessionInfo setSessionInfo setMacros setGroups setPersistentSessionInfo
|
||||||
setLocalGroups store secondFactor);
|
setLocalGroups store secondFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub validSession {
|
sub validSession {
|
||||||
|
@ -56,10 +56,11 @@ sub handler {
|
||||||
if ( $sp or %{ $req->pdata } ) {
|
if ( $sp or %{ $req->pdata } ) {
|
||||||
my %v = (
|
my %v = (
|
||||||
name => $self->conf->{cookieName} . 'pdata',
|
name => $self->conf->{cookieName} . 'pdata',
|
||||||
(
|
( %{ $req->pdata }
|
||||||
%{ $req->pdata }
|
|
||||||
? ( value => uri_escape( JSON::to_json( $req->pdata ) ) )
|
? ( value => uri_escape( JSON::to_json( $req->pdata ) ) )
|
||||||
: ( value => '', expires => 'Wed, 21 Oct 2015 00:00:00 GMT' )
|
: ( value => '',
|
||||||
|
expires => 'Wed, 21 Oct 2015 00:00:00 GMT'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
push @{ $res->[1] }, 'Set-Cookie', $self->cookie(%v);
|
push @{ $res->[1] }, 'Set-Cookie', $self->cookie(%v);
|
||||||
|
@ -91,8 +92,7 @@ sub login {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->do(
|
return $self->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ 'controlUrl', @{ $self->beforeAuth },
|
||||||
'controlUrl', @{ $self->beforeAuth },
|
|
||||||
$self->authProcess, @{ $self->betweenAuthAndData },
|
$self->authProcess, @{ $self->betweenAuthAndData },
|
||||||
$self->sessionData, @{ $self->afterData },
|
$self->sessionData, @{ $self->afterData },
|
||||||
$self->validSession, @{ $self->endAuth },
|
$self->validSession, @{ $self->endAuth },
|
||||||
|
@ -104,8 +104,7 @@ sub postLogin {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->do(
|
return $self->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ 'restoreArgs', 'controlUrl',
|
||||||
'restoreArgs', 'controlUrl',
|
|
||||||
@{ $self->beforeAuth }, $self->authProcess,
|
@{ $self->beforeAuth }, $self->authProcess,
|
||||||
@{ $self->betweenAuthAndData }, $self->sessionData,
|
@{ $self->betweenAuthAndData }, $self->sessionData,
|
||||||
@{ $self->afterData }, $self->validSession,
|
@{ $self->afterData }, $self->validSession,
|
||||||
|
@ -118,8 +117,7 @@ sub authenticatedRequest {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->do(
|
return $self->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ 'importHandlerData', 'controlUrl',
|
||||||
'importHandlerData', 'controlUrl',
|
|
||||||
'checkLogout', @{ $self->forAuthUser }
|
'checkLogout', @{ $self->forAuthUser }
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -129,8 +127,7 @@ sub postAuthenticatedRequest {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->do(
|
return $self->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ 'importHandlerData', 'restoreArgs',
|
||||||
'importHandlerData', 'restoreArgs',
|
|
||||||
'controlUrl', 'checkLogout',
|
'controlUrl', 'checkLogout',
|
||||||
@{ $self->forAuthUser }
|
@{ $self->forAuthUser }
|
||||||
]
|
]
|
||||||
|
@ -148,8 +145,7 @@ sub refresh {
|
||||||
delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ );
|
delete $data{$_} unless ( /^_/ or /^(?:startTime)$/ );
|
||||||
}
|
}
|
||||||
$req->steps(
|
$req->steps(
|
||||||
[
|
[ 'getUser',
|
||||||
'getUser',
|
|
||||||
@{ $self->betweenAuthAndData },
|
@{ $self->betweenAuthAndData },
|
||||||
'setAuthSessionInfo',
|
'setAuthSessionInfo',
|
||||||
'setSessionInfo',
|
'setSessionInfo',
|
||||||
|
@ -167,21 +163,21 @@ sub refresh {
|
||||||
if ($res) {
|
if ($res) {
|
||||||
$req->info(
|
$req->info(
|
||||||
$self->loadTemplate(
|
$self->loadTemplate(
|
||||||
'simpleInfo', params => { trspan => 'rightsReloadNeedsLogout' }
|
'simpleInfo',
|
||||||
|
params => { trspan => 'rightsReloadNeedsLogout' }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$req->urldc( $self->conf->{portal} );
|
$req->urldc( $self->conf->{portal} );
|
||||||
return $self->do( $req, [ sub { PE_INFO } ] );
|
return $self->do( $req, [ sub {PE_INFO} ] );
|
||||||
}
|
}
|
||||||
return $self->do( $req, [ sub { PE_OK } ] );
|
return $self->do( $req, [ sub {PE_OK} ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub logout {
|
sub logout {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->do(
|
return $self->do(
|
||||||
$req,
|
$req,
|
||||||
[
|
[ 'controlUrl', @{ $self->beforeLogout },
|
||||||
'controlUrl', @{ $self->beforeLogout },
|
|
||||||
'authLogout', 'deleteSession'
|
'authLogout', 'deleteSession'
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -198,9 +194,9 @@ sub do {
|
||||||
|
|
||||||
# Update status
|
# Update status
|
||||||
if ( my $p = $self->HANDLER->tsv->{statusPipe} ) {
|
if ( my $p = $self->HANDLER->tsv->{statusPipe} ) {
|
||||||
$p->print( ( $req->user ? $req->user : $req->address ) . ' => '
|
$p->print(( $req->user ? $req->user : $req->address ) . ' => '
|
||||||
. $req->uri
|
. $req->uri
|
||||||
. " $err\n" );
|
. " $err\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update history
|
# Update history
|
||||||
|
@ -212,8 +208,7 @@ sub do {
|
||||||
if ( ( $err > 0 and !$req->id ) or $err eq PE_SESSIONNOTGRANTED ) {
|
if ( ( $err > 0 and !$req->id ) or $err eq PE_SESSIONNOTGRANTED ) {
|
||||||
return [
|
return [
|
||||||
401,
|
401,
|
||||||
[
|
[ 'WWW-Authenticate' => "SSO " . $self->conf->{portal},
|
||||||
'WWW-Authenticate' => "SSO " . $self->conf->{portal},
|
|
||||||
'Access-Control-Allow-Origin' => '*'
|
'Access-Control-Allow-Origin' => '*'
|
||||||
],
|
],
|
||||||
[qq'{"result":0,"error":$err}']
|
[qq'{"result":0,"error":$err}']
|
||||||
|
@ -229,16 +224,14 @@ sub do {
|
||||||
else {
|
else {
|
||||||
return $self->sendJSONresponse(
|
return $self->sendJSONresponse(
|
||||||
$req,
|
$req,
|
||||||
{
|
{ result => 1,
|
||||||
result => 1,
|
|
||||||
code => $err
|
code => $err
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (
|
if ( $err
|
||||||
$err
|
|
||||||
and $err != PE_LOGOUT_OK
|
and $err != PE_LOGOUT_OK
|
||||||
and (
|
and (
|
||||||
$err != PE_REDIRECT
|
$err != PE_REDIRECT
|
||||||
|
@ -247,7 +240,7 @@ sub do {
|
||||||
and $req->data->{redirectFormMethod} eq 'post' )
|
and $req->data->{redirectFormMethod} eq 'post' )
|
||||||
or $req->info
|
or $req->info
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
my ( $tpl, $prms ) = $self->display($req);
|
my ( $tpl, $prms ) = $self->display($req);
|
||||||
$self->logger->debug("Calling sendHtml with template $tpl");
|
$self->logger->debug("Calling sendHtml with template $tpl");
|
||||||
|
@ -265,21 +258,20 @@ sub do {
|
||||||
|
|
||||||
sub getModule {
|
sub getModule {
|
||||||
my ( $self, $req, $type ) = @_;
|
my ( $self, $req, $type ) = @_;
|
||||||
if (
|
if (my $mod = {
|
||||||
my $mod = {
|
|
||||||
auth => '_authentication',
|
auth => '_authentication',
|
||||||
user => '_userDB',
|
user => '_userDB',
|
||||||
password => '_passwordDB'
|
password => '_passwordDB'
|
||||||
}->{$type}
|
}->{$type}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if ( my $sub = $self->$mod->can('name') ) {
|
if ( my $sub = $self->$mod->can('name') ) {
|
||||||
return $sub->( $self->$mod, $req, $type );
|
return $sub->( $self->$mod, $req, $type );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
my $s = ref( $self->$mod );
|
my $s = ref( $self->$mod );
|
||||||
$s =~
|
$s
|
||||||
s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//;
|
=~ s/^Lemonldap::NG::Portal::(?:(?:Issuer|UserDB|Auth|Password)::)?//;
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +288,7 @@ sub autoRedirect {
|
||||||
|
|
||||||
# Set redirection URL if needed
|
# Set redirection URL if needed
|
||||||
$req->{urldc} ||= $self->conf->{portal}
|
$req->{urldc} ||= $self->conf->{portal}
|
||||||
if ( $req->mustRedirect and not( $req->info ) );
|
if ( $req->mustRedirect and not( $req->info ) );
|
||||||
|
|
||||||
# Redirection should be made if urldc defined
|
# Redirection should be made if urldc defined
|
||||||
if ( $req->{urldc} ) {
|
if ( $req->{urldc} ) {
|
||||||
|
@ -306,8 +298,9 @@ sub autoRedirect {
|
||||||
$req->data->{redirectFormMethod} = "get";
|
$req->data->{redirectFormMethod} = "get";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return [ 302,
|
return [
|
||||||
[ Location => $req->{urldc}, @{ $req->respHeaders } ], [] ];
|
302, [ Location => $req->{urldc}, @{ $req->respHeaders } ], []
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my ( $tpl, $prms ) = $self->display($req);
|
my ( $tpl, $prms ) = $self->display($req);
|
||||||
|
@ -329,8 +322,7 @@ sub getApacheSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $as = Lemonldap::NG::Common::Session->new(
|
my $as = Lemonldap::NG::Common::Session->new(
|
||||||
{
|
{ storageModule => $self->conf->{globalStorage},
|
||||||
storageModule => $self->conf->{globalStorage},
|
|
||||||
storageModuleOptions => $self->conf->{globalStorageOptions},
|
storageModuleOptions => $self->conf->{globalStorageOptions},
|
||||||
cacheModule => $self->conf->{localSessionStorage},
|
cacheModule => $self->conf->{localSessionStorage},
|
||||||
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
|
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
|
||||||
|
@ -344,8 +336,7 @@ sub getApacheSession {
|
||||||
if ( my $err = $as->error ) {
|
if ( my $err = $as->error ) {
|
||||||
$self->lmLog(
|
$self->lmLog(
|
||||||
$err,
|
$err,
|
||||||
(
|
( $err =~ /(?:Object does not exist|Invalid session ID)/
|
||||||
$err =~ /(?:Object does not exist|Invalid session ID)/
|
|
||||||
? 'notice'
|
? 'notice'
|
||||||
: 'error'
|
: 'error'
|
||||||
)
|
)
|
||||||
|
@ -359,17 +350,16 @@ sub getApacheSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $now = time;
|
my $now = time;
|
||||||
if (
|
if ( $id
|
||||||
$id
|
|
||||||
and defined $as->data->{_utime}
|
and defined $as->data->{_utime}
|
||||||
and (
|
and (
|
||||||
$now - $as->data->{_utime} > $self->conf->{timeout}
|
$now - $as->data->{_utime} > $self->conf->{timeout}
|
||||||
or ( $self->conf->{timeoutActivity}
|
or ( $self->conf->{timeoutActivity}
|
||||||
and $as->data->{_lastSeen}
|
and $as->data->{_lastSeen}
|
||||||
and $now - $as->data->{_lastSeen} >
|
and $now - $as->data->{_lastSeen}
|
||||||
$self->conf->{timeoutActivity} )
|
> $self->conf->{timeoutActivity} )
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
$self->logger->debug("Session $args{kind} $id expired");
|
$self->logger->debug("Session $args{kind} $id expired");
|
||||||
return;
|
return;
|
||||||
|
@ -392,8 +382,7 @@ sub getPersistentSession {
|
||||||
$info->{_session_uid} = $uid;
|
$info->{_session_uid} = $uid;
|
||||||
|
|
||||||
my $ps = Lemonldap::NG::Common::Session->new(
|
my $ps = Lemonldap::NG::Common::Session->new(
|
||||||
{
|
{ storageModule => $self->conf->{persistentStorage},
|
||||||
storageModule => $self->conf->{persistentStorage},
|
|
||||||
storageModuleOptions => $self->conf->{persistentStorageOptions},
|
storageModuleOptions => $self->conf->{persistentStorageOptions},
|
||||||
id => $pid,
|
id => $pid,
|
||||||
force => 1,
|
force => 1,
|
||||||
|
@ -433,11 +422,12 @@ sub updatePersistentSession {
|
||||||
|
|
||||||
# Return if no infos to update
|
# Return if no infos to update
|
||||||
return () unless ( ref $infos eq 'HASH' and %$infos );
|
return () unless ( ref $infos eq 'HASH' and %$infos );
|
||||||
|
|
||||||
$uid ||= $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
$uid ||= $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
||||||
|| $req->userData->{ $self->conf->{whatToTrace} };
|
|| $req->userData->{ $self->conf->{whatToTrace} };
|
||||||
|
$self->logger->debug("Found 'whatToTrace' -> $uid");
|
||||||
unless ($uid) {
|
unless ($uid) {
|
||||||
$self->logger->debug('No uid found, skipping updatePersistentSession');
|
$self->logger->debug(
|
||||||
|
'No uid found, skipping updatePersistentSession');
|
||||||
return ();
|
return ();
|
||||||
}
|
}
|
||||||
$self->logger->debug("Update $uid persistent session");
|
$self->logger->debug("Update $uid persistent session");
|
||||||
|
@ -479,14 +469,14 @@ sub updateSession {
|
||||||
foreach ( keys %$infos ) {
|
foreach ( keys %$infos ) {
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Update sessionInfo $_ with " . $infos->{$_} );
|
"Update sessionInfo $_ with " . $infos->{$_} );
|
||||||
$req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_} =
|
$req->{sessionInfo}->{$_} = $self->HANDLER->data->{$_}
|
||||||
$infos->{$_};
|
= $infos->{$_};
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update session in global storage with _updateTime
|
# Update session in global storage with _updateTime
|
||||||
$infos->{_updateTime} = strftime( "%Y%m%d%H%M%S", localtime() );
|
$infos->{_updateTime} = strftime( "%Y%m%d%H%M%S", localtime() );
|
||||||
if ( my $apacheSession =
|
if ( my $apacheSession
|
||||||
$self->getApacheSession( $id, info => $infos ) )
|
= $self->getApacheSession( $id, info => $infos ) )
|
||||||
{
|
{
|
||||||
if ( $apacheSession->error ) {
|
if ( $apacheSession->error ) {
|
||||||
$self->logger->error("Cannot update session $id");
|
$self->logger->error("Cannot update session $id");
|
||||||
|
@ -569,8 +559,10 @@ sub isTrustedUrl {
|
||||||
|
|
||||||
sub stamp {
|
sub stamp {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $res =
|
my $res
|
||||||
$self->conf->{cipher} ? $self->conf->{cipher}->encrypt( time() ) : 1;
|
= $self->conf->{cipher}
|
||||||
|
? $self->conf->{cipher}->encrypt( time() )
|
||||||
|
: 1;
|
||||||
$res =~ s/\+/%2B/g;
|
$res =~ s/\+/%2B/g;
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
@ -702,7 +694,7 @@ sub cookie {
|
||||||
$h{path} ||= '/';
|
$h{path} ||= '/';
|
||||||
$h{HttpOnly} //= $self->conf->{httpOnly};
|
$h{HttpOnly} //= $self->conf->{httpOnly};
|
||||||
$h{max_age} //= $self->conf->{cookieExpiration}
|
$h{max_age} //= $self->conf->{cookieExpiration}
|
||||||
if ( $self->conf->{cookieExpiration} );
|
if ( $self->conf->{cookieExpiration} );
|
||||||
foreach (qw(domain path expires max_age HttpOnly)) {
|
foreach (qw(domain path expires max_age HttpOnly)) {
|
||||||
my $f = $_;
|
my $f = $_;
|
||||||
$f =~ s/_/-/g;
|
$f =~ s/_/-/g;
|
||||||
|
@ -725,8 +717,8 @@ sub sendHtml {
|
||||||
my ( $self, $req, $template, %args ) = @_;
|
my ( $self, $req, $template, %args ) = @_;
|
||||||
my $res = $self->SUPER::sendHtml( $req, $template, %args );
|
my $res = $self->SUPER::sendHtml( $req, $template, %args );
|
||||||
push @{ $res->[1] },
|
push @{ $res->[1] },
|
||||||
'X-XSS-Protection' => '1; mode=block',
|
'X-XSS-Protection' => '1; mode=block',
|
||||||
'X-Content-Type-Options' => 'nosniff';
|
'X-Content-Type-Options' => 'nosniff';
|
||||||
|
|
||||||
# Set authorized URL for POST
|
# Set authorized URL for POST
|
||||||
my $csp = $self->csp . "form-action 'self'";
|
my $csp = $self->csp . "form-action 'self'";
|
||||||
|
@ -740,13 +732,14 @@ sub sendHtml {
|
||||||
if ( defined $url ) {
|
if ( defined $url ) {
|
||||||
$self->logger->debug("Required Params URL : $url");
|
$self->logger->debug("Required Params URL : $url");
|
||||||
if ( $url =~ s#(https?://[^/]+).*#$1# ) {
|
if ( $url =~ s#(https?://[^/]+).*#$1# ) {
|
||||||
$self->logger->debug("Set CSP form-action with Params URL : $url");
|
$self->logger->debug(
|
||||||
|
"Set CSP form-action with Params URL : $url");
|
||||||
$csp .= " $url";
|
$csp .= " $url";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( defined $req->{cspFormAction} ) {
|
if ( defined $req->{cspFormAction} ) {
|
||||||
$self->logger->debug(
|
$self->logger->debug( "Set CSP form-action with request URL: "
|
||||||
"Set CSP form-action with request URL: " . $req->{cspFormAction} );
|
. $req->{cspFormAction} );
|
||||||
$csp .= " " . $req->{cspFormAction};
|
$csp .= " " . $req->{cspFormAction};
|
||||||
}
|
}
|
||||||
$csp .= ';';
|
$csp .= ';';
|
||||||
|
@ -761,7 +754,7 @@ sub sendHtml {
|
||||||
my @url;
|
my @url;
|
||||||
if ( $req->info ) {
|
if ( $req->info ) {
|
||||||
@url = map { s#https?://([^/]+).*#$1#; $_ }
|
@url = map { s#https?://([^/]+).*#$1#; $_ }
|
||||||
( $req->info =~ /<iframe.*?src="(.*?)"/sg );
|
( $req->info =~ /<iframe.*?src="(.*?)"/sg );
|
||||||
}
|
}
|
||||||
if (@url) {
|
if (@url) {
|
||||||
$csp .= join( ' ', 'child-src', @url ) . ';';
|
$csp .= join( ' ', 'child-src', @url ) . ';';
|
||||||
|
@ -775,17 +768,16 @@ sub sendHtml {
|
||||||
|
|
||||||
sub sendCss {
|
sub sendCss {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
my $s =
|
my $s
|
||||||
'html,body{background:url("'
|
= 'html,body{background:url("'
|
||||||
. $self->staticPrefix
|
. $self->staticPrefix
|
||||||
. '/common/backgrounds/'
|
. '/common/backgrounds/'
|
||||||
. $self->conf->{portalSkinBackground}
|
. $self->conf->{portalSkinBackground}
|
||||||
. '") no-repeat center fixed;'
|
. '") no-repeat center fixed;'
|
||||||
. 'background-size:cover;}';
|
. 'background-size:cover;}';
|
||||||
return [
|
return [
|
||||||
200,
|
200,
|
||||||
[
|
[ 'Content-Type' => 'text/css',
|
||||||
'Content-Type' => 'text/css',
|
|
||||||
'Content-Length' => length($s),
|
'Content-Length' => length($s),
|
||||||
'Cache-Control' => 'public,max-age=3600',
|
'Cache-Control' => 'public,max-age=3600',
|
||||||
],
|
],
|
||||||
|
@ -807,16 +799,16 @@ sub lmError {
|
||||||
|
|
||||||
# Error code
|
# Error code
|
||||||
$templateParams{"ERROR$_"} = ( $httpError == $_ ? 1 : 0 )
|
$templateParams{"ERROR$_"} = ( $httpError == $_ ? 1 : 0 )
|
||||||
foreach ( 403, 404, 500, 502, 503 );
|
foreach ( 403, 404, 500, 502, 503 );
|
||||||
return $self->sendHtml( $req, 'error', params => \%templateParams );
|
return $self->sendHtml( $req, 'error', params => \%templateParams );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rebuildCookies {
|
sub rebuildCookies {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
my @tmp;
|
my @tmp;
|
||||||
for ( my $i = 0 ; $i < @{ $req->{respHeaders} } ; $i += 2 ) {
|
for ( my $i = 0; $i < @{ $req->{respHeaders} }; $i += 2 ) {
|
||||||
push @tmp, $req->respHeaders->[0], $req->respHeaders->[1]
|
push @tmp, $req->respHeaders->[0], $req->respHeaders->[1]
|
||||||
unless ( $req->respHeaders->[0] eq 'Set-Cookie' );
|
unless ( $req->respHeaders->[0] eq 'Set-Cookie' );
|
||||||
}
|
}
|
||||||
$req->{respHeaders} = \@tmp;
|
$req->{respHeaders} = \@tmp;
|
||||||
$self->buildCookie($req);
|
$self->buildCookie($req);
|
||||||
|
@ -839,7 +831,7 @@ sub tplParams {
|
||||||
sub registerLogin {
|
sub registerLogin {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return
|
return
|
||||||
unless ( $self->conf->{loginHistoryEnabled}
|
unless ( $self->conf->{loginHistoryEnabled}
|
||||||
and defined $req->authResult );
|
and defined $req->authResult );
|
||||||
my $history = $req->sessionInfo->{_loginHistory} ||= {};
|
my $history = $req->sessionInfo->{_loginHistory} ||= {};
|
||||||
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
|
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
|
||||||
|
@ -849,14 +841,15 @@ sub registerLogin {
|
||||||
# Gather current login's parameters
|
# Gather current login's parameters
|
||||||
my $login = $self->_sumUpSession( $req->{sessionInfo}, 1 );
|
my $login = $self->_sumUpSession( $req->{sessionInfo}, 1 );
|
||||||
$login->{error} = $self->error( $req->authResult )
|
$login->{error} = $self->error( $req->authResult )
|
||||||
if ( $req->authResult );
|
if ( $req->authResult );
|
||||||
|
|
||||||
# Add current login into history
|
# Add current login into history
|
||||||
unshift @{ $history->{$type} }, $login;
|
unshift @{ $history->{$type} }, $login;
|
||||||
|
|
||||||
# Forget oldest logins
|
# Forget oldest logins
|
||||||
splice @{ $history->{$type} }, $self->conf->{ $type . "Number" }
|
splice @{ $history->{$type} }, $self->conf->{ $type . "Number" }
|
||||||
if ( scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
if (
|
||||||
|
scalar @{ $history->{$type} } > $self->conf->{ $type . "Number" } );
|
||||||
|
|
||||||
# Save into persistent session
|
# Save into persistent session
|
||||||
$self->updatePersistentSession( $req, { _loginHistory => $history, } );
|
$self->updatePersistentSession( $req, { _loginHistory => $history, } );
|
||||||
|
@ -867,12 +860,12 @@ sub registerLogin {
|
||||||
# @return hashref
|
# @return hashref
|
||||||
sub _sumUpSession {
|
sub _sumUpSession {
|
||||||
my ( $self, $session, $withoutUser ) = @_;
|
my ( $self, $session, $withoutUser ) = @_;
|
||||||
my $res =
|
my $res
|
||||||
$withoutUser
|
= $withoutUser
|
||||||
? {}
|
? {}
|
||||||
: { user => $session->{ $self->conf->{whatToTrace} } };
|
: { user => $session->{ $self->conf->{whatToTrace} } };
|
||||||
$res->{$_} = $session->{$_}
|
$res->{$_} = $session->{$_}
|
||||||
foreach ( "_utime", "ipAddr",
|
foreach ( "_utime", "ipAddr",
|
||||||
keys %{ $self->conf->{sessionDataToRemember} } );
|
keys %{ $self->conf->{sessionDataToRemember} } );
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
@ -881,11 +874,12 @@ sub _sumUpSession {
|
||||||
sub loadTemplate {
|
sub loadTemplate {
|
||||||
my ( $self, $name, %prm ) = @_;
|
my ( $self, $name, %prm ) = @_;
|
||||||
$name .= '.tpl';
|
$name .= '.tpl';
|
||||||
my $file =
|
my $file
|
||||||
$self->conf->{templateDir} . '/'
|
= $self->conf->{templateDir} . '/'
|
||||||
. $self->conf->{portalSkin} . '/'
|
. $self->conf->{portalSkin} . '/'
|
||||||
. $name;
|
. $name;
|
||||||
$file = $self->conf->{templateDir} . '/common/' . $name unless ( -e $file );
|
$file = $self->conf->{templateDir} . '/common/' . $name
|
||||||
|
unless ( -e $file );
|
||||||
unless ( -e $file ) {
|
unless ( -e $file ) {
|
||||||
die "Unable to find $name in $self->conf->{templateDir}";
|
die "Unable to find $name in $self->conf->{templateDir}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package Lemonldap::NG::Portal::Main::SecondFactor;
|
package Lemonldap::NG::Portal::Main::SecondFactor;
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
use strict;
|
use strict;
|
||||||
use Mouse;
|
use Mouse;
|
||||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||||
|
@ -88,7 +87,6 @@ sub _verify {
|
||||||
$req->sessionInfo($session);
|
$req->sessionInfo($session);
|
||||||
$req->id( delete $req->sessionInfo->{_2fRealSession} );
|
$req->id( delete $req->sessionInfo->{_2fRealSession} );
|
||||||
$req->urldc( delete $req->sessionInfo->{_2fUrldc} );
|
$req->urldc( delete $req->sessionInfo->{_2fUrldc} );
|
||||||
$self->logger->debug("req badcredentials -> " . Dumper($req));
|
|
||||||
$req->authResult(PE_BADCREDENTIALS);
|
$req->authResult(PE_BADCREDENTIALS);
|
||||||
return $self->p->do( $req,
|
return $self->p->do( $req,
|
||||||
[ $self->p->storeHistory($req), sub {$res} ] );
|
[ $self->p->storeHistory($req), sub {$res} ] );
|
||||||
|
@ -98,11 +96,13 @@ sub _verify {
|
||||||
$req->sessionInfo($session);
|
$req->sessionInfo($session);
|
||||||
$req->id( delete $req->sessionInfo->{_2fRealSession} );
|
$req->id( delete $req->sessionInfo->{_2fRealSession} );
|
||||||
$req->urldc( delete $req->sessionInfo->{_2fUrldc} );
|
$req->urldc( delete $req->sessionInfo->{_2fUrldc} );
|
||||||
|
$req->{sessionInfo}->{_utime} = delete $req->{sessionInfo}->{_2fUtime};
|
||||||
$self->p->rebuildCookies($req);
|
$self->p->rebuildCookies($req);
|
||||||
$req->mustRedirect(1);
|
$req->mustRedirect(1);
|
||||||
$self->userLogger->notice( $self->prefix
|
$self->userLogger->notice( $self->prefix
|
||||||
. '2F verification for '
|
. '2F verification for '
|
||||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||||
|
|
||||||
if ( my $l = $self->conf->{ $self->prefix . '2fAuthnLevel' } ) {
|
if ( my $l = $self->conf->{ $self->prefix . '2fAuthnLevel' } ) {
|
||||||
$self->p->updateSession( $req, { authenticationLevel => $l } );
|
$self->p->updateSession( $req, { authenticationLevel => $l } );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user