2018-04-12 14:20:28 +02:00
|
|
|
# TOTP second factor authentication
|
|
|
|
#
|
|
|
|
# This plugin handle authentications to ask TOTP second factor for users that
|
|
|
|
# have registered their TOTP secret
|
2018-02-19 14:23:33 +01:00
|
|
|
package Lemonldap::NG::Portal::2F::TOTP;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Mouse;
|
2018-04-10 11:06:06 +02:00
|
|
|
use JSON qw(from_json to_json);
|
2018-02-19 14:23:33 +01:00
|
|
|
use Lemonldap::NG::Portal::Main::Constants qw(
|
|
|
|
PE_BADCREDENTIALS
|
|
|
|
PE_ERROR
|
|
|
|
PE_FORMEMPTY
|
|
|
|
PE_OK
|
|
|
|
PE_SENDRESPONSE
|
|
|
|
);
|
|
|
|
|
|
|
|
our $VERSION = '2.0.0';
|
|
|
|
|
2018-02-19 22:34:23 +01:00
|
|
|
extends 'Lemonldap::NG::Portal::Main::SecondFactor',
|
|
|
|
'Lemonldap::NG::Common::TOTP';
|
2018-02-19 14:23:33 +01:00
|
|
|
|
|
|
|
# INITIALIZATION
|
|
|
|
|
2018-02-19 22:15:06 +01:00
|
|
|
has prefix => ( is => 'ro', default => 'totp' );
|
|
|
|
|
2018-03-09 16:51:15 +01:00
|
|
|
has logo => ( is => 'rw', default => 'totp.png' );
|
|
|
|
|
2018-02-19 14:23:33 +01:00
|
|
|
sub init {
|
|
|
|
my ($self) = @_;
|
2018-02-21 06:28:42 +01:00
|
|
|
|
|
|
|
# If self registration is enabled and "activation" is simply set to
|
2018-04-06 16:38:07 +02:00
|
|
|
# "enabled", replace the rule to detect if user has registered its key
|
2018-03-08 20:36:32 +01:00
|
|
|
if ( $self->conf->{totp2fSelfRegistration}
|
2018-02-21 06:28:42 +01:00
|
|
|
and $self->conf->{totp2fActivation} eq '1' )
|
|
|
|
{
|
2018-04-08 22:09:16 +02:00
|
|
|
$self->conf->{totp2fActivation} = '$_2fDevices =~ /"type":\s*"TOTP"/s';
|
2018-02-19 14:23:33 +01:00
|
|
|
}
|
|
|
|
return $self->SUPER::init();
|
|
|
|
}
|
|
|
|
|
|
|
|
# RUNNING METHODS
|
|
|
|
|
|
|
|
sub run {
|
|
|
|
my ( $self, $req, $token ) = @_;
|
2018-02-20 22:58:20 +01:00
|
|
|
$self->logger->debug('Generate TOTP form');
|
2018-02-19 14:23:33 +01:00
|
|
|
|
|
|
|
# Prepare form
|
|
|
|
my $tmp = $self->p->sendHtml(
|
|
|
|
$req,
|
2018-02-20 22:58:20 +01:00
|
|
|
'totp2fcheck',
|
2018-02-19 14:23:33 +01:00
|
|
|
params => {
|
|
|
|
SKIN => $self->conf->{portalSkin},
|
|
|
|
TOKEN => $token
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$self->logger->debug("Prepare TOTP 2F verification");
|
|
|
|
|
|
|
|
$req->response($tmp);
|
|
|
|
return PE_SENDRESPONSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub verify {
|
|
|
|
my ( $self, $req, $session ) = @_;
|
2018-02-20 22:58:20 +01:00
|
|
|
$self->logger->debug('TOTP verification');
|
2018-02-19 14:23:33 +01:00
|
|
|
my $code;
|
|
|
|
unless ( $code = $req->param('code') ) {
|
|
|
|
$self->userLogger->error('TOTP 2F: no code');
|
|
|
|
return PE_FORMEMPTY;
|
|
|
|
}
|
2018-04-10 11:06:06 +02:00
|
|
|
|
2018-04-11 09:54:40 +02:00
|
|
|
my $secret = '';
|
|
|
|
my $_2fDevices;
|
|
|
|
if ( $session->{_2fDevices} ) {
|
2018-04-10 16:15:14 +02:00
|
|
|
$self->logger->debug("Loading 2F Devices ...");
|
2018-04-10 11:06:06 +02:00
|
|
|
|
2018-04-10 16:15:14 +02:00
|
|
|
# Read existing 2FDevices
|
2018-04-11 09:54:40 +02:00
|
|
|
$_2fDevices =
|
|
|
|
eval { from_json( $session->{_2fDevices}, { allow_nonref => 1 } ); };
|
|
|
|
if ($@) {
|
|
|
|
$self->logger->error("Bad encoding in _2fDevices: $@");
|
|
|
|
return PE_ERROR;
|
|
|
|
}
|
|
|
|
$self->logger->debug("2F Device(s) found");
|
|
|
|
foreach (@$_2fDevices) {
|
|
|
|
$self->logger->debug("Reading TOTP secret if exists ...");
|
|
|
|
if ( $_->{type} eq 'TOTP' ) {
|
|
|
|
$secret = $_->{_secret};
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
2018-04-10 16:15:14 +02:00
|
|
|
}
|
2018-04-10 11:06:06 +02:00
|
|
|
|
2018-04-11 09:54:40 +02:00
|
|
|
unless ($secret) {
|
|
|
|
$self->logger->debug("No TOTP secret found");
|
|
|
|
return PE_BADCREDENTIALS;
|
2018-04-10 16:15:14 +02:00
|
|
|
}
|
2018-02-19 14:23:33 +01:00
|
|
|
|
2018-02-19 22:34:23 +01:00
|
|
|
my $r = $self->verifyCode(
|
|
|
|
$self->conf->{totp2fInterval},
|
|
|
|
$self->conf->{totp2fRange},
|
2018-02-21 22:07:12 +01:00
|
|
|
$self->conf->{totp2fDigits},
|
2018-04-10 11:06:06 +02:00
|
|
|
$secret, $code
|
2018-02-19 22:34:23 +01:00
|
|
|
);
|
|
|
|
if ( $r == -1 ) { return PE_ERROR; }
|
|
|
|
elsif ($r) {
|
|
|
|
$self->userLogger->info('TOTP succeed');
|
|
|
|
return PE_OK;
|
2018-02-19 14:23:33 +01:00
|
|
|
}
|
2018-02-19 22:34:23 +01:00
|
|
|
else {
|
|
|
|
$self->userLogger->notice( 'Invalid TOTP for '
|
|
|
|
. $session->{ $self->conf->{whatToTrace} }
|
|
|
|
. ')' );
|
|
|
|
return PE_BADCREDENTIALS;
|
2018-02-19 14:23:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|