diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm
index 0d610cae474f81a39f66c306f150646c417beb45..5ae1cd7f10fef475eae60b46d27eaf301888fc7d 100644
--- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm
+++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Session/REST.pm
@@ -3,6 +3,7 @@ package Lemonldap::NG::Common::Session::REST;
use strict;
use Mouse;
use Lemonldap::NG::Common::Conf::Constants;
+use Lemonldap::NG::Common::Util qw/display2F/;
use JSON qw(from_json to_json);
our $VERSION = '2.0.15';
@@ -46,7 +47,7 @@ sub hAttr {
sub delSession {
my ( $self, $req ) = @_;
my $type = $req->params('sessionType');
- my $mod = $self->getMod($req)
+ my $mod = $self->getMod($req)
or return $self->sendError( $req, undef, 400 );
my $id = $req->params('sessionId')
or return $self->sendError( $req, 'sessionId is missing', 400 );
@@ -181,9 +182,16 @@ sub delete2F {
$self->logger->debug(
"Searching for 2F device to delete -> $type / $epoch ...");
if ( defined $element->{type} && defined $element->{epoch} ) {
- push @keep, $element
- unless ( ( $element->{type} eq $type )
- and ( $element->{epoch} eq $epoch ) );
+ if ( ( $element->{type} eq $type )
+ and ( $element->{epoch} eq $epoch ) )
+ {
+ my $uid = $session->data->{_session_uid};
+ $self->userLogger->notice(
+ "2FA deletion for $uid: " . display2F($element) );
+ }
+ else {
+ push @keep, $element;
+ }
}
else {
$self->logger->error("Corrupted _2fDevice");
diff --git a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm
index 569bea4a88736b0cabf502777b88a6e1336de4e2..56560684c2eafdd93e607a1ffa0ef8fe481f95f7 100644
--- a/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm
+++ b/lemonldap-ng-common/lib/Lemonldap/NG/Common/Util.pm
@@ -7,7 +7,7 @@ use MIME::Base64 qw(encode_base64);
our $VERSION = '2.0.14';
our @ISA = qw(Exporter);
-our @EXPORT_OK = qw(getSameSite getPSessionID genId2F);
+our @EXPORT_OK = qw(getSameSite getPSessionID genId2F display2F);
sub getPSessionID {
my ($uid) = @_;
@@ -20,6 +20,11 @@ sub genId2F {
"" );
}
+sub display2F {
+ my ($device) = @_;
+ return sprintf( "[%s]%s", $device->{type}, $device->{epoch} );
+}
+
sub getSameSite {
my ($conf) = @_;
diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm
index 918339607dfdf3c818360f86fc4088a759a3c3e7..04d5ca06878cb0659851a0669fb40553a2ffe6a4 100644
--- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm
+++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Api/2F.pm
@@ -10,7 +10,7 @@ use Mouse;
use JSON;
use Lemonldap::NG::Common::Session;
-use Lemonldap::NG::Common::Util qw/genId2F/;
+use Lemonldap::NG::Common::Util qw/genId2F display2F/;
sub getSecondFactors {
my ( $self, $req ) = @_;
@@ -230,7 +230,7 @@ sub _delete2FFromSessions {
push @keep, $element;
}
else {
- $removed->{ genId2F($element) } = "removed";
+ $removed->{ genId2F($element) } = $element;
}
}
if ( ( $total - scalar @keep ) > 0 ) {
@@ -291,6 +291,13 @@ sub _delete2F {
# merge results
$removed = { %$removed, %{ $res->{removed} } };
$count = scalar( keys %$removed );
+ if ($count) {
+ my $list_log = join(
+ ",", map { display2F( $removed->{$_} ) }
+ keys %$removed
+ );
+ $self->userLogger->notice("[API] 2FA deletion for $uid: $list_log");
+ }
return {
res => 'ok',
diff --git a/lemonldap-ng-manager/site/coffee/2ndfa.coffee b/lemonldap-ng-manager/site/coffee/2ndfa.coffee
index 9e2198d3dcf2182d86191425069a04a42478f81f..9d8e29d7ae00b3b277c3fb6f203de2a580c9992d 100644
--- a/lemonldap-ng-manager/site/coffee/2ndfa.coffee
+++ b/lemonldap-ng-manager/site/coffee/2ndfa.coffee
@@ -203,7 +203,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
array = JSON.parse(session[attr])
if array.length > 0
subres.push
- title: "type"
+ title: "2fid"
value: "name"
epoch: "date"
for sfDevice in array
@@ -215,7 +215,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
if key == 'epoch'
epoch = value
subres.push
- title: title
+ title: '[' + title + ']' + epoch
value: name
epoch: epoch
sfrow: true
diff --git a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js
index b023164be33e8ac020fa6730e1f2f8acf19b3d98..741ca0afa32e63f27cc383d3fb93e20cd70f3d0c 100644
--- a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js
+++ b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.js
@@ -207,7 +207,7 @@
array = JSON.parse(session[attr]);
if (array.length > 0) {
subres.push({
- title: "type",
+ title: "2fid",
value: "name",
epoch: "date"
});
@@ -226,7 +226,7 @@
}
}
subres.push({
- title: title,
+ title: '[' + title + ']' + epoch,
value: name,
epoch: epoch,
sfrow: true
diff --git a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js
index 857e6fcca2a8027191db01de77f4ea57cd6c9533..d031ff9ab6fc9d44eb5d303a0226a9920a3cc128 100644
--- a/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js
+++ b/lemonldap-ng-manager/site/htdocs/static/js/2ndfa.min.js
@@ -1 +1 @@
-!function(){var a={_whatToTrace:[function(e,t){return"groupBy=substr("+e+",1)"},function(e,t){return e+"="+t+"*"}]},d={_whatToTrace:function(e,t,n,r){return console.log("overSchema => level",n,"over",r),1===n&&t.length>r?e+"="+t+"*&groupBy=substr("+e+","+(n+r+1)+")":null}},_={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},i={home:[]};angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(v,t,e,n,p){var f,r,h;return v.links=links,v.menulinks=menulinks,v.staticPrefix=staticPrefix,v.scriptname=scriptname,v.formPrefix=formPrefix,v.availableLanguages=availableLanguages,v.waiting=!0,v.showM=!1,v.showT=!0,v.data=[],v.currentScope=null,v.currentSession=null,v.menu=i,v.searchString="",v.sfatypes={},v.translateP=t.translateP,v.translate=t.translate,v.translateTitle=function(e){return t.translateField(e,"title")},h="persistent",v.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(v.currentNode,v),v[e.action]();break;case"string":v[e.action]();break;default:console.log(typeof e.action)}return v.showM=!1},v.search2FA=function(e){return e&&(v.searchString=""),v.currentSession=null,v.data=[],v.updateTree2("",v.data,0,0)},v.delete2FA=function(e,t){for(var n=document.querySelectorAll(".data-"+t),r=0,a=n.length;r level",n,"over",r),1===n&&t.length>r?e+"="+t+"*&groupBy=substr("+e+","+(n+r+1)+")":null}},_={dateTitle:["_utime","_startTime","_updateTime"],sfaTitle:["_2fDevices"]},i={home:[]};angular.module("llngSessionsExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("SessionsExplorerCtrl",["$scope","$translator","$location","$q","$http",function(v,t,e,n,p){var f,r,h;return v.links=links,v.menulinks=menulinks,v.staticPrefix=staticPrefix,v.scriptname=scriptname,v.formPrefix=formPrefix,v.availableLanguages=availableLanguages,v.waiting=!0,v.showM=!1,v.showT=!0,v.data=[],v.currentScope=null,v.currentSession=null,v.menu=i,v.searchString="",v.sfatypes={},v.translateP=t.translateP,v.translate=t.translate,v.translateTitle=function(e){return t.translateField(e,"title")},h="persistent",v.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(v.currentNode,v),v[e.action]();break;case"string":v[e.action]();break;default:console.log(typeof e.action)}return v.showM=!1},v.search2FA=function(e){return e&&(v.searchString=""),v.currentSession=null,v.data=[],v.updateTree2("",v.data,0,0)},v.delete2FA=function(e,t){for(var n=document.querySelectorAll(".data-"+t),r=0,a=n.length;r
{{translate(node.title)}} |
{{node.title}} |
- {{translate(node.value)}} |
- {{node.value}} |
- {{translate(node.epoch)}} |
+ {{translate(node.value)}} |
+ {{node.value}} |
+ {{translate(node.epoch)}} |
{{localeDate(node.epoch)}} |
diff --git a/lemonldap-ng-manager/t/60-2ndfa.t b/lemonldap-ng-manager/t/60-2ndfa.t
index 0ecd12ad87a74a4ac5f19b1c2e5b4da06e61e912..e63b5840e2b9581d465210c6d31e1117c41f675e 100644
--- a/lemonldap-ng-manager/t/60-2ndfa.t
+++ b/lemonldap-ng-manager/t/60-2ndfa.t
@@ -32,6 +32,7 @@ sub newSession {
uid => $uid,
_utime => time,
_session_kind => $kind,
+ _session_uid => $uid,
_2fDevices => to_json($sfaDevices),
}
);
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm
index f6e7c32d61ae3c5c013b54594e27c94b5f579e92..b0f01c0c2c87e757caf8e3c2a3a57e10f5ec7120 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Password.pm
@@ -8,6 +8,7 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
use Lemonldap::NG::Common::Crypto;
+use Lemonldap::NG::Common::Util qw/display2F/;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_ERROR
@@ -82,6 +83,7 @@ sub run {
sub verify {
my ( $self, $req, $session ) = @_;
my ( $password, $password_device, $secret );
+ my $uid = $session->{ $self->conf->{whatToTrace} };
$self->logger->debug( $self->prefix . '2f: verification' );
@@ -103,13 +105,13 @@ sub verify {
}
if ( $password eq $self->crypto->decrypt($secret) ) {
- $self->userLogger->info( $self->prefix . '2f: correct' );
+
+ $self->userLogger->info( "User $uid authenticated with 2F device: "
+ . display2F($password_device) );
return PE_OK;
}
else {
- $self->userLogger->notice( $self->prefix
- . '2f: invalid for '
- . $session->{ $self->conf->{whatToTrace} } );
+ $self->userLogger->notice( $self->prefix . '2f: invalid for ' . $uid );
return PE_BADCREDENTIALS;
}
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm
index 224b369caab26f82a4f203c3bf6970885f958ee0..c5e899a3b8c08f652208c650e497811a35ce7927 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Generic.pm
@@ -131,8 +131,6 @@ sub run {
)
)
{
- $self->userLogger->notice( $self->prefix
- . "2f: registration of $genericname succeeds for $user" );
return [
200,
[
@@ -160,8 +158,6 @@ sub run {
if ( $self->del2fDevice( $req, $req->userData, $self->prefix, $epoch ) )
{
- $self->userLogger->notice(
- $self->prefix . "2f: device deleted for $user" );
return [
200,
[
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm
index 02b3173200adead46c507bf870cea6963ed83e83..068a652ec21f9ba0d61358364be3a6679a4cb31a 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Password.pm
@@ -110,8 +110,6 @@ sub run {
)
)
{
- $self->userLogger->notice( $self->prefix
- . "2f: registration of password succeeds for $user" );
return [
200,
[
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm
index b593162768907df84710cdf6f16a64aa21c6ae8b..e365359827cc170f4f2ea31b89664630bae022d3 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/TOTP.pm
@@ -119,8 +119,6 @@ sub run {
)
)
{
- $self->userLogger->notice( $self->prefix
- . "2f: registration of $TOTPName succeeds for $user" );
return [
200,
[
@@ -192,8 +190,6 @@ sub run {
or return $self->p->sendError( $req,
$self->prefix . '2f: "epoch" parameter is missing', 400 );
if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) {
- $self->userLogger->notice(
- $self->prefix . "2f: device deleted for $user" );
return [
200,
[
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm
index 15cf90ee7176f837e4215e5db8c4686d81bce682..1e5edc440e67f1e34401d19112c2afc915695823 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/U2F.pm
@@ -39,7 +39,7 @@ sub run {
# Read existing 2F device(s)
my @alldevices = $self->find2fDevicesByType( $req, $req->userData );
- my $challenge = $self->crypter->registrationChallenge;
+ my $challenge = $self->crypter->registrationChallenge;
$self->logger->debug(
$self->prefix . "2f: registration challenge ($challenge)" );
return [
@@ -92,9 +92,6 @@ sub run {
)
)
{
- $self->userLogger->notice( $self->prefix
- . "2f: registration of device $keyName succeeds for $user"
- );
return [
200,
[
@@ -210,8 +207,6 @@ sub run {
or return $self->p->sendError( $req,
$self->prefix . '2f: "epoch" parameter is missing', 400 );
if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) {
- $self->userLogger->notice(
- $self->prefix . "2f: device deleted for $user" );
return [
200,
[
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm
index 135fcd1c3ed702db454527b058581dd33ef4c948..6f4b7d901437ebed1ffd0cb67b8d0bf500912297 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/WebAuthn.pm
@@ -192,8 +192,6 @@ sub _registration {
)
)
{
- $self->userLogger->notice(
- $self->prefix . "2f: registration of $keyName succeeds for $user" );
return $self->p->sendJSONresponse( $req, { result => 1 } );
}
else {
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm
index 749aa36e1b22c401d7cb9621e7de47da91b9976f..1d037f71a9b2ce52d605f9cd198ff0bd66fc554b 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Register/Yubikey.pm
@@ -71,9 +71,6 @@ sub run {
)
)
{
- $self->userLogger->notice( $self->prefix
- . "2f: registration of device $UBKName succeeds for $user"
- );
return $self->p->sendHtml(
$req, 'error',
params => {
@@ -110,8 +107,6 @@ sub run {
or return $self->p->sendError( $req,
$self->prefix . '2f: "epoch" parameter is missing', 400 );
if ( $self->del2fDevice( $req, $req->userData, $self->type, $epoch ) ) {
- $self->userLogger->notice(
- $self->prefix . "2f: device deleted for $user" );
return [
200,
[
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm
index 3acca045cfb72664d551796a80f6537527cd4e90..c936b711167c8ec834a259a17f08a4ecb2d1003c 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/TOTP.pm
@@ -14,6 +14,7 @@ use Lemonldap::NG::Portal::Main::Constants qw(
PE_FORMEMPTY
PE_SENDRESPONSE
);
+use Lemonldap::NG::Common::Util qw/display2F/;
our $VERSION = '2.0.16';
@@ -65,6 +66,7 @@ sub run {
sub verify {
my ( $self, $req, $session ) = @_;
my ( $code, $secret, @totp2f );
+ my $uid = $session->{ $self->conf->{whatToTrace} };
$self->logger->debug( $self->prefix . '2f: verification' );
unless ( $code = $req->param('code') ) {
@@ -73,7 +75,13 @@ sub verify {
}
@totp2f = $self->find2fDevicesByType( $req, $session, $self->type );
- $secret = $_->{_secret} foreach @totp2f;
+ my $_2fDevice;
+ foreach (@totp2f) {
+ if ( $_->{_secret} ) {
+ $secret = $_->{_secret};
+ $_2fDevice = $_;
+ }
+ }
unless ($secret) {
$self->logger->debug( $self->prefix . '2f: no secret found' );
return PE_BADOTP;
@@ -88,13 +96,13 @@ sub verify {
return PE_ERROR if $r == -1;
if ($r) {
- $self->userLogger->info( $self->prefix . '2f: succeed' );
+ $self->userLogger->info( "User $uid authenticated with 2F device: "
+ . display2F($_2fDevice) );
return PE_OK;
}
else {
- $self->userLogger->notice( $self->prefix
- . '2f: invalid attempt for '
- . $session->{ $self->conf->{whatToTrace} } );
+ $self->userLogger->notice(
+ $self->prefix . '2f: invalid attempt for ' . $uid );
return PE_BADOTP;
}
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm
index b64fabe3a86de6442b8d1618025b120e1f044a9c..ee788d3fc3b7a695291a616eaa8faed2e0031cca 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/U2F.pm
@@ -8,6 +8,7 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
use MIME::Base64 qw(decode_base64url);
+use Lemonldap::NG::Common::Util qw/display2F/;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_ERROR
@@ -106,6 +107,7 @@ sub run {
sub verify {
my ( $self, $req, $session ) = @_;
my $crypter;
+ my $uid = $session->{ $self->conf->{whatToTrace} };
# Check U2F signature
if ( my $resp = $req->param('signature')
@@ -151,13 +153,17 @@ sub verify {
return $self->fail($req);
}
if ( $crypter->authenticationVerify($resp) ) {
- $self->userLogger->info( $self->prefix . '2f: signature verified' );
+
+ # Lookup 2f device from authenticator's keyHandle
+ my $device = $req->data->{u2fkhtodev}->{ $data->{keyHandle} };
+ $self->userLogger->info( "User $uid authenticated with 2F device: "
+ . display2F($device) );
return PE_OK;
}
else {
$self->userLogger->notice( $self->prefix
. '2f: unvalid signature for '
- . $session->{ $self->conf->{whatToTrace} } . ' ('
+ . $uid . ' ('
. Crypt::U2F::Server::u2fclib_getError()
. ')' );
$req->error(PE_U2FFAILED);
@@ -165,9 +171,8 @@ sub verify {
}
}
else {
- $self->userLogger->notice( $self->prefix
- . '2f: no valid response for user '
- . $session->{ $self->conf->{whatToTrace} } );
+ $self->userLogger->notice(
+ $self->prefix . '2f: no valid response for user ' . $uid );
$req->authResult(PE_U2FFAILED);
return $self->fail($req);
}
@@ -202,6 +207,7 @@ sub loadUser {
foreach (@u2fs) {
$kh = $_->{_keyHandle};
+ $req->data->{u2fkhtodev}->{$kh} = $_;
$uk = decode_base64url( $_->{_userKey} );
$self->logger->debug(
$self->prefix . "2f: append crypter with kh = $kh" );
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm
index c4b85e1c6851b575ea236a5b5fb46d135ec19837..56b487bb0bc3db06e68771795866ab7dae4aa909 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/Yubikey.pm
@@ -7,6 +7,7 @@ package Lemonldap::NG::Portal::2F::Yubikey;
use strict;
use Mouse;
use JSON qw(from_json to_json);
+use Lemonldap::NG::Common::Util qw/display2F/;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_ERROR
@@ -89,44 +90,63 @@ sub init {
return $self->SUPER::init();
}
-sub _findYubikey {
- my ( $self, $req, $session ) = @_;
+sub _findYubikeyForCode {
+ my ( $self, $req, $session, $code ) = @_;
my ( $yubikey, @ubk2f );
+ my $id_from_code = substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} );
+
# First, lookup from session attribute
if ( $self->conf->{yubikey2fFromSessionAttribute} ) {
my $attr = $self->conf->{yubikey2fFromSessionAttribute};
$yubikey = $session->{$attr};
+ if ($yubikey) {
+ if ( $yubikey eq $id_from_code ) {
+ return { _yubikey => $yubikey, };
+ }
+ else {
+ return;
+ }
+ }
}
# If we didn't find a key, lookup psession
if ( !$yubikey and $session->{_2fDevices} ) {
@ubk2f = $self->find2fDevicesByType( $req, $session, $self->type );
- if ( my $code = $req->param('code') ) {
- $yubikey = $_->{_yubikey} foreach grep {
- $_->{_yubikey} eq
- substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} )
- } @ubk2f;
- }
- else {
- $yubikey = $_->{_yubikey} foreach @ubk2f;
- }
+ my @results = grep { $_->{_yubikey} eq $id_from_code } @ubk2f;
+ return $results[0];
}
+ return;
+}
+
+sub _hasYubikey {
+ my ( $self, $req, $session ) = @_;
+ my ( $yubikey, @ubk2f );
- return $yubikey || '';
+ # Does the user have a session attribute
+ if ( $self->conf->{yubikey2fFromSessionAttribute} ) {
+ my $attr = $self->conf->{yubikey2fFromSessionAttribute};
+ $yubikey = $session->{$attr};
+ }
+
+ # If we didn't find a value, lookup registered devices
+ if ( !$yubikey and $session->{_2fDevices} ) {
+ @ubk2f = $self->find2fDevicesByType( $req, $session, $self->type );
+ $yubikey = $_->{_yubikey} foreach @ubk2f;
+ }
+
+ return ( $yubikey ? 1 : 0 );
}
sub run {
my ( $self, $req, $token ) = @_;
- my $yubikey = $self->_findYubikey( $req, $req->sessionInfo );
- unless ($yubikey) {
+ unless ( $self->_hasYubikey( $req, $req->sessionInfo ) ) {
$self->userLogger->warn( $self->prefix
. '2f: user '
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
. ' has no device registered' );
return PE_BADOTP;
}
- $self->logger->debug( $self->prefix . "2f: found $yubikey" );
# Prepare form
my ( $checkLogins, $stayConnected ) = $self->getFormParams($req);
@@ -157,12 +177,8 @@ sub verify {
}
# Verify OTP
- my $yubikey = $self->_findYubikey( $req, $session );
- if (
- index( $yubikey,
- substr( $code, 0, $self->conf->{yubikey2fPublicIDSize} ) ) == -1
- )
- {
+ my $yubikey = $self->_findYubikeyForCode( $req, $session, $code );
+ unless ($yubikey) {
$self->userLogger->warn( $self->prefix . '2f: device not registered' );
return PE_BADOTP;
}
@@ -173,6 +189,15 @@ sub verify {
$self->userLogger->warn( $self->prefix . '2f: verification failed' );
return PE_BADOTP;
}
+ my $uid = $session->{ $self->conf->{whatToTrace} };
+ if ( $yubikey->{epoch} ) {
+ $self->userLogger->info(
+ "User $uid authenticated with 2F device: " . display2F($yubikey) );
+ }
+ else {
+ $self->userLogger->info("User $uid authenticated with Yubikey");
+ }
+
return PE_OK;
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm
index b51219c9897e584978c141042d5d36e1dab77b1a..214dc6ef010a24ce2f66d08337de322a28ddb0e8 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/2fDevices.pm
@@ -22,6 +22,7 @@ of this module
use strict;
use Mouse::Role;
+use Lemonldap::NG::Common::Util qw/display2F/;
use JSON;
requires qw(p conf logger);
@@ -103,9 +104,10 @@ sub add2fDevice {
my $_2fDevices = $self->get2fDevices( $req, $info );
push @{$_2fDevices}, $device;
- $self->logger->debug(
- "Append 2F device: { type => $device->{type}, name => $device->{name} }"
- );
+
+ my $uid = $info->{ $self->conf->{whatToTrace} };
+ $self->userLogger->notice(
+ "User " . $uid . " registered 2F device: " . display2F($device) );
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
return 1;
@@ -156,8 +158,11 @@ sub del2fDevices {
@updated_2fDevices;
if ( @updated_2fDevices < $size_before ) {
$need_update = 1;
- $self->logger->debug(
- "Deleted 2F device: { type => $type, epoch => $epoch }");
+ my $uid = $info->{ $self->conf->{whatToTrace} };
+ $self->userLogger->notice( "User "
+ . $uid
+ . " deleted 2F device: "
+ . display2F($device_spec) );
}
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm
index b06837dda48378949bf891e72455379c2de75d4c..3a1050c2582a6fea7f220fceaf1b93ab2ceb4acf 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/Code2F.pm
@@ -10,6 +10,7 @@ package Lemonldap::NG::Portal::Lib::Code2F;
use strict;
use Mouse;
+use Lemonldap::NG::Common::Util qw/display2F/;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
@@ -131,9 +132,9 @@ sub _resend {
{
# Resend code and update last retry
- unless ($self->sendCode( $req, $session, $code )){
+ unless ( $self->sendCode( $req, $session, $code ) ) {
return $self->p->do( $req, [ sub { PE_ERROR } ] );
- };
+ }
$self->ott->updateToken( $token, __lastRetry => time );
}
else {
@@ -151,10 +152,18 @@ sub verify {
return PE_FORMEMPTY;
}
- $self->populateDestAttribute( $req, $req->sessionInfo );
+ $self->populateDestAttribute( $req, $session );
- return $self->verify_supplied_code( $req, $session, $usercode );
+ my $res = $self->verify_supplied_code( $req, $session, $usercode );
+ # If we authenticated the user using a 2F Device, log it
+ if ( $res == PE_OK && $req->data->{__2fDevice_registrable} ) {
+ my $uid = $session->{ $self->conf->{whatToTrace} };
+ my $device = $req->data->{__2fDevice_registrable};
+ $self->userLogger->info(
+ "User $uid authenticated with 2F device: " . display2F($device) );
+ }
+ return $res;
}
sub verify_supplied_code {
@@ -236,7 +245,9 @@ sub populateDestAttribute {
my @registered_devices =
$self->find2fDevicesByType( $req, $sessionInfo, $self->prefix );
if (@registered_devices) {
+
$sessionInfo->{destination} = $registered_devices[0]->{_generic};
+ $req->data->{__2fDevice_registrable} = $registered_devices[0];
}
}
}
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm
index 90e98a8dc40ee7ba9414a1ee02ee80f4195690ce..63db100020168b0e652d77faf9a5724814e09b5f 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Lib/WebAuthn.pm
@@ -8,6 +8,7 @@ use Digest::SHA qw(sha256);
use URI;
use Carp;
with 'Lemonldap::NG::Portal::Lib::2fDevices';
+use Lemonldap::NG::Common::Util qw/display2F/;
our $VERSION = '2.0.16';
@@ -186,9 +187,10 @@ sub validateAssertion {
if ( $validation_result->{success} == 1 ) {
my $new_signature_count = $validation_result->{signature_count};
- $self->userLogger->info(
- "Successfully verified signature with count "
- . "$new_signature_count for $user" );
+
+ $self->userLogger->info( "User $user authenticated with 2F device: "
+ . display2F($matching_credential)
+ . " (signature count: $new_signature_count) " );
# Update storedSignCount to be the value of authData.signCount
$self->update2fDevice( $req, $data, $self->type,
diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm
index 30fe3ec3e4d42ba6b088c74cb88577ca8c9004c8..d19ba6ded13f9921027bdbd9417185251ed83e24 100644
--- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm
+++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/SecondFactor.pm
@@ -141,7 +141,7 @@ sub _verify {
# Else restore session
$req->mustRedirect(1);
$self->userLogger->notice( $self->prefix
- . '2f verification for '
+ . '2f verification succeeded for '
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
if ( my $l = $self->authnLevel ) {
|