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;
|
2018-03-29 21:27:35 +02:00
|
|
|
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
|
|
|
|
);
|
|
|
|
|
|
|
|
our $VERSION = '2.0.0';
|
|
|
|
|
|
|
|
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|
|
|
|
|
|
|
# INITIALIZATION
|
|
|
|
|
|
|
|
has prefix => ( is => 'rw', default => 'yubikey' );
|
|
|
|
|
|
|
|
has template => ( is => 'ro', default => 'yubikey2fregister' );
|
|
|
|
|
2018-03-29 21:27:35 +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} };
|
|
|
|
unless ($user) {
|
|
|
|
return $self->p->sendError( $req,
|
|
|
|
'No ' . $self->conf->{whatToTrace} . ' found in user data', 500 );
|
|
|
|
}
|
|
|
|
|
2018-03-20 18:19:53 +01:00
|
|
|
if ( $action eq 'register' ) {
|
2018-03-29 23:11:46 +02:00
|
|
|
my $otp = $req->param('otp');
|
|
|
|
my $UBKName = $req->param('UBKName');
|
2018-04-03 21:17:15 +02:00
|
|
|
my $epoch = time();
|
|
|
|
|
2018-08-31 19:10:35 +02:00
|
|
|
# Set default name if empty, check characters and truncate name if too long
|
2018-04-03 21:17:15 +02:00
|
|
|
$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-03 21:17:15 +02:00
|
|
|
if ( $otp
|
2018-03-29 23:11:46 +02:00
|
|
|
and length($otp) > $self->conf->{yubikey2fPublicIDSize} )
|
|
|
|
{
|
2018-03-29 21:27:35 +02:00
|
|
|
my $key = substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
|
2018-04-11 23:14:58 +02:00
|
|
|
|
|
|
|
# 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 {
|
2018-03-29 23:11:46 +02:00
|
|
|
$self->logger->debug("No 2F Device found");
|
2018-04-07 13:22:06 +02:00
|
|
|
$_2fDevices = [];
|
2018-03-29 23:11:46 +02:00
|
|
|
}
|
2018-04-03 21:17:15 +02:00
|
|
|
|
2018-08-31 19:10:35 +02:00
|
|
|
# Search if the Yubikey is already registered
|
2018-04-03 21:17:15 +02:00
|
|
|
my $SameUBKFound = 0;
|
2018-04-07 13:22:06 +02:00
|
|
|
foreach (@$_2fDevices) {
|
2018-04-03 21:17:15 +02:00
|
|
|
$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!");
|
2018-04-04 23:05:27 +02:00
|
|
|
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',
|
|
|
|
}
|
|
|
|
);
|
2018-04-03 21:17:15 +02:00
|
|
|
}
|
|
|
|
|
2018-04-04 23:50:33 +02:00
|
|
|
# Check if user can register one more device
|
2018-04-07 13:22:06 +02:00
|
|
|
my $size = @$_2fDevices;
|
2018-04-05 19:08:29 +02:00
|
|
|
my $maxSize = $self->conf->{max2FDevices};
|
|
|
|
$self->logger->debug("Nbr 2FDevices = $size / $maxSize");
|
|
|
|
if ( $size >= $maxSize ) {
|
|
|
|
$self->userLogger->error(
|
|
|
|
"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},
|
2018-04-05 19:08:29 +02:00
|
|
|
RAW_ERROR => 'maxNumberof2FDevicesReached',
|
|
|
|
AUTH_ERROR_TYPE => 'warning',
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2018-04-04 23:50:33 +02:00
|
|
|
|
2018-04-07 13:22:06 +02:00
|
|
|
push @{$_2fDevices},
|
2018-03-29 23:11:46 +02:00
|
|
|
{
|
|
|
|
type => 'UBK',
|
|
|
|
name => $UBKName,
|
|
|
|
_yubikey => $key,
|
2018-04-03 21:17:15 +02:00
|
|
|
epoch => $epoch
|
2018-03-29 23:11:46 +02:00
|
|
|
};
|
2018-04-03 21:17:15 +02:00
|
|
|
|
2018-03-29 23:11:46 +02:00
|
|
|
$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-29 23:11:46 +02:00
|
|
|
|
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-29 23:11:46 +02:00
|
|
|
RAW_ERROR => 'yourKeyIsRegistered',
|
2018-03-26 10:15:37 +02:00
|
|
|
AUTH_ERROR_TYPE => 'positive',
|
|
|
|
}
|
|
|
|
);
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
else {
|
2018-03-29 23:11:46 +02:00
|
|
|
$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
|
|
|
}
|
|
|
|
}
|
2018-04-03 00:01:01 +02:00
|
|
|
|
2018-08-20 11:36:23 +02:00
|
|
|
elsif ( $action eq 'delete' ) {
|
2018-04-03 00:01:01 +02:00
|
|
|
|
2018-08-20 11:36:23 +02:00
|
|
|
# Check if unregistration is allowed
|
|
|
|
unless ( $self->conf->{yubikey2fUserCanRemoveKey} ) {
|
|
|
|
return $self->p->sendError( $req, 'notAuthorized', 400 );
|
|
|
|
}
|
|
|
|
|
2018-08-30 19:45:35 +02:00
|
|
|
my $epoch = $req->param('epoch')
|
|
|
|
or return $self->p->sendError( $req, '"epoch" parameter is missing',
|
|
|
|
400 );
|
|
|
|
|
2018-04-11 23:14:58 +02:00
|
|
|
# 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");
|
|
|
|
$_2fDevices = [];
|
|
|
|
}
|
2018-04-03 00:01:01 +02:00
|
|
|
|
2019-04-26 23:00:17 +02:00
|
|
|
# Delete Yubikey device
|
|
|
|
@$_2fDevices = grep { $_->{epoch} ne $epoch } @$_2fDevices;
|
2018-04-03 00:01:01 +02:00
|
|
|
$self->logger->debug(
|
|
|
|
"Delete 2F Device : { type => 'UBK', epoch => $epoch }");
|
|
|
|
$self->p->updatePersistentSession( $req,
|
2019-04-26 23:00:17 +02:00
|
|
|
{ _2fDevices => to_json( $_2fDevices ) } );
|
2018-04-03 00:01:01 +02:00
|
|
|
$self->userLogger->notice('Yubikey deletion succeed');
|
|
|
|
return [
|
|
|
|
200,
|
|
|
|
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
|
|
|
|
['{"result":1}']
|
|
|
|
];
|
|
|
|
}
|
2018-03-20 18:19:53 +01:00
|
|
|
else {
|
2018-08-20 11:36:23 +02:00
|
|
|
$self->logger->error("Unknown Yubikey action -> $action");
|
|
|
|
return $self->p->sendError( $req, 'unknownAction', 400 );
|
2018-03-20 18:19:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|