Better fix and improve unit test (#2337)
This commit is contained in:
parent
222a6472f4
commit
b573dbb789
|
@ -484,14 +484,20 @@ sub _displayRegister {
|
||||||
);
|
);
|
||||||
|
|
||||||
# Retrieve user all second factors
|
# Retrieve user all second factors
|
||||||
my $_2fDevices =
|
my $_2fDevices = [];
|
||||||
$req->userData->{_2fDevices}
|
if ( $self->allowedUpdateSfa($req) ) {
|
||||||
? eval {
|
$_2fDevices = $req->userData->{_2fDevices}
|
||||||
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } ); }
|
? eval {
|
||||||
: undef;
|
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
|
||||||
unless ($_2fDevices) {
|
}
|
||||||
$self->logger->debug("No 2F Device found");
|
: undef;
|
||||||
$_2fDevices = [];
|
unless ($_2fDevices) {
|
||||||
|
$self->logger->debug("No 2F Device found");
|
||||||
|
$_2fDevices = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->userLogger->warn("Do not diplay 2F Devices!");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Parse second factors to display delete button if allowed
|
# Parse second factors to display delete button if allowed
|
||||||
|
|
|
@ -36,6 +36,10 @@ sub run {
|
||||||
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
||||||
unless $user;
|
unless $user;
|
||||||
|
|
||||||
|
# Check if TOTP can be updated
|
||||||
|
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
||||||
|
unless $self->allowedUpdateSfa($req);
|
||||||
|
|
||||||
# Verification that user has a valid TOTP app
|
# Verification that user has a valid TOTP app
|
||||||
if ( $action eq 'verify' ) {
|
if ( $action eq 'verify' ) {
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ sub run {
|
||||||
}
|
}
|
||||||
$TOTPName =
|
$TOTPName =
|
||||||
substr( $TOTPName, 0, $self->conf->{max2FDevicesNameLength} );
|
substr( $TOTPName, 0, $self->conf->{max2FDevicesNameLength} );
|
||||||
$self->logger->debug("TOTP name : $TOTPName");
|
$self->logger->debug("TOTP name: $TOTPName");
|
||||||
|
|
||||||
unless ($code) {
|
unless ($code) {
|
||||||
$self->userLogger->info('TOTP registration: empty validation form');
|
$self->userLogger->info('TOTP registration: empty validation form');
|
||||||
|
@ -101,7 +105,7 @@ sub run {
|
||||||
my $secret = '';
|
my $secret = '';
|
||||||
|
|
||||||
# Reading existing 2FDevices
|
# Reading existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my $_2fDevices;
|
my $_2fDevices;
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -128,7 +132,7 @@ sub run {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Loading TOTP secret
|
# Loading TOTP secret
|
||||||
$self->logger->debug("Reading TOTP secret if exists ...");
|
$self->logger->debug("Reading TOTP secret if exists...");
|
||||||
$secret = $_->{_secret} foreach (@totp2f);
|
$secret = $_->{_secret} foreach (@totp2f);
|
||||||
return $self->p->sendError( $req, 'totpExistingKey', 200 )
|
return $self->p->sendError( $req, 'totpExistingKey', 200 )
|
||||||
if ( $token->{_totp2fSecret} eq $secret );
|
if ( $token->{_totp2fSecret} eq $secret );
|
||||||
|
@ -141,17 +145,13 @@ sub run {
|
||||||
|
|
||||||
# Check if user can register one more device
|
# Check if user can register one more device
|
||||||
my $maxSize = $self->conf->{max2FDevices};
|
my $maxSize = $self->conf->{max2FDevices};
|
||||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
$self->logger->debug("Registered 2F Device(s): $size / $maxSize");
|
||||||
if ( $size >= $maxSize ) {
|
if ( $size >= $maxSize ) {
|
||||||
$self->userLogger->warn("Max number of 2F devices is reached");
|
$self->userLogger->warn("Max number of 2F devices is reached");
|
||||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||||
400 );
|
400 );
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if TOTP can be stored
|
|
||||||
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
|
||||||
unless $self->allowedUpdateSfa($req);
|
|
||||||
|
|
||||||
# Store TOTP secret
|
# Store TOTP secret
|
||||||
push @keep,
|
push @keep,
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ sub run {
|
||||||
epoch => $epoch
|
epoch => $epoch
|
||||||
};
|
};
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Append 2F Device : { type => 'TOTP', name => $TOTPName }");
|
"Append 2F Device: { type => 'TOTP', name => $TOTPName }");
|
||||||
$self->p->updatePersistentSession( $req,
|
$self->p->updatePersistentSession( $req,
|
||||||
{ _2fDevices => to_json( \@keep ) } );
|
{ _2fDevices => to_json( \@keep ) } );
|
||||||
$self->userLogger->notice(
|
$self->userLogger->notice(
|
||||||
|
@ -179,7 +179,7 @@ sub run {
|
||||||
my $secret = '';
|
my $secret = '';
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my $_2fDevices;
|
my $_2fDevices;
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -207,7 +207,7 @@ sub run {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Loading TOTP secret
|
# Loading TOTP secret
|
||||||
$self->logger->debug("Reading TOTP secret if exists ...");
|
$self->logger->debug("Reading TOTP secret if exists...");
|
||||||
$secret = $_->{_secret} foreach (@totp2f);
|
$secret = $_->{_secret} foreach (@totp2f);
|
||||||
|
|
||||||
if ( ( $req->param('newkey') and $self->conf->{totp2fUserCanChangeKey} )
|
if ( ( $req->param('newkey') and $self->conf->{totp2fUserCanChangeKey} )
|
||||||
|
@ -263,15 +263,14 @@ sub run {
|
||||||
|
|
||||||
# Check if unregistration is allowed
|
# Check if unregistration is allowed
|
||||||
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
||||||
unless ( $self->conf->{totp2fUserCanRemoveKey}
|
unless $self->conf->{totp2fUserCanRemoveKey};
|
||||||
&& $self->allowedUpdateSfa($req) );
|
|
||||||
|
|
||||||
my $epoch = $req->param('epoch')
|
my $epoch = $req->param('epoch')
|
||||||
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
||||||
400 );
|
400 );
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Loading 2F Devices ...");
|
$self->logger->debug("Loading 2F Devices...");
|
||||||
my ( $_2fDevices, $TOTPName );
|
my ( $_2fDevices, $TOTPName );
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -294,7 +293,7 @@ sub run {
|
||||||
else { $_ }
|
else { $_ }
|
||||||
} @$_2fDevices;
|
} @$_2fDevices;
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Delete 2F Device : { type => 'TOTP', epoch => $epoch, name => $TOTPName }"
|
"Delete 2F Device: { type => 'TOTP', epoch => $epoch, name => $TOTPName }"
|
||||||
);
|
);
|
||||||
$self->p->updatePersistentSession( $req,
|
$self->p->updatePersistentSession( $req,
|
||||||
{ _2fDevices => to_json($_2fDevices) } );
|
{ _2fDevices => to_json($_2fDevices) } );
|
||||||
|
|
|
@ -33,10 +33,14 @@ sub run {
|
||||||
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
||||||
unless $user;
|
unless $user;
|
||||||
|
|
||||||
|
# Check if U2F key can be updated
|
||||||
|
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
||||||
|
unless $self->allowedUpdateSfa($req);
|
||||||
|
|
||||||
if ( $action eq 'register' ) {
|
if ( $action eq 'register' ) {
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my $_2fDevices;
|
my $_2fDevices;
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -56,7 +60,7 @@ sub run {
|
||||||
# Check if user can register one more 2F device
|
# Check if user can register one more 2F device
|
||||||
my $size = @$_2fDevices;
|
my $size = @$_2fDevices;
|
||||||
my $maxSize = $self->conf->{max2FDevices};
|
my $maxSize = $self->conf->{max2FDevices};
|
||||||
$self->logger->debug("Registered 2F Device(s) : $size / $maxSize");
|
$self->logger->debug("Registered 2F Device(s): $size / $maxSize");
|
||||||
if ( $size >= $maxSize ) {
|
if ( $size >= $maxSize ) {
|
||||||
$self->userLogger->warn("Max number of 2F devices is reached");
|
$self->userLogger->warn("Max number of 2F devices is reached");
|
||||||
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
return $self->p->sendError( $req, 'maxNumberof2FDevicesReached',
|
||||||
|
@ -96,7 +100,7 @@ sub run {
|
||||||
if ( $keyHandle and $userKey ) {
|
if ( $keyHandle and $userKey ) {
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my $_2fDevices;
|
my $_2fDevices;
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -117,10 +121,6 @@ sub run {
|
||||||
$_2fDevices = [];
|
$_2fDevices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if U2F key can be stored
|
|
||||||
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
|
||||||
unless $self->allowedUpdateSfa($req);
|
|
||||||
|
|
||||||
my $keyName = $req->param('keyName');
|
my $keyName = $req->param('keyName');
|
||||||
my $epoch = time();
|
my $epoch = time();
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ sub run {
|
||||||
}
|
}
|
||||||
$keyName =
|
$keyName =
|
||||||
substr( $keyName, 0, $self->conf->{max2FDevicesNameLength} );
|
substr( $keyName, 0, $self->conf->{max2FDevicesNameLength} );
|
||||||
$self->logger->debug("Key name : $keyName");
|
$self->logger->debug("Key name: $keyName");
|
||||||
|
|
||||||
push @{$_2fDevices},
|
push @{$_2fDevices},
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,7 @@ sub run {
|
||||||
epoch => $epoch
|
epoch => $epoch
|
||||||
};
|
};
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Append 2F Device : { type => 'U2F', name => $keyName }");
|
"Append 2F Device: { type => 'U2F', name => $keyName }");
|
||||||
$self->p->updatePersistentSession( $req,
|
$self->p->updatePersistentSession( $req,
|
||||||
{ _2fDevices => to_json($_2fDevices) } );
|
{ _2fDevices => to_json($_2fDevices) } );
|
||||||
$self->userLogger->notice(
|
$self->userLogger->notice(
|
||||||
|
@ -250,15 +250,14 @@ sub run {
|
||||||
|
|
||||||
# Check if unregistration is allowed
|
# Check if unregistration is allowed
|
||||||
return $self->p->sendError( $req, 'notAuthorized', 200 )
|
return $self->p->sendError( $req, 'notAuthorized', 200 )
|
||||||
unless ( $self->conf->{u2fUserCanRemoveKey}
|
unless $self->conf->{u2fUserCanRemoveKey};
|
||||||
&& $self->allowedUpdateSfa($req) );
|
|
||||||
|
|
||||||
my $epoch = $req->param('epoch')
|
my $epoch = $req->param('epoch')
|
||||||
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
||||||
400 );
|
400 );
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my ( $_2fDevices, $keyName );
|
my ( $_2fDevices, $keyName );
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -302,10 +301,10 @@ sub run {
|
||||||
|
|
||||||
sub loadUser {
|
sub loadUser {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
$self->logger->debug("Loading user U2F Devices ...");
|
$self->logger->debug("Loading user U2F Devices...");
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my ( $kh, $uk, $_2fDevices );
|
my ( $kh, $uk, $_2fDevices );
|
||||||
my @u2fs = ();
|
my @u2fs = ();
|
||||||
|
|
||||||
|
@ -325,7 +324,7 @@ sub loadUser {
|
||||||
|
|
||||||
# Reading existing U2F keys
|
# Reading existing U2F keys
|
||||||
foreach (@$_2fDevices) {
|
foreach (@$_2fDevices) {
|
||||||
$self->logger->debug("Looking for registered U2F key(s) ...");
|
$self->logger->debug("Looking for registered U2F key(s)...");
|
||||||
if ( $_->{type} eq 'U2F' ) {
|
if ( $_->{type} eq 'U2F' ) {
|
||||||
unless ( $_->{_userKey} and $_->{_keyHandle} ) {
|
unless ( $_->{_userKey} and $_->{_keyHandle} ) {
|
||||||
$self->logger->error(
|
$self->logger->error(
|
||||||
|
|
|
@ -34,6 +34,16 @@ sub run {
|
||||||
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
|
||||||
unless $user;
|
unless $user;
|
||||||
|
|
||||||
|
# Check if UBK key can be updated
|
||||||
|
return $self->p->sendHtml(
|
||||||
|
$req, 'error',
|
||||||
|
params => {
|
||||||
|
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||||
|
RAW_ERROR => 'notAuthorized',
|
||||||
|
AUTH_ERROR_TYPE => 'warning',
|
||||||
|
}
|
||||||
|
) unless $self->allowedUpdateSfa($req);
|
||||||
|
|
||||||
if ( $action eq 'register' ) {
|
if ( $action eq 'register' ) {
|
||||||
my $otp = $req->param('otp');
|
my $otp = $req->param('otp');
|
||||||
my $UBKName = $req->param('UBKName');
|
my $UBKName = $req->param('UBKName');
|
||||||
|
@ -46,7 +56,7 @@ sub run {
|
||||||
return $self->p->sendError( $req, 'badName', 200 );
|
return $self->p->sendError( $req, 'badName', 200 );
|
||||||
}
|
}
|
||||||
$UBKName = substr( $UBKName, 0, $self->conf->{max2FDevicesNameLength} );
|
$UBKName = substr( $UBKName, 0, $self->conf->{max2FDevicesNameLength} );
|
||||||
$self->logger->debug("Yubikey name : $UBKName");
|
$self->logger->debug("Yubikey name: $UBKName");
|
||||||
|
|
||||||
if ( $otp
|
if ( $otp
|
||||||
and length($otp) > $self->conf->{yubikey2fPublicIDSize} )
|
and length($otp) > $self->conf->{yubikey2fPublicIDSize} )
|
||||||
|
@ -54,7 +64,7 @@ sub run {
|
||||||
my $key = substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
|
my $key = substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my $_2fDevices;
|
my $_2fDevices;
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -73,20 +83,10 @@ sub run {
|
||||||
$_2fDevices = [];
|
$_2fDevices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if UBK key can be stored
|
|
||||||
return $self->p->sendHtml(
|
|
||||||
$req, 'error',
|
|
||||||
params => {
|
|
||||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
|
||||||
RAW_ERROR => 'notAuthorized',
|
|
||||||
AUTH_ERROR_TYPE => 'warning',
|
|
||||||
}
|
|
||||||
) unless $self->allowedUpdateSfa($req);
|
|
||||||
|
|
||||||
# Search if the Yubikey is already registered
|
# Search if the Yubikey is already registered
|
||||||
my $SameUBKFound = 0;
|
my $SameUBKFound = 0;
|
||||||
foreach (@$_2fDevices) {
|
foreach (@$_2fDevices) {
|
||||||
$self->logger->debug("Reading Yubikeys ...");
|
$self->logger->debug("Reading Yubikeys...");
|
||||||
if ( $_->{_yubikey} eq $key ) {
|
if ( $_->{_yubikey} eq $key ) {
|
||||||
$SameUBKFound = 1;
|
$SameUBKFound = 1;
|
||||||
last;
|
last;
|
||||||
|
@ -108,7 +108,7 @@ sub run {
|
||||||
# Check if user can register one more device
|
# Check if user can register one more device
|
||||||
my $size = @$_2fDevices;
|
my $size = @$_2fDevices;
|
||||||
my $maxSize = $self->conf->{max2FDevices};
|
my $maxSize = $self->conf->{max2FDevices};
|
||||||
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
$self->logger->debug("Registered 2F Device(s): $size / $maxSize");
|
||||||
if ( $size >= $maxSize ) {
|
if ( $size >= $maxSize ) {
|
||||||
$self->userLogger->warn("Max number of 2F devices is reached");
|
$self->userLogger->warn("Max number of 2F devices is reached");
|
||||||
return $self->p->sendHtml(
|
return $self->p->sendHtml(
|
||||||
|
@ -129,7 +129,7 @@ sub run {
|
||||||
epoch => $epoch
|
epoch => $epoch
|
||||||
};
|
};
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Append 2F Device : { type => 'UBK', name => $UBKName }");
|
"Append 2F Device: { type => 'UBK', name => $UBKName }");
|
||||||
$self->p->updatePersistentSession( $req,
|
$self->p->updatePersistentSession( $req,
|
||||||
{ _2fDevices => to_json($_2fDevices) } );
|
{ _2fDevices => to_json($_2fDevices) } );
|
||||||
$self->userLogger->notice(
|
$self->userLogger->notice(
|
||||||
|
@ -161,15 +161,14 @@ sub run {
|
||||||
|
|
||||||
# Check if unregistration is allowed
|
# Check if unregistration is allowed
|
||||||
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
return $self->p->sendError( $req, 'notAuthorized', 400 )
|
||||||
unless ( $self->conf->{yubikey2fUserCanRemoveKey}
|
unless $self->conf->{yubikey2fUserCanRemoveKey};
|
||||||
&& $self->allowedUpdateSfa($req) );
|
|
||||||
|
|
||||||
my $epoch = $req->param('epoch')
|
my $epoch = $req->param('epoch')
|
||||||
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
||||||
400 );
|
400 );
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices...");
|
||||||
my ( $_2fDevices, $UBKName );
|
my ( $_2fDevices, $UBKName );
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
|
@ -192,7 +191,7 @@ sub run {
|
||||||
else { $_ }
|
else { $_ }
|
||||||
} @$_2fDevices;
|
} @$_2fDevices;
|
||||||
$self->logger->debug(
|
$self->logger->debug(
|
||||||
"Delete 2F Device : { type => 'UBK', epoch => $epoch, name => $UBKName }"
|
"Delete 2F Device: { type => 'UBK', epoch => $epoch, name => $UBKName }"
|
||||||
);
|
);
|
||||||
$self->p->updatePersistentSession( $req,
|
$self->p->updatePersistentSession( $req,
|
||||||
{ _2fDevices => to_json($_2fDevices) } );
|
{ _2fDevices => to_json($_2fDevices) } );
|
||||||
|
|
|
@ -106,6 +106,7 @@ sub createNotification {
|
||||||
|
|
||||||
sub allowedUpdateSfa {
|
sub allowedUpdateSfa {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
|
my $user = $req->userData->{ $self->conf->{whatToTrace} };
|
||||||
my $res = 1;
|
my $res = 1;
|
||||||
if ( $self->conf->{impersonationRule} ) {
|
if ( $self->conf->{impersonationRule} ) {
|
||||||
$self->logger->debug('Impersonation plugin is enabled!');
|
$self->logger->debug('Impersonation plugin is enabled!');
|
||||||
|
@ -114,12 +115,12 @@ sub allowedUpdateSfa {
|
||||||
$req->userData->{_user} )
|
$req->userData->{_user} )
|
||||||
{
|
{
|
||||||
$self->userLogger->warn(
|
$self->userLogger->warn(
|
||||||
'Impersonation in progress! User is not allowed to update SFA.'
|
"Impersonation in progress! $user is not allowed to update SFA."
|
||||||
);
|
);
|
||||||
undef $res;
|
undef $res;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$self->userLogger->info('User is allowed to update SFA');
|
$self->userLogger->info("$user is allowed to update SFA");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $res;
|
return $res;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use IO::String;
|
||||||
BEGIN {
|
BEGIN {
|
||||||
require 't/test-lib.pm';
|
require 't/test-lib.pm';
|
||||||
}
|
}
|
||||||
my $maintests = 27;
|
my $maintests = 57;
|
||||||
|
|
||||||
SKIP: {
|
SKIP: {
|
||||||
require Lemonldap::NG::Common::TOTP;
|
require Lemonldap::NG::Common::TOTP;
|
||||||
|
@ -36,45 +36,29 @@ SKIP: {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
## Try to impersonate
|
# Try to authenticate
|
||||||
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
|
# -------------------
|
||||||
my ( $host, $url, $query ) =
|
|
||||||
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
|
|
||||||
|
|
||||||
$query =~ s/user=/user=dwho/;
|
|
||||||
$query =~ s/password=/password=dwho/;
|
|
||||||
$query =~ s/spoofId=/spoofId=rtyler/;
|
|
||||||
|
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/',
|
'/', IO::String->new('user=rtyler&password=rtyler'),
|
||||||
IO::String->new($query),
|
length => 27
|
||||||
length => length($query),
|
|
||||||
accept => 'text/html',
|
|
||||||
),
|
),
|
||||||
'Auth query'
|
'Auth query'
|
||||||
);
|
);
|
||||||
|
|
||||||
my $id = expectCookie($res);
|
my $id = expectCookie($res);
|
||||||
expectRedirection( $res, 'http://auth.example.com/' );
|
|
||||||
|
|
||||||
# Get Menu
|
|
||||||
# ------------------------
|
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_get(
|
$res = $client->_get(
|
||||||
'/',
|
'/',
|
||||||
cookie => "lemonldap=$id",
|
cookie => "lemonldap=$id",
|
||||||
accept => 'text/html'
|
accept => 'text/html'
|
||||||
),
|
),
|
||||||
'Get Menu',
|
'Get Menu'
|
||||||
);
|
);
|
||||||
expectOK($res);
|
|
||||||
ok(
|
|
||||||
$res->[2]->[0] =~
|
|
||||||
m%<span trspan="connectedAs">Connected as</span> rtyler%,
|
|
||||||
'Connected as dwho'
|
|
||||||
) or print STDERR Dumper( $res->[2]->[0] );
|
|
||||||
expectAuthenticatedAs( $res, 'rtyler' );
|
expectAuthenticatedAs( $res, 'rtyler' );
|
||||||
|
ok( $res->[2]->[0] =~ m%<span trspan="sfaManager">sfaManager</span>%,
|
||||||
|
'sfaManager link found' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
## Try to register a TOTP
|
## Try to register a TOTP
|
||||||
# TOTP form
|
# TOTP form
|
||||||
|
@ -105,8 +89,10 @@ SKIP: {
|
||||||
ok( not($@), 'Content is JSON' )
|
ok( not($@), 'Content is JSON' )
|
||||||
or explain( $res->[2]->[0], 'JSON content' );
|
or explain( $res->[2]->[0], 'JSON content' );
|
||||||
my ( $key, $token );
|
my ( $key, $token );
|
||||||
ok( $key = $res->{secret}, 'Found secret' );
|
ok( $key = $res->{secret}, 'Found secret' ) or print STDERR Dumper($res);
|
||||||
ok( $token = $res->{token}, 'Found token' );
|
ok( $token = $res->{token}, 'Found token' ) or print STDERR Dumper($res);
|
||||||
|
ok( $res->{user} eq 'rtyler', 'Found user' )
|
||||||
|
or print STDERR Dumper($res);
|
||||||
$key = Convert::Base32::decode_base32($key);
|
$key = Convert::Base32::decode_base32($key);
|
||||||
|
|
||||||
# Post code
|
# Post code
|
||||||
|
@ -114,7 +100,7 @@ SKIP: {
|
||||||
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
||||||
'Code' );
|
'Code' );
|
||||||
ok( $code =~ /^\d{6}$/, 'Code contains 6 digits' );
|
ok( $code =~ /^\d{6}$/, 'Code contains 6 digits' );
|
||||||
my $s = "code=$code&token=$token";
|
my $s = "code=$code&token=$token&TOTPName=myTOTP";
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/2fregisters/totp/verify',
|
'/2fregisters/totp/verify',
|
||||||
|
@ -127,11 +113,9 @@ SKIP: {
|
||||||
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
||||||
ok( not($@), 'Content is JSON' )
|
ok( not($@), 'Content is JSON' )
|
||||||
or explain( $res->[2]->[0], 'JSON content' );
|
or explain( $res->[2]->[0], 'JSON content' );
|
||||||
ok( $res->{error} eq 'notAuthorized', 'Not authorized to register a TOTP' )
|
ok( $res->{result} == 1, 'TOTP is registered' );
|
||||||
or explain( $res, 'Bad result' );
|
|
||||||
|
|
||||||
## Tru to register an U2F key
|
## Try to register an U2F key
|
||||||
# U2F form
|
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_get(
|
$res = $client->_get(
|
||||||
'/2fregisters/u',
|
'/2fregisters/u',
|
||||||
|
@ -200,11 +184,12 @@ JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
|
||||||
version => "U2F_V2"
|
version => "U2F_V2"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
( $host, $url, $query );
|
my ( $host, $url, $query );
|
||||||
$query = Lemonldap::NG::Common::FormEncode::build_urlencoded(
|
$query = Lemonldap::NG::Common::FormEncode::build_urlencoded(
|
||||||
registration => $registrationData,
|
registration => $registrationData,
|
||||||
challenge => $res->[2]->[0],
|
challenge => $res->[2]->[0],
|
||||||
);
|
);
|
||||||
|
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/2fregisters/u/registration', IO::String->new($query),
|
'/2fregisters/u/registration', IO::String->new($query),
|
||||||
|
@ -214,6 +199,181 @@ JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
|
||||||
),
|
),
|
||||||
'Push registration data'
|
'Push registration data'
|
||||||
);
|
);
|
||||||
|
expectOK($res);
|
||||||
|
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||||
|
ok( not($@), ' Content is JSON' )
|
||||||
|
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||||
|
ok( $data->{result} == 1, 'U2F key is registered' )
|
||||||
|
or explain( $data, '"result":1' );
|
||||||
|
|
||||||
|
$client->logout($id);
|
||||||
|
|
||||||
|
## Try to impersonate
|
||||||
|
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
|
||||||
|
( $host, $url, $query ) =
|
||||||
|
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
|
||||||
|
|
||||||
|
$query =~ s/user=/user=rtyler/;
|
||||||
|
$query =~ s/password=/password=rtyler/;
|
||||||
|
$query =~ s/spoofId=/spoofId=dwho/;
|
||||||
|
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new($query),
|
||||||
|
length => length($query),
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'Auth query'
|
||||||
|
);
|
||||||
|
( $host, $url, $query ) = expectForm( $res, undef, '/2fchoice', 'token' );
|
||||||
|
$query .= '&sf=totp';
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fchoice',
|
||||||
|
IO::String->new($query),
|
||||||
|
length => length($query),
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'Post TOTP choice'
|
||||||
|
);
|
||||||
|
( $host, $url, $query ) =
|
||||||
|
expectForm( $res, undef, '/totp2fcheck', 'token' );
|
||||||
|
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
||||||
|
'Code' );
|
||||||
|
$query =~ s/code=/code=$code/;
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/totp2fcheck', IO::String->new($query),
|
||||||
|
length => length($query),
|
||||||
|
),
|
||||||
|
'Post code'
|
||||||
|
);
|
||||||
|
$id = expectCookie($res);
|
||||||
|
|
||||||
|
# Get Menu
|
||||||
|
# ------------------------
|
||||||
|
ok(
|
||||||
|
$res = $client->_get(
|
||||||
|
'/',
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
accept => 'text/html'
|
||||||
|
),
|
||||||
|
'Get Menu',
|
||||||
|
);
|
||||||
|
expectOK($res);
|
||||||
|
expectAuthenticatedAs( $res, 'dwho' );
|
||||||
|
|
||||||
|
# 2fregisters
|
||||||
|
ok(
|
||||||
|
$res = $client->_get(
|
||||||
|
'/2fregisters',
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'Form 2fregisters'
|
||||||
|
);
|
||||||
|
ok( $res->[2]->[0] =~ /<span id="msg" trspan="choose2f">/,
|
||||||
|
'Found choose 2F' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
ok( $res->[2]->[0] !~ m%<span device=\'(TOTP|U2F)\' epoch=\'\d{10}\'%g,
|
||||||
|
'No 2F device found' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
|
## Try to register a TOTP
|
||||||
|
# TOTP form
|
||||||
|
ok(
|
||||||
|
$res = $client->_get(
|
||||||
|
'/2fregisters/totp',
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'Form registration'
|
||||||
|
);
|
||||||
|
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' )
|
||||||
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
|
ok(
|
||||||
|
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
|
||||||
|
'Found custom Main Logo'
|
||||||
|
) or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
|
# JS query
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fregisters/totp/getkey', IO::String->new(''),
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
length => 0,
|
||||||
|
),
|
||||||
|
'Get new key'
|
||||||
|
);
|
||||||
|
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
||||||
|
ok( not($@), 'Content is JSON' )
|
||||||
|
or explain( $res->[2]->[0], 'JSON content' );
|
||||||
|
ok( $res->{error} eq 'notAuthorized', 'Not authorized to register a TOTP' )
|
||||||
|
or explain( $res, 'Bad result' );
|
||||||
|
|
||||||
|
# Try to unregister TOTP
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fregisters/totp/delete',
|
||||||
|
IO::String->new("epoch=1234567890"),
|
||||||
|
length => 16,
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
),
|
||||||
|
'Delete TOTP query'
|
||||||
|
);
|
||||||
|
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||||
|
ok( not($@), ' Content is JSON' )
|
||||||
|
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||||
|
ok(
|
||||||
|
$data->{error} eq 'notAuthorized',
|
||||||
|
'Not authorized to unregister a TOTP'
|
||||||
|
) or explain( $data, 'Bad result' );
|
||||||
|
|
||||||
|
# Try to verify TOTP
|
||||||
|
$s = "code=123456&token=1234567890&TOTPName=myTOTP";
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fregisters/totp/verify',
|
||||||
|
IO::String->new($s),
|
||||||
|
length => length($s),
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
),
|
||||||
|
'Post code'
|
||||||
|
);
|
||||||
|
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||||
|
ok( not($@), ' Content is JSON' )
|
||||||
|
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||||
|
ok( $data->{error} eq 'notAuthorized', 'Not authorized to verify a TOTP' )
|
||||||
|
or explain( $data, 'Bad result' );
|
||||||
|
|
||||||
|
## Try to register an U2F key
|
||||||
|
# U2F form
|
||||||
|
ok(
|
||||||
|
$res = $client->_get(
|
||||||
|
'/2fregisters/u',
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'Form registration'
|
||||||
|
);
|
||||||
|
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
|
||||||
|
ok(
|
||||||
|
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
|
||||||
|
'Found custom Main Logo'
|
||||||
|
) or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
|
# Ajax registration request
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fregisters/u/register', IO::String->new(''),
|
||||||
|
accept => 'application/json',
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
length => 0,
|
||||||
|
),
|
||||||
|
'Get registration challenge'
|
||||||
|
);
|
||||||
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||||
ok( not($@), ' Content is JSON' )
|
ok( not($@), ' Content is JSON' )
|
||||||
or explain( [ $@, $res->[2] ], 'JSON content' );
|
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||||
|
@ -222,6 +382,24 @@ JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
|
||||||
'Not authorized to register an U2F key'
|
'Not authorized to register an U2F key'
|
||||||
) or explain( $data, 'Bad result' );
|
) or explain( $data, 'Bad result' );
|
||||||
|
|
||||||
|
# Try to unregister U2F key
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/2fregisters/u/delete',
|
||||||
|
IO::String->new("epoch=1234567890"),
|
||||||
|
length => 16,
|
||||||
|
cookie => "lemonldap=$id",
|
||||||
|
),
|
||||||
|
'Delete U2F key query'
|
||||||
|
);
|
||||||
|
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||||
|
ok( not($@), ' Content is JSON' )
|
||||||
|
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||||
|
ok(
|
||||||
|
$data->{error} eq 'notAuthorized',
|
||||||
|
'Not authorized to unregister an U2F key'
|
||||||
|
) or explain( $data, 'Bad result' );
|
||||||
|
|
||||||
$client->logout($id);
|
$client->logout($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ SKIP: {
|
||||||
'Get Menu'
|
'Get Menu'
|
||||||
);
|
);
|
||||||
ok( $res->[2]->[0] !~ m%<span trspan="sfaManager">sfaManager</span>%,
|
ok( $res->[2]->[0] !~ m%<span trspan="sfaManager">sfaManager</span>%,
|
||||||
'sfaManager link found' )
|
'sfaManager link not found' )
|
||||||
or print STDERR Dumper( $res->[2]->[0] );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
# TOTP form
|
# TOTP form
|
||||||
|
@ -113,7 +113,7 @@ SKIP: {
|
||||||
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
||||||
ok( not($@), 'Content is JSON' )
|
ok( not($@), 'Content is JSON' )
|
||||||
or explain( $res->[2]->[0], 'JSON content' );
|
or explain( $res->[2]->[0], 'JSON content' );
|
||||||
ok( $res->{result} == 1, 'Key is registered' );
|
ok( $res->{result} == 1, 'TOTP is registered' );
|
||||||
|
|
||||||
# Try to sign-in
|
# Try to sign-in
|
||||||
$client->logout($id);
|
$client->logout($id);
|
||||||
|
|
|
@ -46,7 +46,7 @@ SKIP: {
|
||||||
'Get Menu'
|
'Get Menu'
|
||||||
);
|
);
|
||||||
ok( $res->[2]->[0] !~ m%<span trspan="sfaManager">sfaManager</span>%,
|
ok( $res->[2]->[0] !~ m%<span trspan="sfaManager">sfaManager</span>%,
|
||||||
'sfaManager link found' )
|
'sfaManager link not found' )
|
||||||
or print STDERR Dumper( $res->[2]->[0] );
|
or print STDERR Dumper( $res->[2]->[0] );
|
||||||
|
|
||||||
# U2F form
|
# U2F form
|
||||||
|
|
Loading…
Reference in New Issue
Block a user