lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm

122 lines
3.1 KiB
Perl
Raw Normal View History

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_BADOTP
2018-02-19 14:23:33 +01:00
PE_ERROR
PE_FORMEMPTY
PE_OK
PE_SENDRESPONSE
);
2020-02-17 23:22:31 +01:00
our $VERSION = '2.0.8';
2018-02-19 14:23:33 +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
2018-08-18 18:29:45 +02:00
# If self registration is enabled and "activation" is just set to
2018-04-06 16:38:07 +02:00
# "enabled", replace the rule to detect if user has registered its key
if ( $self->conf->{totp2fSelfRegistration}
2018-02-21 06:28:42 +01:00
and $self->conf->{totp2fActivation} eq '1' )
{
2018-09-02 17:31:58 +02:00
$self->conf->{totp2fActivation} =
'$_2fDevices && $_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 ) = @_;
$self->logger->debug('Generate TOTP form');
2018-06-21 21:35:16 +02:00
my $checkLogins = $req->param('checkLogins');
$self->logger->debug("TOTP checkLogins set") if ($checkLogins);
2018-02-19 14:23:33 +01:00
# Prepare form
my $tmp = $self->p->sendHtml(
$req,
'totp2fcheck',
2018-02-19 14:23:33 +01:00
params => {
2018-10-12 19:41:13 +02:00
MAIN_LOGO => $self->conf->{portalMainLogo},
2019-06-28 15:56:57 +02:00
SKIN => $self->p->getSkin($req),
2018-06-21 21:35:16 +02:00
TOKEN => $token,
CHECKLOGINS => $checkLogins
2018-02-19 14:23:33 +01:00
}
);
$self->logger->debug("Prepare TOTP 2F verification");
$req->response($tmp);
return PE_SENDRESPONSE;
}
sub verify {
my ( $self, $req, $session ) = @_;
$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
2020-02-17 23:22:31 +01:00
my ( $secret, $_2fDevices );
2018-04-11 09:54:40 +02:00
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");
2020-02-17 23:22:31 +01:00
$self->logger->debug("Reading TOTP secret if exists...");
$secret = $_->{_secret}
foreach grep { $_->{type} eq 'TOTP' } @$_2fDevices;
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_BADOTP;
2018-04-10 16:15:14 +02:00
}
2018-02-19 14:23:33 +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
);
2020-02-17 23:22:31 +01:00
return PE_ERROR if ( $r == -1 );
if ($r) {
$self->userLogger->info('TOTP succeed');
return PE_OK;
2018-02-19 14:23:33 +01:00
}
else {
$self->userLogger->notice( 'Invalid TOTP for '
. $session->{ $self->conf->{whatToTrace} }
. ')' );
return PE_BADOTP;
2018-02-19 14:23:33 +01:00
}
}
1;