Update Ext2F to support resend code (#2762)
This commit is contained in:
parent
86e572db52
commit
31db698df0
|
@ -4,6 +4,8 @@ use strict;
|
|||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_OK
|
||||
PE_NOTOKEN
|
||||
PE_TOKENEXPIRED
|
||||
PE_ERROR
|
||||
PE_BADOTP
|
||||
PE_FORMEMPTY
|
||||
|
@ -17,10 +19,30 @@ extends 'Lemonldap::NG::Portal::Main::SecondFactor';
|
|||
# INITIALIZATION
|
||||
|
||||
has prefix => ( is => 'rw', default => 'ext' );
|
||||
|
||||
has resend_interval => (
|
||||
is => 'rw',
|
||||
lazy => 1,
|
||||
default => sub {
|
||||
$_[0]->{conf}->{ext2fResendInterval} || 0;
|
||||
}
|
||||
);
|
||||
|
||||
has random => ( is => 'rw' );
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->addUnauthRoute(
|
||||
$self->prefix . '2fresend' => '_resend',
|
||||
['POST']
|
||||
);
|
||||
|
||||
$self->addAuthRoute(
|
||||
$self->prefix . '2fresend' => '_resend',
|
||||
['POST']
|
||||
);
|
||||
|
||||
unless ( $self->conf->{ext2fCodeActivation} ) {
|
||||
foreach (qw(ext2FSendCommand ext2FValidateCommand)) {
|
||||
unless ( $self->conf->{$_} ) {
|
||||
|
@ -50,12 +72,6 @@ sub init {
|
|||
sub run {
|
||||
my ( $self, $req, $token ) = @_;
|
||||
|
||||
my $checkLogins = $req->param('checkLogins');
|
||||
$self->logger->debug("Ext2F: checkLogins set") if $checkLogins;
|
||||
|
||||
my $stayconnected = $req->param('stayconnected');
|
||||
$self->logger->debug("Ext2F: stayconnected set") if $stayconnected;
|
||||
|
||||
# Generate Code to send
|
||||
my $code;
|
||||
if ( $self->conf->{ext2fCodeActivation} ) {
|
||||
|
@ -64,18 +80,23 @@ sub run {
|
|||
$self->ott->updateToken( $token, __ext2fcode => $code );
|
||||
}
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug( 'Launching "Send" external 2F command -> '
|
||||
. $self->conf->{ext2FSendCommand} );
|
||||
if (
|
||||
my $c = $self->launch(
|
||||
$req->sessionInfo, $self->conf->{ext2FSendCommand}, $code
|
||||
)
|
||||
)
|
||||
{
|
||||
$self->logger->error("External send command failed (code $c)");
|
||||
return PE_ERROR;
|
||||
}
|
||||
return PE_ERROR unless $self->sendCode( $req, $req->sessionInfo, $code );
|
||||
|
||||
$self->logger->debug("Prepare external 2F verification");
|
||||
my $tmp = $self->sendCodeForm( $req, TOKEN => $token );
|
||||
|
||||
$req->response($tmp);
|
||||
return PE_SENDRESPONSE;
|
||||
}
|
||||
|
||||
sub sendCodeForm {
|
||||
my ( $self, $req, %params ) = @_;
|
||||
|
||||
my $checkLogins = $req->param('checkLogins');
|
||||
$self->logger->debug("Ext2F: checkLogins set") if $checkLogins;
|
||||
|
||||
my $stayconnected = $req->param('stayconnected');
|
||||
$self->logger->debug("Ext2F: stayconnected set") if $stayconnected;
|
||||
|
||||
# Prepare form
|
||||
my $tmp = $self->p->sendHtml(
|
||||
|
@ -84,20 +105,67 @@ sub run {
|
|||
params => {
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
SKIN => $self->p->getSkin($req),
|
||||
TOKEN => $token,
|
||||
PREFIX => $self->prefix,
|
||||
TARGET => '/'
|
||||
(
|
||||
$self->resend_interval
|
||||
? ( RESENDTARGET => '/'
|
||||
. $self->prefix
|
||||
. '2fresend?skin='
|
||||
. $self->p->getSkin($req) )
|
||||
: ()
|
||||
),
|
||||
TARGET => '/'
|
||||
. $self->prefix
|
||||
. '2fcheck?skin='
|
||||
. $self->p->getSkin($req),
|
||||
CHECKLOGINS => $checkLogins,
|
||||
STAYCONNECTED => $stayconnected
|
||||
STAYCONNECTED => $stayconnected,
|
||||
%params,
|
||||
}
|
||||
);
|
||||
$self->logger->debug("Prepare external 2F verification");
|
||||
}
|
||||
|
||||
$req->response($tmp);
|
||||
return PE_SENDRESPONSE;
|
||||
sub _resend {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
# Check token
|
||||
my $token;
|
||||
unless ( $token = $req->param('token') ) {
|
||||
$self->userLogger->error( $self->prefix . ' 2F access without token' );
|
||||
eval { $self->setSecurity($req) };
|
||||
$req->mustRedirect(1);
|
||||
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
|
||||
}
|
||||
|
||||
my $session;
|
||||
|
||||
# Do not invalidate the token while getting it
|
||||
unless ( $session = $self->ott->getToken( $token, 1 ) ) {
|
||||
$self->userLogger->info('Token expired');
|
||||
$self->setSecurity($req);
|
||||
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
|
||||
}
|
||||
|
||||
my $code = $session->{__ext2fcode};
|
||||
|
||||
my $legend;
|
||||
|
||||
# Timer
|
||||
my $lastretry =
|
||||
$session->{__lastRetry} || $session->{tokenSessionStartTimestamp} || time;
|
||||
if ( $self->resend_interval
|
||||
and ( $lastretry + $self->resend_interval < time ) )
|
||||
{
|
||||
|
||||
# Resend code and update last retry
|
||||
return PE_ERROR unless $self->sendCode( $req, $session, $code );
|
||||
$self->ott->updateToken( $token, __lastRetry => time );
|
||||
}
|
||||
else {
|
||||
$legend = "resendTooSoon";
|
||||
}
|
||||
|
||||
return $self->sendCodeForm( $req, TOKEN => $token, LEGEND => $legend );
|
||||
}
|
||||
|
||||
sub verify {
|
||||
|
@ -108,25 +176,34 @@ sub verify {
|
|||
return PE_FORMEMPTY;
|
||||
}
|
||||
|
||||
unless ( $self->conf->{ext2fCodeActivation} ) {
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug( 'Launching "Validate" external 2F command -> '
|
||||
. $self->conf->{ext2FValidateCommand} );
|
||||
$self->logger->debug(" code -> $usercode");
|
||||
if (
|
||||
my $c = $self->launch(
|
||||
$session, $self->conf->{ext2FValidateCommand}, $usercode
|
||||
)
|
||||
)
|
||||
{
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
$self->logger->error("External verify command failed (code $c)");
|
||||
return PE_BADOTP;
|
||||
}
|
||||
return PE_OK;
|
||||
if ( $self->conf->{ext2fCodeActivation} ) {
|
||||
return $self->verify_internal( $req, $session, $usercode );
|
||||
}
|
||||
else {
|
||||
return $self->verify_external( $req, $session, $usercode );
|
||||
}
|
||||
}
|
||||
|
||||
sub verify_external {
|
||||
my ( $self, $req, $session, $code ) = @_;
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug( 'Launching "Validate" external 2F command -> '
|
||||
. $self->conf->{ext2FValidateCommand} );
|
||||
$self->logger->debug(" code -> $code");
|
||||
if ( my $c =
|
||||
$self->launch( $session, $self->conf->{ext2FValidateCommand}, $code ) )
|
||||
{
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
$self->logger->error("External verify command failed (code $c)");
|
||||
return PE_BADOTP;
|
||||
}
|
||||
return PE_OK;
|
||||
}
|
||||
|
||||
sub verify_internal {
|
||||
my ( $self, $req, $session, $code ) = @_;
|
||||
|
||||
my $savedcode = $session->{__ext2fcode};
|
||||
unless ($savedcode) {
|
||||
|
@ -135,12 +212,30 @@ sub verify {
|
|||
return PE_ERROR;
|
||||
}
|
||||
|
||||
$self->logger->debug("Verifying Ext 2F code: $usercode VS $savedcode");
|
||||
return PE_OK if ( $usercode eq $savedcode );
|
||||
$self->logger->debug("Verifying Ext 2F code: $code VS $savedcode");
|
||||
if ( $code eq $savedcode ) {
|
||||
return PE_OK;
|
||||
}
|
||||
else {
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
return PE_BADOTP;
|
||||
}
|
||||
}
|
||||
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
return PE_BADOTP;
|
||||
sub sendCode {
|
||||
my ( $self, $req, $sessionInfo, $code ) = @_;
|
||||
|
||||
# Prepare command and launch it
|
||||
$self->logger->debug( 'Launching "Send" external 2F command -> '
|
||||
. $self->conf->{ext2FSendCommand} );
|
||||
if ( my $c =
|
||||
$self->launch( $sessionInfo, $self->conf->{ext2FSendCommand}, $code ) )
|
||||
{
|
||||
$self->logger->error("External send command failed (code $c)");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# system() is used with an array to avoid shell injection
|
||||
|
|
Loading…
Reference in New Issue