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

223 lines
7.4 KiB
Perl
Raw Normal View History

2018-03-30 21:24:34 +02:00
# Self Yubikey registration
2018-03-20 18:19:53 +01:00
package Lemonldap::NG::Portal::2F::Register::Yubikey;
use strict;
use Mouse;
use JSON qw(from_json to_json);
2018-03-20 18:19:53 +01:00
use Lemonldap::NG::Portal::Main::Constants qw(
PE_FORMEMPTY
PE_ERROR
);
2020-10-01 21:49:00 +02:00
our $VERSION = '2.0.10';
2018-03-20 18:19:53 +01:00
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
2020-10-01 21:49:00 +02:00
has prefix => ( is => 'rw', default => 'yubikey' );
2018-03-20 18:19:53 +01:00
has template => ( is => 'ro', default => 'yubikey2fregister' );
2020-10-16 23:12:52 +02:00
has welcome => ( is => 'ro', default => 'clickOnYubikey' );
2020-10-01 21:49:00 +02:00
has logo => ( is => 'rw', default => 'yubikey.png' );
2018-03-20 18:19:53 +01:00
sub init {
my ($self) = @_;
return 1;
}
# RUNNING METHODS
# Main method
sub run {
my ( $self, $req, $action ) = @_;
2019-04-09 21:48:59 +02:00
my $user = $req->userData->{ $self->conf->{whatToTrace} };
2020-02-16 22:42:10 +01:00
return $self->p->sendError( $req,
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 )
unless $user;
2019-04-09 21:48:59 +02:00
# Check if UBK key can be updated
2020-10-16 23:12:52 +02:00
my $msg = $self->canUpdateSfa( $req, $action );
return $self->p->sendHtml(
$req, 'error',
params => {
MAIN_LOGO => $self->conf->{portalMainLogo},
2020-10-12 19:21:20 +02:00
RAW_ERROR => 'notAuthorizedAuthLevel',
AUTH_ERROR_TYPE => 'warning',
}
2020-10-16 23:12:52 +02:00
) if $msg;
2018-03-20 18:19:53 +01:00
if ( $action eq 'register' ) {
my $otp = $req->param('otp');
my $UBKName = $req->param('UBKName');
my $epoch = time();
2018-08-31 19:10:35 +02:00
# Set default name if empty, check characters and truncate name if too long
$UBKName ||= $epoch;
2018-08-31 19:10:35 +02:00
unless ( $UBKName =~ /^[\w]+$/ ) {
$self->userLogger->error('Yubikey name with bad character(s)');
return $self->p->sendError( $req, 'badName', 200 );
}
2018-04-05 19:43:06 +02:00
$UBKName = substr( $UBKName, 0, $self->conf->{max2FDevicesNameLength} );
$self->logger->debug("Yubikey name: $UBKName");
2018-04-05 19:43:06 +02:00
if ( $otp
and length($otp) > $self->conf->{yubikey2fPublicIDSize} )
{
my $key = substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
# Read existing 2FDevices
$self->logger->debug("Looking for 2F Devices...");
my $_2fDevices;
if ( $req->userData->{_2fDevices} ) {
$_2fDevices = eval {
from_json( $req->userData->{_2fDevices},
{ allow_nonref => 1 } );
};
if ($@) {
$self->logger->error("Corrupted session (_2fDevices): $@");
return $self->p->sendError( $req, "Corrupted session",
500 );
}
}
else {
$self->logger->debug("No 2F Device found");
2018-04-07 13:22:06 +02:00
$_2fDevices = [];
}
2018-08-31 19:10:35 +02:00
# Search if the Yubikey is already registered
my $SameUBKFound = 0;
2018-04-07 13:22:06 +02:00
foreach (@$_2fDevices) {
$self->logger->debug("Reading Yubikeys...");
if ( $_->{_yubikey} eq $key ) {
$SameUBKFound = 1;
last;
}
}
if ($SameUBKFound) {
2018-08-31 19:10:35 +02:00
$self->userLogger->error("Yubikey already registered!");
return $self->p->sendHtml(
2018-04-04 23:16:36 +02:00
$req, 'error',
params => {
2018-10-12 19:41:13 +02:00
MAIN_LOGO => $self->conf->{portalMainLogo},
2018-04-04 23:16:36 +02:00
RAW_ERROR => 'yourKeyIsAlreadyRegistered',
AUTH_ERROR_TYPE => 'warning',
}
);
}
# Check if user can register one more device
2018-04-07 13:22:06 +02:00
my $size = @$_2fDevices;
my $maxSize = $self->conf->{max2FDevices};
$self->logger->debug("Registered 2F Device(s): $size / $maxSize");
if ( $size >= $maxSize ) {
$self->userLogger->warn("Max number of 2F devices is reached");
return $self->p->sendHtml(
$req, 'error',
params => {
2018-10-12 19:41:13 +02:00
MAIN_LOGO => $self->conf->{portalMainLogo},
RAW_ERROR => 'maxNumberof2FDevicesReached',
AUTH_ERROR_TYPE => 'warning',
}
);
}
2018-04-07 13:22:06 +02:00
push @{$_2fDevices},
{
type => 'UBK',
name => $UBKName,
_yubikey => $key,
epoch => $epoch
};
$self->logger->debug(
"Append 2F Device: { type => 'UBK', name => $UBKName }");
$self->p->updatePersistentSession( $req,
2018-04-07 13:22:06 +02:00
{ _2fDevices => to_json($_2fDevices) } );
2019-04-09 21:48:59 +02:00
$self->userLogger->notice(
2019-04-09 23:40:16 +02:00
"Yubikey registration of $UBKName succeeds for $user");
2018-03-26 10:15:37 +02:00
return $self->p->sendHtml(
$req, 'error',
params => {
2018-10-12 19:41:13 +02:00
MAIN_LOGO => $self->conf->{portalMainLogo},
RAW_ERROR => 'yourKeyIsRegistered',
2018-03-26 10:15:37 +02:00
AUTH_ERROR_TYPE => 'positive',
}
);
2018-03-20 18:19:53 +01:00
}
else {
$self->userLogger->error('Yubikey 2F: no code or name');
2018-03-26 10:15:37 +02:00
return $self->p->sendHtml(
$req, 'error',
params => {
2018-10-12 19:41:13 +02:00
MAIN_LOGO => $self->conf->{portalMainLogo},
2018-03-26 10:15:37 +02:00
AUTH_ERROR => PE_FORMEMPTY,
AUTH_ERROR_TYPE => 'positive',
}
);
2018-03-20 18:19:53 +01:00
}
}
elsif ( $action eq 'delete' ) {
# Check if unregistration is allowed
2020-02-16 22:42:10 +01:00
return $self->p->sendError( $req, 'notAuthorized', 400 )
unless $self->conf->{yubikey2fUserCanRemoveKey};
2018-08-30 19:45:35 +02:00
my $epoch = $req->param('epoch')
or return $self->p->sendError( $req, '"epoch" parameter is missing',
400 );
# Read existing 2FDevices
$self->logger->debug("Looking for 2F Devices...");
2020-10-01 21:49:00 +02:00
my ( $_2fDevices, $UBKName );
if ( $req->userData->{_2fDevices} ) {
$_2fDevices = eval {
from_json( $req->userData->{_2fDevices},
{ allow_nonref => 1 } );
};
if ($@) {
$self->logger->error("Corrupted session (_2fDevices): $@");
return $self->p->sendError( $req, "Corrupted session", 500 );
}
}
else {
$self->logger->debug("No 2F Device found");
$_2fDevices = [];
}
2019-04-26 23:00:17 +02:00
# Delete Yubikey device
2020-10-01 21:49:00 +02:00
@$_2fDevices = map {
if ( $_->{epoch} eq $epoch ) { $UBKName = $_->{name}; () }
else { $_ }
} @$_2fDevices;
2020-10-09 22:29:56 +02:00
if ($UBKName) {
$self->logger->debug(
"Delete 2F Device: { type => 'UBK', epoch => $epoch, name => $UBKName }"
2020-10-09 22:29:56 +02:00
);
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
$self->userLogger->notice(
"Yubikey $UBKName unregistration succeeds for $user");
return [
200,
[
'Content-Type' => 'application/json',
'Content-Length' => 12,
],
['{"result":1}']
];
}
else {
2020-10-12 15:16:55 +02:00
$self->p->sendError( $req, '2FDeviceNotFound', 400 );
2020-10-09 22:29:56 +02:00
}
}
2018-03-20 18:19:53 +01:00
else {
$self->logger->error("Unknown Yubikey action -> $action");
return $self->p->sendError( $req, 'unknownAction', 400 );
2018-03-20 18:19:53 +01:00
}
}
1;