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

132 lines
3.6 KiB
Perl
Raw Normal View History

2018-03-17 13:34:42 +01:00
package Lemonldap::NG::Portal::2F::UTOTP;
use strict;
use Mouse;
2018-04-10 11:06:06 +02:00
use JSON qw(from_json to_json);
2018-03-17 13:34:42 +01:00
use Lemonldap::NG::Portal::Main::Constants qw(
);
2020-02-16 22:42:10 +01:00
our $VERSION = '2.0.8';
2018-03-17 13:34:42 +01:00
2018-03-18 09:15:23 +01:00
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
2018-03-17 13:34:42 +01:00
# INITIALIZATION
2018-03-18 09:15:23 +01:00
has prefix => ( is => 'ro', default => 'utotp' );
2020-10-31 23:43:08 +01:00
has logo => ( is => 'rw', default => 'utotp.png' );
has u2f => ( is => 'rw' );
has totp => ( is => 'rw' );
2018-03-17 13:34:42 +01:00
use Lemonldap::NG::Portal::Main::Constants qw(
2020-10-31 23:43:08 +01:00
PE_OK
PE_ERROR
2018-03-17 13:34:42 +01:00
PE_FORMEMPTY
PE_SENDRESPONSE
);
sub init {
my ($self) = @_;
2019-02-07 09:27:56 +01:00
if ( (
2018-03-18 22:32:42 +01:00
$self->conf->{totp2fSelfRegistration}
or $self->conf->{u2fSelfRegistration}
)
and $self->conf->{utotp2fActivation} eq '1'
)
2018-03-18 09:15:23 +01:00
{
$self->conf->{utotp2fActivation} =
2018-08-21 17:37:14 +02:00
'$_2fDevices && $_2fDevices =~ /"type":\s*"(?:TOTP|U2F)"/s';
2018-03-18 09:15:23 +01:00
}
2018-03-17 13:34:42 +01:00
foreach (qw(U2F TOTP)) {
2018-03-18 20:52:50 +01:00
2018-03-18 09:15:23 +01:00
# Arg "noRoute" is set for sub 2F modules to avoid enabling direct
# REST routes
unless ( $self->{ lc($_) } =
2018-03-18 20:52:50 +01:00
$self->p->loadModule( "::2F::$_", undef, noRoute => 1 )
and $self->{ lc($_) }->init )
2018-03-18 09:15:23 +01:00
{
2018-03-17 13:34:42 +01:00
$self->error("Unable to load ::2F::$_");
return 0;
}
}
2018-03-18 09:15:23 +01:00
return $self->SUPER::init();
2018-03-17 13:34:42 +01:00
}
# RUNNING METHODS
sub run {
my ( $self, $req, $token ) = @_;
$self->logger->debug('Generate TOTP form');
my $checkLogins = $req->param('checkLogins');
$self->logger->debug("UTOTP: checkLogins set") if $checkLogins;
my $stayconnected = $req->param('stayconnected');
$self->logger->debug("UTOTP: stayconnected set") if $stayconnected;
my %tplPrms = (
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
CHECKLOGINS => $checkLogins,
STAYCONNECTED => $stayconnected
);
2018-03-18 20:52:50 +01:00
if ( my $res = $self->u2f->loadUser( $req, $req->sessionInfo ) ) {
if ( $res > 0 ) {
$self->logger->debug('U2F key is registered');
# Get a challenge (from first key)
my $data = eval {
from_json(
$req->data->{crypter}->[0]->authenticationChallenge );
};
if ($@) {
$self->logger->error( Crypt::U2F::Server::u2fclib_getError() );
return PE_ERROR;
}
# Get registered keys
2020-02-16 22:42:10 +01:00
my @rk =
map {
{ keyHandle => $_->{keyHandle}, version => $data->{version} }
} @{ $req->data->{crypter} };
$self->ott->updateToken( $token, __ch => $data->{challenge} );
# Serialize data
2019-02-07 09:27:56 +01:00
$data = to_json( {
challenge => $data->{challenge},
appId => $data->{appId},
registeredKeys => \@rk,
}
);
$tplPrms{DATA} = $data;
2018-03-17 13:34:42 +01:00
}
2018-03-18 20:52:50 +01:00
}
# Prepare form
my $tmp = $self->p->sendHtml( $req, 'utotp2fcheck', params => \%tplPrms, );
$self->logger->debug("Prepare U2F-or-TOTP 2F verification");
2018-03-17 13:34:42 +01:00
$req->response($tmp);
return PE_SENDRESPONSE;
}
sub verify {
my ( $self, $req, $session ) = @_;
my ($r1);
if ( $req->param('signature') ) {
2018-03-18 09:15:23 +01:00
$self->logger->debug('UTOTP: U2F response detected');
2018-03-17 13:34:42 +01:00
my $r1 = $self->u2f->verify( $req, $session );
2020-02-17 23:22:31 +01:00
return PE_OK if ( $r1 == PE_OK );
2018-03-17 13:34:42 +01:00
}
if ( $req->param('code') ) {
2018-03-18 09:15:23 +01:00
$self->logger->debug('UTOTP: TOTP response detected');
2018-03-17 13:34:42 +01:00
return $self->totp->verify( $req, $session );
}
return ( $r1 ? $r1 : PE_FORMEMPTY );
}
1;