Merge branch 'portal-2f-improvement' into 'master'
Portal 2f improvement See merge request lemonldap-ng/lemonldap-ng!26
This commit is contained in:
commit
0d72ad10df
|
@ -3,6 +3,7 @@ package Lemonldap::NG::Common::Session::REST;
|
||||||
use strict;
|
use strict;
|
||||||
use Mouse;
|
use Mouse;
|
||||||
use Lemonldap::NG::Common::Conf::Constants;
|
use Lemonldap::NG::Common::Conf::Constants;
|
||||||
|
use JSON qw(from_json to_json);
|
||||||
|
|
||||||
our $VERSION = '2.0.0';
|
our $VERSION = '2.0.0';
|
||||||
|
|
||||||
|
@ -44,15 +45,17 @@ sub delSession {
|
||||||
my $id = $req->params('sessionId')
|
my $id = $req->params('sessionId')
|
||||||
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
||||||
my $session = $self->getApacheSession( $mod, $id );
|
my $session = $self->getApacheSession( $mod, $id );
|
||||||
|
$self->logger->debug("Delete session : $id");
|
||||||
$session->remove;
|
$session->remove;
|
||||||
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
||||||
|
|
||||||
if ( $session->error ) {
|
if ( $session->error ) {
|
||||||
return $self->sendError( $req, $session->error, 200 );
|
return $self->sendError( $req, $session->error, 200 );
|
||||||
}
|
}
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
return $self->sendJSONresponse( $req, { result => 1 } );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub deleteU2FKey {
|
sub delete2F {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } )
|
return $self->sendJSONresponse( $req, { result => 1 } )
|
||||||
if ( $self->{demoMode} );
|
if ( $self->{demoMode} );
|
||||||
|
@ -62,12 +65,51 @@ sub deleteU2FKey {
|
||||||
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
||||||
|
|
||||||
# Try to read session
|
# Try to read session
|
||||||
|
$self->logger->debug("Loading session : $id");
|
||||||
my $session = $self->getApacheSession( $mod, $id )
|
my $session = $self->getApacheSession( $mod, $id )
|
||||||
or return $self->sendError( $req, undef, 400 );
|
or return $self->sendError( $req, undef, 400 );
|
||||||
|
|
||||||
# Delete U2F key attributs and update session
|
# Try to read 2F parameters
|
||||||
$session->data->{_u2fKeyHandle} = '';
|
$self->logger->debug("Reading parameters ...");
|
||||||
$session->data->{_u2fUserKey} = '';
|
my $params = $req->parameters();
|
||||||
|
my $type = $params->{type}
|
||||||
|
or return $self->sendError( $req, '2F device Type is missing', 400 );
|
||||||
|
my $epoch = $params->{epoch}
|
||||||
|
or return $self->sendError( $req, '2F device Epoch is missing', 400 );
|
||||||
|
|
||||||
|
# Try to load 2F Device(s) from session
|
||||||
|
$self->logger->debug("Looking for 2F Device(s) ...");
|
||||||
|
my $_2fDevices;
|
||||||
|
if ( $session->data->{_2fDevices} ) {
|
||||||
|
$_2fDevices = eval {
|
||||||
|
from_json( $session->data->{_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 = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete 2F device
|
||||||
|
$self->logger->debug("Reading 2F device(s) ...");
|
||||||
|
my @keep = ();
|
||||||
|
while (@$_2fDevices) {
|
||||||
|
my $element = shift @$_2fDevices;
|
||||||
|
$self->logger->debug(
|
||||||
|
"Searching 2F device to delete -> $type / $epoch ...");
|
||||||
|
push @keep, $element
|
||||||
|
unless ( ( $element->{type} eq $type )
|
||||||
|
and ( $element->{epoch} eq $epoch ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update session
|
||||||
|
$self->logger->debug("Saving 2F Devices ...");
|
||||||
|
$session->data->{_2fDevices} = to_json( \@keep );
|
||||||
|
$self->logger->debug("Updating session ...");
|
||||||
$session->update( \%{ $session->data } );
|
$session->update( \%{ $session->data } );
|
||||||
|
|
||||||
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
||||||
|
@ -77,79 +119,55 @@ sub deleteU2FKey {
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
return $self->sendJSONresponse( $req, { result => 1 } );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub deleteTOTPKey {
|
#sub add2F {
|
||||||
my ( $self, $req ) = @_;
|
#my ( $self, $req ) = @_;
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } )
|
#return $self->sendJSONresponse( $req, { result => 1 } )
|
||||||
if ( $self->{demoMode} );
|
#if ( $self->{demoMode} );
|
||||||
my $mod = $self->getMod($req)
|
#my $mod = $self->getMod($req)
|
||||||
or return $self->sendError( $req, undef, 400 );
|
#or return $self->sendError( $req, undef, 400 );
|
||||||
my $id = $req->params('sessionId')
|
#my $id = $req->params('sessionId')
|
||||||
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
#or return $self->sendError( $req, 'sessionId is missing', 400 );
|
||||||
|
|
||||||
# Try to read session
|
## Try to read session
|
||||||
my $session = $self->getApacheSession( $mod, $id )
|
#my $session = $self->getApacheSession( $mod, $id )
|
||||||
or return $self->sendError( $req, undef, 400 );
|
#or return $self->sendError( $req, undef, 400 );
|
||||||
|
|
||||||
# Delete U2F key attributs and update session
|
## Delete U2F key attributs and update session
|
||||||
$session->data->{_totp2fSecret} = '';
|
#$session->data->{_u2fKeyHandle} = 'TOF';
|
||||||
$session->update( \%{ $session->data } );
|
#$session->data->{_u2fUserKey} = 'TOF';
|
||||||
|
#$session->update( \%{ $session->data } );
|
||||||
|
|
||||||
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
#Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
||||||
if ( $session->error ) {
|
#if ( $session->error ) {
|
||||||
return $self->sendError( $req, $session->error, 200 );
|
#return $self->sendError( $req, $session->error, 200 );
|
||||||
}
|
#}
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
#return $self->sendJSONresponse( $req, { result => 1 } );
|
||||||
}
|
#}
|
||||||
|
|
||||||
sub addU2FKey {
|
#sub verify2F {
|
||||||
my ( $self, $req ) = @_;
|
#my ( $self, $req ) = @_;
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } )
|
#return $self->sendJSONresponse( $req, { result => 1 } )
|
||||||
if ( $self->{demoMode} );
|
#if ( $self->{demoMode} );
|
||||||
my $mod = $self->getMod($req)
|
#my $mod = $self->getMod($req)
|
||||||
or return $self->sendError( $req, undef, 400 );
|
#or return $self->sendError( $req, undef, 400 );
|
||||||
my $id = $req->params('sessionId')
|
#my $id = $req->params('sessionId')
|
||||||
or return $self->sendError( $req, 'sessionId is missing', 400 );
|
#or return $self->sendError( $req, 'sessionId is missing', 400 );
|
||||||
|
|
||||||
# Try to read session
|
## Try to read session
|
||||||
my $session = $self->getApacheSession( $mod, $id )
|
#my $session = $self->getApacheSession( $mod, $id )
|
||||||
or return $self->sendError( $req, undef, 400 );
|
#or return $self->sendError( $req, undef, 400 );
|
||||||
|
|
||||||
# Delete U2F key attributs and update session
|
## Delete U2F key attributs and update session
|
||||||
$session->data->{_u2fKeyHandle} = 'TOF';
|
#$session->data->{_u2fKeyHandle} = 'OK';
|
||||||
$session->data->{_u2fUserKey} = 'TOF';
|
#$session->data->{_u2fUserKey} = 'OK';
|
||||||
$session->update( \%{ $session->data } );
|
#$session->update( \%{ $session->data } );
|
||||||
|
|
||||||
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
#Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
||||||
if ( $session->error ) {
|
#if ( $session->error ) {
|
||||||
return $self->sendError( $req, $session->error, 200 );
|
#return $self->sendError( $req, $session->error, 200 );
|
||||||
}
|
#}
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
#return $self->sendJSONresponse( $req, { result => 1 } );
|
||||||
}
|
#}
|
||||||
|
|
||||||
sub verifyU2FKey {
|
|
||||||
my ( $self, $req ) = @_;
|
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } )
|
|
||||||
if ( $self->{demoMode} );
|
|
||||||
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 );
|
|
||||||
|
|
||||||
# Try to read session
|
|
||||||
my $session = $self->getApacheSession( $mod, $id )
|
|
||||||
or return $self->sendError( $req, undef, 400 );
|
|
||||||
|
|
||||||
# Delete U2F key attributs and update session
|
|
||||||
$session->data->{_u2fKeyHandle} = 'OK';
|
|
||||||
$session->data->{_u2fUserKey} = 'OK';
|
|
||||||
$session->update( \%{ $session->data } );
|
|
||||||
|
|
||||||
Lemonldap::NG::Handler::PSGI::Main->localUnlog( $req, $id );
|
|
||||||
if ( $session->error ) {
|
|
||||||
return $self->sendError( $req, $session->error, 200 );
|
|
||||||
}
|
|
||||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub session {
|
sub session {
|
||||||
my ( $self, $req, $id, $skey ) = @_;
|
my ( $self, $req, $id, $skey ) = @_;
|
||||||
|
|
|
@ -27,7 +27,7 @@ use constant defaultRoute => '2ndfa.html#/persistent';
|
||||||
sub addRoutes {
|
sub addRoutes {
|
||||||
my ( $self, $conf ) = @_;
|
my ( $self, $conf ) = @_;
|
||||||
|
|
||||||
# Remote Procedure Call are defined in Lemonldap::NG::Common::Session::REST
|
# Remote Procedure are defined in Lemonldap::NG::Common::Session::REST
|
||||||
# HTML template
|
# HTML template
|
||||||
$self->addRoute( '2ndfa.html', undef, ['GET'] )
|
$self->addRoute( '2ndfa.html', undef, ['GET'] )
|
||||||
|
|
||||||
|
@ -36,24 +36,24 @@ sub addRoutes {
|
||||||
['GET']
|
['GET']
|
||||||
)
|
)
|
||||||
|
|
||||||
# DELETE 2FA KEY
|
# DELETE 2FA DEVICE
|
||||||
->addRoute(
|
->addRoute(
|
||||||
sfa => { ':sessionType' => { ':sessionId' => 'delete2FAKey' } },
|
sfa => { ':sessionType' => { ':sessionId' => 'delete2FA' } },
|
||||||
['DELETE']
|
['DELETE']
|
||||||
)
|
|
||||||
|
|
||||||
# ADD 2FA KEY
|
|
||||||
->addRoute(
|
|
||||||
sfa => { ':sessionType' => { ':sessionId' => 'add2FAKey' } },
|
|
||||||
['PUT']
|
|
||||||
)
|
|
||||||
|
|
||||||
# VERIFY 2FA KEY
|
|
||||||
->addRoute(
|
|
||||||
sfa => { ':sessionType' => { ':sessionId' => 'verify2FAKey' } },
|
|
||||||
['POST']
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
## ADD 2FA DEVICE
|
||||||
|
#->addRoute(
|
||||||
|
#sfa => { ':sessionType' => { ':sessionId' => 'add2FA' } },
|
||||||
|
#['PUT']
|
||||||
|
#)
|
||||||
|
|
||||||
|
## VERIFY 2FA DEVICE
|
||||||
|
#->addRoute(
|
||||||
|
#sfa => { ':sessionType' => { ':sessionId' => 'verify2FA' } },
|
||||||
|
#['POST']
|
||||||
|
#);
|
||||||
|
|
||||||
$self->setTypes($conf);
|
$self->setTypes($conf);
|
||||||
|
|
||||||
$self->{ipField} ||= 'ipAddr';
|
$self->{ipField} ||= 'ipAddr';
|
||||||
|
@ -61,54 +61,60 @@ sub addRoutes {
|
||||||
$self->{hiddenAttributes} //= "_password";
|
$self->{hiddenAttributes} //= "_password";
|
||||||
$self->{TOTPCheck} = '1';
|
$self->{TOTPCheck} = '1';
|
||||||
$self->{U2FCheck} = '1';
|
$self->{U2FCheck} = '1';
|
||||||
|
$self->{UBKCheck} = '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# II. 2FA METHODS #
|
# II. 2FA METHODS #
|
||||||
###################
|
###################
|
||||||
|
|
||||||
sub delete2FAKey {
|
sub delete2FA {
|
||||||
|
|
||||||
my ( $self, $req, $session, $skey ) = @_;
|
my ( $self, $req, $session, $skey ) = @_;
|
||||||
|
|
||||||
my $mod = $self->getMod($req)
|
my $mod = $self->getMod($req)
|
||||||
or return $self->sendError( $req, undef, 400 );
|
or return $self->sendError( $req, undef, 400 );
|
||||||
|
|
||||||
my $params = $req->parameters();
|
my $params = $req->parameters();
|
||||||
my $Key = $params->{Key};
|
my $type = $params->{type};
|
||||||
|
my $epoch = $params->{epoch};
|
||||||
|
|
||||||
if ( $Key =~ /\bU2F\b/ ) {
|
if ( $type =~ /\b(?:U2F|TOTP|UBK)\b/ and $epoch ) {
|
||||||
$self->logger->debug("Call procedure deleteU2FKey");
|
$self->logger->debug("Call procedure delete2F with type=$type and epoch=$epoch");
|
||||||
return $self->deleteU2FKey( $req, $session, $skey );
|
return $self->delete2F( $req, $session, $skey );
|
||||||
}
|
|
||||||
elsif ( $Key =~ /\bTOTP\b/ ) {
|
|
||||||
$self->logger->debug("Call procedure deleteTOTPKey");
|
|
||||||
return $self->deleteTOTPKey( $req, $session, $skey );
|
|
||||||
}
|
}
|
||||||
|
#elsif ( $type =~ /\bTOTP\b/ ) {
|
||||||
|
#$self->logger->debug("Call procedure deleteTOTP");
|
||||||
|
#return $self->deleteTOTP( $req, $session, $skey );
|
||||||
|
#}
|
||||||
|
#elsif ( $type =~ /\bUBK\b/ ) {
|
||||||
|
#$self->logger->debug("Call procedure deleteUBK");
|
||||||
|
#return $self->deleteUBK( $req, $session, $skey );
|
||||||
|
#}
|
||||||
else {
|
else {
|
||||||
return $self->sendError( $req, undef, 400 );
|
return $self->sendError( $req, undef, 400 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add2FAKey {
|
#sub add2FA {
|
||||||
|
|
||||||
my ( $self, $req, $session, $skey ) = @_;
|
#my ( $self, $req, $session, $skey ) = @_;
|
||||||
|
|
||||||
eval 'use Crypt::U2F::Server::Simple';
|
#eval 'use Crypt::U2F::Server::Simple';
|
||||||
if ($@) {
|
#if ($@) {
|
||||||
$self->error("Can't load U2F library: $@");
|
#$self->error("Can't load U2F library: $@");
|
||||||
return 0;
|
#return 0;
|
||||||
}
|
#}
|
||||||
|
|
||||||
return $self->addU2FKey( $req, $session, $skey );
|
#return $self->addU2FKey( $req, $session, $skey );
|
||||||
}
|
#}
|
||||||
|
|
||||||
sub verify2FAKey {
|
#sub verify2FA {
|
||||||
|
|
||||||
my ( $self, $req, $session, $skey ) = @_;
|
#my ( $self, $req, $session, $skey ) = @_;
|
||||||
|
|
||||||
return $self->addU2FKey( $req, $session, $skey );
|
#return $self->addU2FKey( $req, $session, $skey );
|
||||||
}
|
#}
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# III. DISPLAY METHODS #
|
# III. DISPLAY METHODS #
|
||||||
|
@ -137,8 +143,7 @@ sub sfa {
|
||||||
# 2.1 Get fields to require
|
# 2.1 Get fields to require
|
||||||
my @fields = (
|
my @fields = (
|
||||||
'_httpSessionType', $self->{ipField},
|
'_httpSessionType', $self->{ipField},
|
||||||
$whatToTrace, '_u2fKeyHandle',
|
$whatToTrace, '_2fDevices'
|
||||||
'_totp2fSecret'
|
|
||||||
);
|
);
|
||||||
if ( my $groupBy = $params->{groupBy} ) {
|
if ( my $groupBy = $params->{groupBy} ) {
|
||||||
$groupBy =~ s/^substr\((\w+)(?:,\d+(?:,\d+)?)?\)$/$1/;
|
$groupBy =~ s/^substr\((\w+)(?:,\d+(?:,\d+)?)?\)$/$1/;
|
||||||
|
@ -155,9 +160,10 @@ sub sfa {
|
||||||
$moduleOptions->{backend} = $mod->{module};
|
$moduleOptions->{backend} = $mod->{module};
|
||||||
|
|
||||||
# Select 2FA sessions to display
|
# Select 2FA sessions to display
|
||||||
if ( defined $params->{TOTPCheck} and defined $params->{TOTPCheck} ) {
|
if ( defined $params->{TOTPCheck} or defined $params->{U2FCheck} or defined $params->{UBKCheck}) {
|
||||||
$self->{TOTPCheck} = delete $params->{TOTPCheck};
|
$self->{TOTPCheck} = delete $params->{TOTPCheck};
|
||||||
$self->{U2FCheck} = delete $params->{U2FCheck};
|
$self->{U2FCheck} = delete $params->{U2FCheck};
|
||||||
|
$self->{UBKCheck} = delete $params->{UBKCheck};
|
||||||
}
|
}
|
||||||
|
|
||||||
my %filters = map {
|
my %filters = map {
|
||||||
|
@ -236,15 +242,22 @@ sub sfa {
|
||||||
if ( $self->{U2FCheck} eq '2' ) {
|
if ( $self->{U2FCheck} eq '2' ) {
|
||||||
foreach my $session ( keys %$res ) {
|
foreach my $session ( keys %$res ) {
|
||||||
delete $res->{$session}
|
delete $res->{$session}
|
||||||
unless ( defined $res->{$session}->{_u2fKeyHandle}
|
unless ( defined $res->{$session}->{_2fDevices}
|
||||||
and length $res->{$session}->{_u2fKeyHandle} );
|
and $res->{$session}->{_2fDevices} =~ /"type":\s*"U2F"/s );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( $self->{TOTPCheck} eq '2' ) {
|
if ( $self->{TOTPCheck} eq '2' ) {
|
||||||
foreach my $session ( keys %$res ) {
|
foreach my $session ( keys %$res ) {
|
||||||
delete $res->{$session}
|
delete $res->{$session}
|
||||||
unless ( defined $res->{$session}->{_totp2fSecret}
|
unless ( defined $res->{$session}->{_2fDevices}
|
||||||
and length $res->{$session}->{_totp2fSecret} );
|
and $res->{$session}->{_2fDevices} =~ /"type":\s*"TOTP"/s );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $self->{UBKCheck} eq '2' ) {
|
||||||
|
foreach my $session ( keys %$res ) {
|
||||||
|
delete $res->{$session}
|
||||||
|
unless ( defined $res->{$session}->{_2fDevices}
|
||||||
|
and $res->{$session}->{_2fDevices} =~ /"type":\s*"UBK"/s );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,6 @@ displayError = (j, status, err) ->
|
||||||
setMsg res, 'warning'
|
setMsg res, 'warning'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Max number of session to display (see overScheme)
|
# Max number of session to display (see overScheme)
|
||||||
max = 25
|
max = 25
|
||||||
|
|
||||||
|
@ -72,40 +69,34 @@ hiddenAttributes = '_password'
|
||||||
categories =
|
categories =
|
||||||
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
|
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
|
||||||
connectionTitle: ['ipAddr', '_timezone', '_url']
|
connectionTitle: ['ipAddr', '_timezone', '_url']
|
||||||
authenticationTitle:['_session_id', '_user', '_password', 'authenticationLevel']
|
sfaTitle: ['_2fDevices']
|
||||||
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti']
|
|
||||||
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump']
|
|
||||||
groups: ['groups', 'hGroups']
|
|
||||||
ldap: ['dn']
|
|
||||||
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw']
|
|
||||||
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token']
|
|
||||||
|
|
||||||
# Menu entries
|
# Menu entries
|
||||||
menu =
|
menu =
|
||||||
delU2FKey: [
|
#delU2FKey: [
|
||||||
title: 'deleteU2FKey'
|
#title: 'deleteU2FKey'
|
||||||
icon: 'trash'
|
#icon: 'trash'
|
||||||
]
|
#]
|
||||||
addU2FKey: [
|
#addU2FKey: [
|
||||||
title: 'addU2FKey'
|
#title: 'addU2FKey'
|
||||||
icon: 'plus'
|
#icon: 'plus'
|
||||||
]
|
#]
|
||||||
verifyU2FKey: [
|
#verifyU2FKey: [
|
||||||
title: 'verifyU2FKey'
|
#title: 'verifyU2FKey'
|
||||||
icon: 'check'
|
#icon: 'check'
|
||||||
]
|
#]
|
||||||
delTOTPKey: [
|
#delTOTPKey: [
|
||||||
title: 'deleteTOTPKey'
|
#title: 'deleteTOTPKey'
|
||||||
icon: 'trash'
|
#icon: 'trash'
|
||||||
]
|
#]
|
||||||
addTOTPKey: [
|
#addTOTPKey: [
|
||||||
title: 'addTOTPKey'
|
#title: 'addTOTPKey'
|
||||||
icon: 'plus'
|
#icon: 'plus'
|
||||||
]
|
#]
|
||||||
verifyTOTPKey: [
|
#verifyTOTPKey: [
|
||||||
title: 'verifyTOTPKey'
|
#title: 'verifyTOTPKey'
|
||||||
icon: 'check'
|
#icon: 'check'
|
||||||
]
|
#]
|
||||||
home: []
|
home: []
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -146,6 +137,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
switch typeof button.action
|
switch typeof button.action
|
||||||
when 'function'
|
when 'function'
|
||||||
button.action $scope.currentNode, $scope
|
button.action $scope.currentNode, $scope
|
||||||
|
$scope[button.action]()
|
||||||
when 'string'
|
when 'string'
|
||||||
$scope[button.action]()
|
$scope[button.action]()
|
||||||
else
|
else
|
||||||
|
@ -161,83 +153,49 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
$scope.data = []
|
$scope.data = []
|
||||||
$scope.updateTree2 '', $scope.data, 0, 0
|
$scope.updateTree2 '', $scope.data, 0, 0
|
||||||
|
|
||||||
# Delete U2F key
|
# Delete 2FA device
|
||||||
$scope.deleteU2FKey = ->
|
$scope.delete2FA = (type, epoch) ->
|
||||||
|
item = angular.element("#data-#{epoch}")
|
||||||
|
item.remove()
|
||||||
$scope.waiting = true
|
$scope.waiting = true
|
||||||
$http['delete']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=U2F").then (response) ->
|
$http['delete']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?type=#{type}&epoch=#{epoch}").then (response) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
|
#$scope.data = []
|
||||||
$scope.waiting = false
|
$scope.waiting = false
|
||||||
, (resp) ->
|
, (resp) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
$scope.waiting = false
|
|
||||||
$scope.showT = false
|
|
||||||
|
|
||||||
# Delete TOTP key
|
|
||||||
$scope.deleteTOTPKey = ->
|
|
||||||
$scope.waiting = true
|
|
||||||
$http['delete']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=TOTP").then (response) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
|
||||||
, (resp) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
$scope.waiting = false
|
||||||
$scope.showT = false
|
$scope.showT = false
|
||||||
|
#$scope.data = []
|
||||||
|
|
||||||
# Add U2F key
|
## Add 2FA device
|
||||||
$scope.addU2FKey = ->
|
#$scope.add2FA (type) = ->
|
||||||
$scope.waiting = true
|
#$scope.waiting = true
|
||||||
$http['put']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=U2F").then (response) ->
|
#$http['put']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=U2F").then (response) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
$scope.waiting = false
|
#$scope.waiting = false
|
||||||
, (resp) ->
|
#, (resp) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
$scope.waiting = false
|
#$scope.waiting = false
|
||||||
$scope.showT = false
|
#$scope.showT = false
|
||||||
|
|
||||||
# Add TOTP key
|
|
||||||
$scope.addTOTPKey = ->
|
|
||||||
$scope.waiting = true
|
|
||||||
$http['put']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=TOTP").then (response) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
|
||||||
, (resp) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
|
||||||
$scope.showT = false
|
|
||||||
|
|
||||||
# Verify U2F key
|
|
||||||
$scope.verifyU2FKey = ->
|
|
||||||
$scope.waiting = true
|
|
||||||
$http['post']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=U2F").then (response) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
|
||||||
, (resp) ->
|
|
||||||
$scope.currentSession = null
|
|
||||||
$scope.currentScope.remove()
|
|
||||||
$scope.waiting = false
|
|
||||||
$scope.showT = true
|
|
||||||
|
|
||||||
# Verify TOTP key
|
## Verify 2FA device
|
||||||
$scope.verifyTOTPKey = ->
|
#$scope.verify2FA (epoch) = ->
|
||||||
$scope.waiting = true
|
#$scope.waiting = true
|
||||||
$http['post']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=TOTP").then (response) ->
|
#$http['post']("#{scriptname}sfa/#{sessionType}/#{$scope.currentSession.id}?Key=TOTP").then (response) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
$scope.waiting = false
|
#$scope.waiting = false
|
||||||
, (resp) ->
|
#, (resp) ->
|
||||||
$scope.currentSession = null
|
#$scope.currentSession = null
|
||||||
$scope.currentScope.remove()
|
#$scope.currentScope.remove()
|
||||||
$scope.waiting = false
|
#$scope.waiting = false
|
||||||
$scope.showT = true
|
#$scope.showT = true
|
||||||
|
|
||||||
# Open node
|
# Open node
|
||||||
$scope.stoggle = (scope) ->
|
$scope.stoggle = (scope) ->
|
||||||
|
@ -284,66 +242,46 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
else if key.match /^(_utime|_lastAuthnUTime|_lastSeen|notification)$/
|
else if key.match /^(_utime|_lastAuthnUTime|_lastSeen|notification)$/
|
||||||
session[key] = $scope.localeDate value
|
session[key] = $scope.localeDate value
|
||||||
else if key.match /^(_startTime|_updateTime)$/
|
else if key.match /^(_startTime|_updateTime)$/
|
||||||
session[key] = _stToStr value
|
session[key] = _stToStr value
|
||||||
#else if key.match /^(_u2fKeyHandle|_u2fUserKey|_totp2fSecret)$/
|
|
||||||
# session[key] = '##########'
|
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
# 2. Push session keys in result, grouped by categories
|
# 2. Push session keys in result, grouped by categories
|
||||||
for category, attrs of categories
|
for category, attrs of categories
|
||||||
subres = []
|
subres = []
|
||||||
for attr in attrs
|
for attr in attrs
|
||||||
if session[attr]
|
if session[attr] and session[attr].match(/\w+/)
|
||||||
subres.push
|
if session[attr].match(/"type":\s*"(?:TOTP|U2F|UBK)"/)
|
||||||
title: attr
|
subres.push
|
||||||
value: session[attr]
|
title: "type"
|
||||||
delete session[attr]
|
value: "name"
|
||||||
|
sdate: "date"
|
||||||
|
array = JSON.parse(session[attr]);
|
||||||
|
for sfDevice in array
|
||||||
|
for key, value of sfDevice
|
||||||
|
if key == 'type'
|
||||||
|
title = value
|
||||||
|
if key == 'name'
|
||||||
|
name = value
|
||||||
|
if key == 'epoch'
|
||||||
|
epoch = value
|
||||||
|
newDate = new Date(value * 1000)
|
||||||
|
myDate = newDate.toLocaleString()
|
||||||
|
subres.push
|
||||||
|
title: title
|
||||||
|
value: name
|
||||||
|
epoch: epoch
|
||||||
|
sdate: myDate
|
||||||
|
delete session[attr]
|
||||||
|
else
|
||||||
|
subres.push
|
||||||
|
title: attr
|
||||||
|
value: session[attr]
|
||||||
|
delete session[attr]
|
||||||
|
|
||||||
if subres.length >0
|
if subres.length >0
|
||||||
res.push
|
res.push
|
||||||
title: "__#{category}__"
|
title: "__#{category}__"
|
||||||
nodes: subres
|
nodes: subres
|
||||||
|
|
||||||
# 3. Add OpenID and notifications already notified
|
|
||||||
_insert '^openid', 'OpenID'
|
|
||||||
_insert '^notification_(.+)', '__notificationsDone__'
|
|
||||||
|
|
||||||
# 4. Add session history if exists
|
|
||||||
if session._loginHistory
|
|
||||||
tmp = []
|
|
||||||
if session._loginHistory.successLogin
|
|
||||||
for l in session._loginHistory.successLogin
|
|
||||||
tmp.push
|
|
||||||
t: l._utime
|
|
||||||
title: $scope.localeDate l._utime
|
|
||||||
value: "Success (IP #{l.ipAddr})"
|
|
||||||
if session._loginHistory.failedLogin
|
|
||||||
for l in session._loginHistory.failedLogin
|
|
||||||
tmp.push
|
|
||||||
t: l._utime
|
|
||||||
title: $scope.localeDate l._utime
|
|
||||||
value: "#{l.error} (IP #{l.ipAddr})"
|
|
||||||
delete session._loginHistory
|
|
||||||
tmp.sort (a,b) ->
|
|
||||||
a.t - b.t
|
|
||||||
res.push
|
|
||||||
title: '__loginHistory__'
|
|
||||||
nodes: tmp
|
|
||||||
|
|
||||||
# 5. Other keys (attributes and macros)
|
|
||||||
tmp = []
|
|
||||||
for key, value of session
|
|
||||||
tmp.push
|
|
||||||
title: key
|
|
||||||
value: value
|
|
||||||
tmp.sort (a,b) ->
|
|
||||||
if a.title > b.title then 1
|
|
||||||
else if a.title < b.title then -1
|
|
||||||
else 0
|
|
||||||
|
|
||||||
res.push
|
|
||||||
title: '__attributesAndMacros__'
|
|
||||||
nodes: tmp
|
|
||||||
return {
|
return {
|
||||||
_utime: time
|
_utime: time
|
||||||
id: id
|
id: id
|
||||||
|
@ -478,7 +416,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
over = 0
|
over = 0
|
||||||
|
|
||||||
# Launch HTTP
|
# Launch HTTP
|
||||||
$http.get("#{scriptname}sfa/#{sessionType}?_session_uid=#{$scope.searchString}*&groupBy=substr(_session_uid,#{$scope.searchString.length})&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}").then (response) ->
|
$http.get("#{scriptname}sfa/#{sessionType}?_session_uid=#{$scope.searchString}*&groupBy=substr(_session_uid,#{$scope.searchString.length})&U2FCheck=#{$scope.U2FCheck}&TOTPCheck=#{$scope.TOTPCheck}&UBKCheck=#{$scope.UBKCheck}").then (response) ->
|
||||||
data = response.data
|
data = response.data
|
||||||
if data.result
|
if data.result
|
||||||
for n in data.values
|
for n in data.values
|
||||||
|
@ -528,3 +466,5 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
$scope.type = if c then c[1] else '_whatToTrace'
|
$scope.type = if c then c[1] else '_whatToTrace'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ hiddenAttributes = '_password'
|
||||||
categories =
|
categories =
|
||||||
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
|
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen']
|
||||||
connectionTitle: ['ipAddr', '_timezone', '_url']
|
connectionTitle: ['ipAddr', '_timezone', '_url']
|
||||||
authenticationTitle:['_session_id', '_user', '_password', 'authenticationLevel']
|
authenticationTitle:['_session_id', '_user', '_password', 'authenticationLevel', '_2fDevices']
|
||||||
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti']
|
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti']
|
||||||
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump']
|
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump']
|
||||||
groups: ['groups', 'hGroups']
|
groups: ['groups', 'hGroups']
|
||||||
|
@ -196,8 +196,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||||
session[key] = $scope.localeDate value
|
session[key] = $scope.localeDate value
|
||||||
else if key.match /^(_startTime|_updateTime)$/
|
else if key.match /^(_startTime|_updateTime)$/
|
||||||
session[key] = _stToStr value
|
session[key] = _stToStr value
|
||||||
#else if key.match /^(_u2fKeyHandle|_u2fUserKey|_totp2fSecret)$/
|
|
||||||
# session[key] = '##########'
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
# 2. Push session keys in result, grouped by categories
|
# 2. Push session keys in result, grouped by categories
|
||||||
|
|
|
@ -82,52 +82,10 @@
|
||||||
categories = {
|
categories = {
|
||||||
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
|
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
|
||||||
connectionTitle: ['ipAddr', '_timezone', '_url'],
|
connectionTitle: ['ipAddr', '_timezone', '_url'],
|
||||||
authenticationTitle: ['_session_id', '_user', '_password', 'authenticationLevel'],
|
sfaTitle: ['_2fDevices']
|
||||||
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti'],
|
|
||||||
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump'],
|
|
||||||
groups: ['groups', 'hGroups'],
|
|
||||||
ldap: ['dn'],
|
|
||||||
BrowserID: ['_browserIdAnswer', '_browserIdAnswerRaw'],
|
|
||||||
OpenIDConnect: ['_oidc_id_token', '_oidc_OP', '_oidc_access_token']
|
|
||||||
};
|
};
|
||||||
|
|
||||||
menu = {
|
menu = {
|
||||||
delU2FKey: [
|
|
||||||
{
|
|
||||||
title: 'deleteU2FKey',
|
|
||||||
icon: 'trash'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
addU2FKey: [
|
|
||||||
{
|
|
||||||
title: 'addU2FKey',
|
|
||||||
icon: 'plus'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
verifyU2FKey: [
|
|
||||||
{
|
|
||||||
title: 'verifyU2FKey',
|
|
||||||
icon: 'check'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
delTOTPKey: [
|
|
||||||
{
|
|
||||||
title: 'deleteTOTPKey',
|
|
||||||
icon: 'trash'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
addTOTPKey: [
|
|
||||||
{
|
|
||||||
title: 'addTOTPKey',
|
|
||||||
icon: 'plus'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
verifyTOTPKey: [
|
|
||||||
{
|
|
||||||
title: 'verifyTOTPKey',
|
|
||||||
icon: 'check'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
home: []
|
home: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,6 +129,7 @@
|
||||||
switch (typeof button.action) {
|
switch (typeof button.action) {
|
||||||
case 'function':
|
case 'function':
|
||||||
button.action($scope.currentNode, $scope);
|
button.action($scope.currentNode, $scope);
|
||||||
|
$scope[button.action]();
|
||||||
break;
|
break;
|
||||||
case 'string':
|
case 'string':
|
||||||
$scope[button.action]();
|
$scope[button.action]();
|
||||||
|
@ -189,84 +148,18 @@
|
||||||
$scope.data = [];
|
$scope.data = [];
|
||||||
return $scope.updateTree2('', $scope.data, 0, 0);
|
return $scope.updateTree2('', $scope.data, 0, 0);
|
||||||
};
|
};
|
||||||
$scope.deleteU2FKey = function() {
|
$scope.delete2FA = function(type, epoch) {
|
||||||
|
var item;
|
||||||
|
item = angular.element("#data-" + epoch);
|
||||||
|
item.remove();
|
||||||
$scope.waiting = true;
|
$scope.waiting = true;
|
||||||
$http['delete'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=U2F").then(function(response) {
|
$http['delete'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?type=" + type + "&epoch=" + epoch).then(function(response) {
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
return $scope.waiting = false;
|
||||||
}, function(resp) {
|
}, function(resp) {
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
return $scope.waiting = false;
|
||||||
});
|
});
|
||||||
return $scope.showT = false;
|
return $scope.showT = false;
|
||||||
};
|
};
|
||||||
$scope.deleteTOTPKey = function() {
|
|
||||||
$scope.waiting = true;
|
|
||||||
$http['delete'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=TOTP").then(function(response) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
}, function(resp) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
});
|
|
||||||
return $scope.showT = false;
|
|
||||||
};
|
|
||||||
$scope.addU2FKey = function() {
|
|
||||||
$scope.waiting = true;
|
|
||||||
$http['put'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=U2F").then(function(response) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
}, function(resp) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
});
|
|
||||||
return $scope.showT = false;
|
|
||||||
};
|
|
||||||
$scope.addTOTPKey = function() {
|
|
||||||
$scope.waiting = true;
|
|
||||||
$http['put'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=TOTP").then(function(response) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
}, function(resp) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
});
|
|
||||||
return $scope.showT = false;
|
|
||||||
};
|
|
||||||
$scope.verifyU2FKey = function() {
|
|
||||||
$scope.waiting = true;
|
|
||||||
$http['post'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=U2F").then(function(response) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
}, function(resp) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
});
|
|
||||||
return $scope.showT = true;
|
|
||||||
};
|
|
||||||
$scope.verifyTOTPKey = function() {
|
|
||||||
$scope.waiting = true;
|
|
||||||
$http['post'](scriptname + "sfa/" + sessionType + "/" + $scope.currentSession.id + "?Key=TOTP").then(function(response) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
}, function(resp) {
|
|
||||||
$scope.currentSession = null;
|
|
||||||
$scope.currentScope.remove();
|
|
||||||
return $scope.waiting = false;
|
|
||||||
});
|
|
||||||
return $scope.showT = true;
|
|
||||||
};
|
|
||||||
$scope.stoggle = function(scope) {
|
$scope.stoggle = function(scope) {
|
||||||
var node;
|
var node;
|
||||||
node = scope.$modelValue;
|
node = scope.$modelValue;
|
||||||
|
@ -278,7 +171,7 @@
|
||||||
$scope.displaySession = function(scope) {
|
$scope.displaySession = function(scope) {
|
||||||
var sessionId, transformSession;
|
var sessionId, transformSession;
|
||||||
transformSession = function(session) {
|
transformSession = function(session) {
|
||||||
var _insert, _stToStr, attr, attrs, category, i, id, k, key, l, len, len1, len2, m, ref, ref1, res, subres, time, tmp, value;
|
var _insert, _stToStr, array, attr, attrs, category, epoch, i, id, k, key, len, len1, myDate, name, newDate, res, sfDevice, subres, time, title, value;
|
||||||
_stToStr = function(s) {
|
_stToStr = function(s) {
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
@ -330,12 +223,45 @@
|
||||||
subres = [];
|
subres = [];
|
||||||
for (i = 0, len = attrs.length; i < len; i++) {
|
for (i = 0, len = attrs.length; i < len; i++) {
|
||||||
attr = attrs[i];
|
attr = attrs[i];
|
||||||
if (session[attr]) {
|
if (session[attr] && session[attr].match(/\w+/)) {
|
||||||
subres.push({
|
if (session[attr].match(/"type":\s*"(?:TOTP|U2F|UBK)"/)) {
|
||||||
title: attr,
|
subres.push({
|
||||||
value: session[attr]
|
title: "type",
|
||||||
});
|
value: "name",
|
||||||
delete session[attr];
|
sdate: "date"
|
||||||
|
});
|
||||||
|
array = JSON.parse(session[attr]);
|
||||||
|
for (k = 0, len1 = array.length; k < len1; k++) {
|
||||||
|
sfDevice = array[k];
|
||||||
|
for (key in sfDevice) {
|
||||||
|
value = sfDevice[key];
|
||||||
|
if (key === 'type') {
|
||||||
|
title = value;
|
||||||
|
}
|
||||||
|
if (key === 'name') {
|
||||||
|
name = value;
|
||||||
|
}
|
||||||
|
if (key === 'epoch') {
|
||||||
|
epoch = value;
|
||||||
|
newDate = new Date(value * 1000);
|
||||||
|
myDate = newDate.toLocaleString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subres.push({
|
||||||
|
title: title,
|
||||||
|
value: name,
|
||||||
|
epoch: epoch,
|
||||||
|
sdate: myDate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
delete session[attr];
|
||||||
|
} else {
|
||||||
|
subres.push({
|
||||||
|
title: attr,
|
||||||
|
value: session[attr]
|
||||||
|
});
|
||||||
|
delete session[attr];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (subres.length > 0) {
|
if (subres.length > 0) {
|
||||||
|
@ -345,62 +271,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_insert('^openid', 'OpenID');
|
|
||||||
_insert('^notification_(.+)', '__notificationsDone__');
|
|
||||||
if (session._loginHistory) {
|
|
||||||
tmp = [];
|
|
||||||
if (session._loginHistory.successLogin) {
|
|
||||||
ref = session._loginHistory.successLogin;
|
|
||||||
for (k = 0, len1 = ref.length; k < len1; k++) {
|
|
||||||
l = ref[k];
|
|
||||||
tmp.push({
|
|
||||||
t: l._utime,
|
|
||||||
title: $scope.localeDate(l._utime),
|
|
||||||
value: "Success (IP " + l.ipAddr + ")"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (session._loginHistory.failedLogin) {
|
|
||||||
ref1 = session._loginHistory.failedLogin;
|
|
||||||
for (m = 0, len2 = ref1.length; m < len2; m++) {
|
|
||||||
l = ref1[m];
|
|
||||||
tmp.push({
|
|
||||||
t: l._utime,
|
|
||||||
title: $scope.localeDate(l._utime),
|
|
||||||
value: l.error + " (IP " + l.ipAddr + ")"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete session._loginHistory;
|
|
||||||
tmp.sort(function(a, b) {
|
|
||||||
return a.t - b.t;
|
|
||||||
});
|
|
||||||
res.push({
|
|
||||||
title: '__loginHistory__',
|
|
||||||
nodes: tmp
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tmp = [];
|
|
||||||
for (key in session) {
|
|
||||||
value = session[key];
|
|
||||||
tmp.push({
|
|
||||||
title: key,
|
|
||||||
value: value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
tmp.sort(function(a, b) {
|
|
||||||
if (a.title > b.title) {
|
|
||||||
return 1;
|
|
||||||
} else if (a.title < b.title) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
res.push({
|
|
||||||
title: '__attributesAndMacros__',
|
|
||||||
nodes: tmp
|
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
_utime: time,
|
_utime: time,
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -502,7 +372,7 @@
|
||||||
} else {
|
} else {
|
||||||
over = 0;
|
over = 0;
|
||||||
}
|
}
|
||||||
return $http.get(scriptname + "sfa/" + sessionType + "?_session_uid=" + $scope.searchString + "*&groupBy=substr(_session_uid," + $scope.searchString.length + ")&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck).then(function(response) {
|
return $http.get(scriptname + "sfa/" + sessionType + "?_session_uid=" + $scope.searchString + "*&groupBy=substr(_session_uid," + $scope.searchString.length + ")&U2FCheck=" + $scope.U2FCheck + "&TOTPCheck=" + $scope.TOTPCheck + "&UBKCheck=" + $scope.UBKCheck).then(function(response) {
|
||||||
var data, i, len, n, ref;
|
var data, i, len, n, ref;
|
||||||
data = response.data;
|
data = response.data;
|
||||||
if (data.result) {
|
if (data.result) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -94,7 +94,7 @@
|
||||||
categories = {
|
categories = {
|
||||||
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
|
dateTitle: ['_utime', '_startTime', '_updateTime', '_lastAuthnUTime', '_lastSeen'],
|
||||||
connectionTitle: ['ipAddr', '_timezone', '_url'],
|
connectionTitle: ['ipAddr', '_timezone', '_url'],
|
||||||
authenticationTitle: ['_session_id', '_user', '_password', 'authenticationLevel'],
|
authenticationTitle: ['_session_id', '_user', '_password', 'authenticationLevel', '_2fDevices'],
|
||||||
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti'],
|
modulesTitle: ['_auth', '_userDB', '_passwordDB', '_issuerDB', '_authChoice', '_authMulti', '_userDBMulti'],
|
||||||
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump'],
|
saml: ['_idp', '_idpConfKey', '_samlToken', '_lassoSessionDump', '_lassoIdentityDump'],
|
||||||
groups: ['groups', 'hGroups'],
|
groups: ['groups', 'hGroups'],
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -672,6 +672,7 @@
|
||||||
"sessionStartedAt":"بدأت الجلسة",
|
"sessionStartedAt":"بدأت الجلسة",
|
||||||
"sessionStorage":"تخزين الجلسات",
|
"sessionStorage":"تخزين الجلسات",
|
||||||
"sessionTitle":"محتوى الجلسة",
|
"sessionTitle":"محتوى الجلسة",
|
||||||
|
"sfaTitle":"Seconds Factors Authentication",
|
||||||
"show":"عرض",
|
"show":"عرض",
|
||||||
"showHelp":"عرض المساعدة",
|
"showHelp":"عرض المساعدة",
|
||||||
"singleIP":"عنوان آي بي واحد لكل مستخدم",
|
"singleIP":"عنوان آي بي واحد لكل مستخدم",
|
||||||
|
|
|
@ -664,7 +664,7 @@
|
||||||
"security":"Security",
|
"security":"Security",
|
||||||
"serverError":"Server error",
|
"serverError":"Server error",
|
||||||
"session":"session",
|
"session":"session",
|
||||||
"sessions":"Sessions",
|
"sessions":"sessions",
|
||||||
"session_s":"session(s)",
|
"session_s":"session(s)",
|
||||||
"sessionDataToRemember":"Session data to store",
|
"sessionDataToRemember":"Session data to store",
|
||||||
"sessionDeleted":"The session was deleted",
|
"sessionDeleted":"The session was deleted",
|
||||||
|
@ -672,6 +672,7 @@
|
||||||
"sessionStartedAt":"Session started on",
|
"sessionStartedAt":"Session started on",
|
||||||
"sessionStorage":"Sessions Storage",
|
"sessionStorage":"Sessions Storage",
|
||||||
"sessionTitle":"Session content",
|
"sessionTitle":"Session content",
|
||||||
|
"sfaTitle":"Seconds Factors Authentication",
|
||||||
"show":"Show",
|
"show":"Show",
|
||||||
"showHelp":"Show help",
|
"showHelp":"Show help",
|
||||||
"singleIP":"One IP only by user",
|
"singleIP":"One IP only by user",
|
||||||
|
|
|
@ -664,7 +664,7 @@
|
||||||
"security":"Sécurité",
|
"security":"Sécurité",
|
||||||
"serverError":"Erreur du serveur",
|
"serverError":"Erreur du serveur",
|
||||||
"session":"session",
|
"session":"session",
|
||||||
"sessions":"Sessions",
|
"sessions":"sessions",
|
||||||
"session_s":"session(s)",
|
"session_s":"session(s)",
|
||||||
"sessionDataToRemember":"Données de session à conserver",
|
"sessionDataToRemember":"Données de session à conserver",
|
||||||
"sessionDeleted":"La session a été supprimée",
|
"sessionDeleted":"La session a été supprimée",
|
||||||
|
@ -672,6 +672,7 @@
|
||||||
"sessionStartedAt":"Session démarrée le ",
|
"sessionStartedAt":"Session démarrée le ",
|
||||||
"sessionStorage":"Stockage des sessions",
|
"sessionStorage":"Stockage des sessions",
|
||||||
"sessionTitle":"Contenu de la session",
|
"sessionTitle":"Contenu de la session",
|
||||||
|
"sfaTitle":"Seconds Facteurs d'Authentification",
|
||||||
"show":"Montrer",
|
"show":"Montrer",
|
||||||
"showHelp":"Montrer l'aide",
|
"showHelp":"Montrer l'aide",
|
||||||
"singleIP":"Une seule session par couple utilisateur/IP",
|
"singleIP":"Une seule session par couple utilisateur/IP",
|
||||||
|
|
|
@ -672,6 +672,7 @@
|
||||||
"sessionStartedAt":"La sessione è stata avviata",
|
"sessionStartedAt":"La sessione è stata avviata",
|
||||||
"sessionStorage":"Conservazione di sessioni",
|
"sessionStorage":"Conservazione di sessioni",
|
||||||
"sessionTitle":"Contenuto della sessione",
|
"sessionTitle":"Contenuto della sessione",
|
||||||
|
"sfaTitle":"Seconds Factors Authentication",
|
||||||
"show":"Mostra",
|
"show":"Mostra",
|
||||||
"showHelp":"Mostra aiuto",
|
"showHelp":"Mostra aiuto",
|
||||||
"singleIP":"Solo un IP per utente",
|
"singleIP":"Solo un IP per utente",
|
||||||
|
|
|
@ -665,13 +665,14 @@
|
||||||
"serverError":"Lỗi máy chủ",
|
"serverError":"Lỗi máy chủ",
|
||||||
"session":"phiên",
|
"session":"phiên",
|
||||||
"sessions":"Phiên",
|
"sessions":"Phiên",
|
||||||
"session_s":"session (s)",
|
"session_s":"session(s)",
|
||||||
"sessionDataToRemember":"Dữ liệu phiên để lưu trữ",
|
"sessionDataToRemember":"Dữ liệu phiên để lưu trữ",
|
||||||
"sessionDeleted":"Phiên đã bị xóa",
|
"sessionDeleted":"Phiên đã bị xóa",
|
||||||
"sessionParams":"Phiên",
|
"sessionParams":"Phiên",
|
||||||
"sessionStartedAt":"Phiên bắt đầu lúc",
|
"sessionStartedAt":"Phiên bắt đầu lúc",
|
||||||
"sessionStorage":"Sessions lưu trữ",
|
"sessionStorage":"Sessions lưu trữ",
|
||||||
"sessionTitle":"Nội dung phiên",
|
"sessionTitle":"Nội dung phiên",
|
||||||
|
"sfaTitle":"Seconds Factors Authentication",
|
||||||
"show":"Hiển thị",
|
"show":"Hiển thị",
|
||||||
"showHelp":"Hiển thị trợ giúp",
|
"showHelp":"Hiển thị trợ giúp",
|
||||||
"singleIP":"Chỉ một địa chỉ IP bởi người dùng",
|
"singleIP":"Chỉ một địa chỉ IP bởi người dùng",
|
||||||
|
|
|
@ -16,9 +16,13 @@
|
||||||
<form name="filterForm">
|
<form name="filterForm">
|
||||||
<div class="form-check ">
|
<div class="form-check ">
|
||||||
<input type="checkbox" ng-model="U2FCheck" class="form-check-input" ng-true-value="'2'" ng-false-value="'1'" ng-change="search2FA()"/>
|
<input type="checkbox" ng-model="U2FCheck" class="form-check-input" ng-true-value="'2'" ng-false-value="'1'" ng-change="search2FA()"/>
|
||||||
<label class="form-check-label" for="U2FCheck">U2F</label>
|
<label class="form-check-label" for="U2FCheck">U2F</label>
|
||||||
|
|
||||||
<input type="checkbox" ng-model="TOTPCheck" class="form-check-input" ng-true-value="'2'" ng-false-value="'1'" ng-change="search2FA()"/>
|
<input type="checkbox" ng-model="TOTPCheck" class="form-check-input" ng-true-value="'2'" ng-false-value="'1'" ng-change="search2FA()"/>
|
||||||
<label class="form-check-label" for="TOTPCheck">TOTP</label>
|
<label class="form-check-label" for="TOTPCheck">TOTP</label>
|
||||||
|
|
||||||
|
<input type="checkbox" ng-model="UBKCheck" class="form-check-input" ng-true-value="'2'" ng-false-value="'1'" ng-change="search2FA()"/>
|
||||||
|
<label class="form-check-label" for="UBKCheck">UBK</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -54,49 +58,7 @@
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- Right(main) div -->
|
<!-- Right(main) div -->
|
||||||
<div id="right" class="col-lg-8 col-md-8 col-sm-7 col-xs-12 scrollable" ng-class="{'hidden-xs':showT&&!showM}">
|
<div id="right" class="col-lg-8 col-md-8 col-sm-7 col-xs-12 scrollable" ng-class="{'hidden-xs':showT&&!showM}">
|
||||||
<!-- Menu buttons -->
|
|
||||||
|
|
||||||
<div ng-if="currentSession && U2FCheck=='2' || currentSession && U2FCheck!='2' && TOTPCheck!='2'" class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
|
|
||||||
|
|
||||||
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
|
|
||||||
<ul ng-if="currentSession" class="nav navbar-nav">
|
|
||||||
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.delU2FKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.verifyU2FKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.addU2FKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession===null" ng-repeat="button in menu.home" ng-include="'menubutton.html'"></li>
|
|
||||||
<li uib-dropdown class="visible-xs">
|
|
||||||
<a id="langmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Menu <span class="caret"></span></a>
|
|
||||||
<ul uib-dropdown-menu aria-labelled-by="langmenu" role="grid">
|
|
||||||
<li ng-repeat="link in links"><a href="{{link.target}}" role="row"><i ng-if="link.icon" class="glyphicon glyphicon-{{link.icon}}"></i> {{translate(link.title)}}</a></li>
|
|
||||||
<li ng-repeat="menulink in menulinks"><a href="{{menulink.target}}" role="row"><i ng-if="menulink.icon" class="glyphicon glyphicon-{{menulink.icon}}"></i> {{translate(menulink.title)}}</a></li>
|
|
||||||
<li ng-include="'languages.html'"></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="currentSession && TOTPCheck=='2'|| currentSession && U2FCheck!='2' && TOTPCheck!='2'" class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
|
|
||||||
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
|
|
||||||
<ul ng-if="currentSession" class="nav navbar-nav">
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.delTOTPKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.verifyTOTPKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession" ng-repeat="button in menu.addTOTPKey" ng-include="'menubutton.html'"></li>
|
|
||||||
<li ng-if="currentSession===null" ng-repeat="button in menu.home" ng-include="'menubutton.html'"></li>
|
|
||||||
<li uib-dropdown class="visible-xs">
|
|
||||||
<a id="langmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Menu <span class="caret"></span></a>
|
|
||||||
<ul uib-dropdown-menu aria-labelled-by="langmenu" role="grid">
|
|
||||||
<li ng-repeat="link in links"><a href="{{link.target}}" role="row"><i ng-if="link.icon" class="glyphicon glyphicon-{{link.icon}}"></i> {{translate(link.title)}}</a></li>
|
|
||||||
<li ng-repeat="menulink in menulinks"><a href="{{menulink.target}}" role="row"><i ng-if="menulink.icon" class="glyphicon glyphicon-{{menulink.icon}}"></i> {{translate(menulink.title)}}</a></li>
|
|
||||||
<li ng-include="'languages.html'"></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel panel-default" ng-hide="currentSession===null">
|
<div class="panel panel-default" ng-hide="currentSession===null">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h1 class="panel-title text-center">{{translate("sessionTitle")}} {{currentSession.id}}</h1>
|
<h1 class="panel-title text-center">{{translate("sessionTitle")}} {{currentSession.id}}</h1>
|
||||||
|
@ -123,10 +85,19 @@
|
||||||
<tr ng-repeat="node in node.nodes" ng-include="'session_attr.html'"></tr>
|
<tr ng-repeat="node in node.nodes" ng-include="'session_attr.html'"></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="!node.nodes">
|
<div ng-if="!node.nodes" id="data-{{node.epoch}}" ng-hide="isHidden">
|
||||||
<th>{{translate(node.title)}}</th>
|
<th ng-if="node.title=='type'">{{translate(node.title)}}</th>
|
||||||
<td><tt>${{node.title}}</tt></td>
|
<td ng-if="node.title!='type'">{{node.title}}</td>
|
||||||
<td><span id="v-{{node.title}}">{{node.value}}</td>
|
<th ng-if="node.title=='type'"><span id="v-{{node.value}}">{{translate(node.value)}}</span></th>
|
||||||
|
<td ng-if="node.title!='type'"><span id="v-{{node.value}}">{{node.value}}</span></td>
|
||||||
|
<th ng-if="node.title=='type'"><span id="v-{{node.sdate}}">{{translate(node.sdate)}}</span></th>
|
||||||
|
<td ng-if="node.title!='type'" class="data-epoch"><span id="v-{{node.sdate}}">{{node.sdate}}</span></td>
|
||||||
|
<td>
|
||||||
|
<span ng-if="node.title=='TOTP' || node.title=='UBK' || node.title=='U2F'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="delete2FA(node.title, node.epoch)"></span>
|
||||||
|
<!--
|
||||||
|
<span ng-if="$last && ( node.title=='TOTP' || node.title=='UBK' || node.title=='U2F' )" class="link text-success glyphicon glyphicon-plus-sign" ng-click="menuClick({title:'newRule'})"></span>
|
||||||
|
-->
|
||||||
|
</td>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -138,12 +109,14 @@
|
||||||
</a>
|
</a>
|
||||||
<span id="s-{{node.value}}" ng-click="stoggle(this)">{{node.title || node.value}} <span class="badge">{{node.count}}</span></span>
|
<span id="s-{{node.value}}" ng-click="stoggle(this)">{{node.title || node.value}} <span class="badge">{{node.count}}</span></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span ng-if="node.session">
|
<span ng-if="node.session">
|
||||||
<a class="btn btn-node btn-sm" ng-click="displaySession(this)">
|
<a class="btn btn-node btn-sm" ng-click="displaySession(this)">
|
||||||
<span class="glyphicon glyphicon-eye-open"></span>
|
<span class="glyphicon glyphicon-eye-open"></span>
|
||||||
</a>
|
</a>
|
||||||
<span id="s-{{node.session}}" ng-click="displaySession(this)">{{localeDate(node.date)}}</span>
|
<span id="s-{{node.session}}" ng-click="displaySession(this)">{{localeDate(node.date)}}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<ol ui-tree-nodes="" ng-model="node.nodes" ng-class="{hidden: collapsed}">
|
<ol ui-tree-nodes="" ng-model="node.nodes" ng-class="{hidden: collapsed}">
|
||||||
<li ng-repeat="node in node.nodes track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
<li ng-repeat="node in node.nodes track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
|
||||||
|
|
|
@ -162,14 +162,43 @@ sub run {
|
||||||
elsif ( $err == 0 ) {
|
elsif ( $err == 0 ) {
|
||||||
return $self->p->sendError( $req, "noU2FKeyFound" );
|
return $self->p->sendError( $req, "noU2FKeyFound" );
|
||||||
}
|
}
|
||||||
my $challenge = $req->datas->{crypter}->authenticationChallenge;
|
|
||||||
|
# Get a challenge (from first key)
|
||||||
|
my $data = eval {
|
||||||
|
from_json( $req->datas->{crypter}->[0]->authenticationChallenge );
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($@) {
|
||||||
|
$self->logger->error( Crypt::U2F::Server::u2fclib_getError() );
|
||||||
|
return $self->p->sendError( $req, "U2F error: $error", 200 );
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get registered keys
|
||||||
|
my @rk;
|
||||||
|
foreach ( @{ $req->datas->{crypter} } ) {
|
||||||
|
my $k = push @rk,
|
||||||
|
{ keyHandle => $_->{keyHandle}, version => $data->{version} };
|
||||||
|
|
||||||
|
#{ keyHandle => $_->{keyHandle}, version => $challenge->{version} };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Serialize datas
|
||||||
|
$data = to_json(
|
||||||
|
{
|
||||||
|
challenge => $data->{challenge},
|
||||||
|
appId => $data->{appId},
|
||||||
|
registeredKeys => \@rk
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
200,
|
200,
|
||||||
[
|
[
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
'Content-Length' => length($challenge),
|
'Content-Length' => length($data),
|
||||||
],
|
],
|
||||||
[$challenge]
|
[$data]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,15 +213,35 @@ sub run {
|
||||||
}
|
}
|
||||||
my ( $err, $error ) = $self->loadUser($req);
|
my ( $err, $error ) = $self->loadUser($req);
|
||||||
if ( $err == -1 ) {
|
if ( $err == -1 ) {
|
||||||
return $self->p->sendError( $req, "U2F error: $error", 200 );
|
return $self->p->sendError( $req, "U2F loading error: $error", 500 );
|
||||||
}
|
}
|
||||||
elsif ( $err == 0 ) {
|
elsif ( $err == 0 ) {
|
||||||
return $self->p->sendError( $req, "noU2FKeyFound" );
|
return $self->p->sendError( $req, "noU2FKeyFound" );
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->logger->debug("Get verify response $resp");
|
$self->logger->debug("Get verify response $resp");
|
||||||
$req->datas->{crypter}->setChallenge($challenge);
|
my $data = eval { JSON::from_json($resp) };
|
||||||
my $res =
|
if ($@) {
|
||||||
( $req->datas->{crypter}->authenticationVerify($resp) ? 1 : 0 );
|
$self->logger->error("U2F response error: $@");
|
||||||
|
return $self->p->sendError( $req, "U2FAnswerError" );
|
||||||
|
}
|
||||||
|
my $crypter;
|
||||||
|
foreach ( @{ $req->datas->{crypter} } ) {
|
||||||
|
$crypter = $_ if ( $_->{keyHandle} eq $data->{keyHandle} );
|
||||||
|
}
|
||||||
|
unless ($crypter) {
|
||||||
|
$self->userLogger->error("Unregistered U2F key");
|
||||||
|
return $self->p->sendError( $req, "U2FKeyUnregistered" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( not $crypter->setChallenge($challenge) ) {
|
||||||
|
$self->logger->error(
|
||||||
|
$@ ? $@ : Crypt::U2F::Server::Simple::lastError() );
|
||||||
|
|
||||||
|
return $self->p->sendError( $req, "U2FServerError" );
|
||||||
|
}
|
||||||
|
|
||||||
|
my $res = ( $crypter->authenticationVerify($resp) ? 1 : 0 );
|
||||||
return [
|
return [
|
||||||
200,
|
200,
|
||||||
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
|
[ 'Content-Type' => 'application/json', 'Content-Length' => 12, ],
|
||||||
|
@ -216,7 +265,6 @@ sub run {
|
||||||
[$challenge]
|
[$challenge]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ( $action eq 'delete' ) {
|
elsif ( $action eq 'delete' ) {
|
||||||
my $epoch = $req->param('epoch');
|
my $epoch = $req->param('epoch');
|
||||||
|
|
||||||
|
@ -266,14 +314,13 @@ sub run {
|
||||||
sub loadUser {
|
sub loadUser {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
$self->logger->debug("Loading user U2F Devices ...");
|
$self->logger->debug("Loading user U2F Devices ...");
|
||||||
my $uid = $req->userData->{ $self->conf->{whatToTrace} };
|
|
||||||
my $session = $self->p->getPersistentSession($uid);
|
|
||||||
|
|
||||||
my $secret = '';
|
|
||||||
|
|
||||||
# Read existing 2FDevices
|
# Read existing 2FDevices
|
||||||
$self->logger->debug("Looking for 2F Devices ...");
|
$self->logger->debug("Looking for 2F Devices ...");
|
||||||
my $_2fDevices;
|
my ( $kh, $uk, $_2fDevices );
|
||||||
|
|
||||||
|
my @u2fs = ();
|
||||||
|
|
||||||
if ( $req->userData->{_2fDevices} ) {
|
if ( $req->userData->{_2fDevices} ) {
|
||||||
$_2fDevices = eval {
|
$_2fDevices = eval {
|
||||||
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
|
from_json( $req->userData->{_2fDevices}, { allow_nonref => 1 } );
|
||||||
|
@ -283,32 +330,56 @@ sub loadUser {
|
||||||
return $self->p->sendError( $req, "Corrupted session", 500 );
|
return $self->p->sendError( $req, "Corrupted session", 500 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
$self->logger->debug("No 2F Device found");
|
$self->logger->debug("No 2F Device found");
|
||||||
$_2fDevices = [];
|
$_2fDevices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reading existing U2F keys
|
# Reading existing U2F keys
|
||||||
$self->logger->debug("Reading U2F keys if exists ...");
|
foreach (@$_2fDevices) {
|
||||||
my @U2Fs = grep { $_->{type} =~ /U2F/s } @$_2fDevices;
|
$self->logger->debug("Looking for registered U2F key(s) ...");
|
||||||
my $kh = $U2Fs[0]{_keyHandle};
|
if ( $_->{type} eq 'U2F' ) {
|
||||||
my $uk = $self->decode_base64url( $U2Fs[0]{_userKey} );
|
unless ( $_->{_userKey} and $_->{_keyHandle} ) {
|
||||||
unless ( $kh and $uk ) {
|
$self->logger->error(
|
||||||
$self->logger->debug("UTOP -> No U2F key found !!!");
|
'Missing required U2F attributes in storage ($session->{_2fDevices})'
|
||||||
|
);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$self->logger->debug( "Found U2F key -> _userKey = "
|
||||||
|
. $_->{_userKey}
|
||||||
|
. "/ _keyHandle = "
|
||||||
|
. $_->{_keyHandle} );
|
||||||
|
$_->{_userKey} = $self->decode_base64url( $_->{_userKey} );
|
||||||
|
push @u2fs, $_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Manage multi u2f keys
|
||||||
|
my @crypters;
|
||||||
|
if (@u2fs) {
|
||||||
|
foreach (@u2fs) {
|
||||||
|
$kh = $_->{_keyHandle};
|
||||||
|
$uk = $_->{_userKey};
|
||||||
|
my $c = $self->crypter( keyHandle => $kh, publicKey => $uk );
|
||||||
|
if ($c) {
|
||||||
|
$self->logger->debug("kh & uk -> OK");
|
||||||
|
push @crypters, $c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->logger->error(
|
||||||
|
'U2F error: ' . Crypt::U2F::Server::u2fclib_getError() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unless (@crypters) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
$req->datas->{crypter} = \@crypters;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->userLogger->info("U2F : user not registered");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$self->logger->debug( "_userKey = " . $uk );
|
|
||||||
|
|
||||||
$req->datas->{crypter} = $self->crypter(
|
|
||||||
keyHandle => $kh,
|
|
||||||
publicKey => $uk
|
|
||||||
);
|
|
||||||
unless ( $req->datas->{crypter} ) {
|
|
||||||
my $error = Crypt::U2F::Server::Simple::lastError();
|
|
||||||
return ( -1, $error );
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -203,10 +203,12 @@ sub loadUser {
|
||||||
}
|
}
|
||||||
$self->logger->debug("2F Device(s) found");
|
$self->logger->debug("2F Device(s) found");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (@$_2fDevices) {
|
foreach (@$_2fDevices) {
|
||||||
$self->logger->debug("Looking for registered U2F key(s) ...");
|
$self->logger->debug("Looking for registered U2F key(s) ...");
|
||||||
if ( $_->{type} eq 'U2F' ) {
|
if ( $_->{type} eq 'U2F' ) {
|
||||||
unless ( $_->{_userKey} and $_->{_userKey} ) {
|
unless ( $_->{_userKey} and $_->{_keyHandle} ) {
|
||||||
$self->logger->error(
|
$self->logger->error(
|
||||||
"Missing required U2F attributes in storage ($session->{_2fDevices})"
|
"Missing required U2F attributes in storage ($session->{_2fDevices})"
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,12 +67,8 @@ verify = ->
|
||||||
error: displayError
|
error: displayError
|
||||||
success: (ch) ->
|
success: (ch) ->
|
||||||
# 2 build response
|
# 2 build response
|
||||||
request = [
|
|
||||||
keyHandle: ch.keyHandle
|
|
||||||
version: ch.version
|
|
||||||
]
|
|
||||||
setMsg 'touchU2fDevice', 'positive'
|
setMsg 'touchU2fDevice', 'positive'
|
||||||
u2f.sign ch.appId, ch.challenge, request, (data) ->
|
u2f.sign ch.appId, ch.challenge, ch.registeredKeys, (data) ->
|
||||||
# Handle errors
|
# Handle errors
|
||||||
if data.errorCode
|
if data.errorCode
|
||||||
setMsg 'unableToGetKey', 'warning'
|
setMsg 'unableToGetKey', 'warning'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Generated by CoffeeScript 1.9.3
|
// Generated by CoffeeScript 1.10.0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
LemonLDAP::NG U2F registration script
|
LemonLDAP::NG U2F registration script
|
||||||
|
@ -82,15 +82,8 @@ LemonLDAP::NG U2F registration script
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
error: displayError,
|
error: displayError,
|
||||||
success: function(ch) {
|
success: function(ch) {
|
||||||
var request;
|
|
||||||
request = [
|
|
||||||
{
|
|
||||||
keyHandle: ch.keyHandle,
|
|
||||||
version: ch.version
|
|
||||||
}
|
|
||||||
];
|
|
||||||
setMsg('touchU2fDevice', 'positive');
|
setMsg('touchU2fDevice', 'positive');
|
||||||
return u2f.sign(ch.appId, ch.challenge, request, function(data) {
|
return u2f.sign(ch.appId, ch.challenge, ch.registeredKeys, function(data) {
|
||||||
if (data.errorCode) {
|
if (data.errorCode) {
|
||||||
return setMsg('unableToGetKey', 'warning');
|
return setMsg('unableToGetKey', 'warning');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
(function(){var a,b,c,d;c=function(e,f){$("#msg").html(window.translate(e));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+f);if(f==="positive"){f="success"}return $("#color").addClass("alert-"+f)};a=function(f,e,h){var g;console.log("Error",h);g=JSON.parse(f.responseText);if(g&&g.error){g=g.error.replace(/.* /,"");console.log("Returned error",g);return c(g,"warning")}};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(e){var f;f=[{challenge:e.challenge,version:e.version}];c("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(e.appId,f,[],function(g){$("#u2fPermission").hide();if(g.errorCode){return c(g.error,"warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(g),challenge:JSON.stringify(e),keyName:$("#keyName").val()},dataType:"json",success:function(h){if(h.error){return c("u2fFailed","warning")}else{if(h.result){return c("yourKeyIsRegistered","positive")}}},error:a})}})}})};d=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(e){var f;f=[{keyHandle:e.keyHandle,version:e.version}];c("touchU2fDevice","positive");return u2f.sign(e.appId,e.challenge,f,function(g){if(g.errorCode){return c("unableToGetKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(g),challenge:e.challenge},dataType:"json",success:function(h){if(h.error){return c("u2fFailed","warning")}else{if(h.result){return c("yourKeyIsVerified","positive")}}},error:function(i,h,k){return console.log("error",k)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",b);$("#verify").on("click",d);return $("#goback").attr("href",portal)})}).call(this);
|
(function(){var a,b,c,d;c=function(e,f){$("#msg").html(window.translate(e));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+f);if(f==="positive"){f="success"}return $("#color").addClass("alert-"+f)};a=function(f,e,h){var g;console.log("Error",h);g=JSON.parse(f.responseText);if(g&&g.error){g=g.error.replace(/.* /,"");console.log("Returned error",g);return c(g,"warning")}};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(e){var f;f=[{challenge:e.challenge,version:e.version}];c("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(e.appId,f,[],function(g){$("#u2fPermission").hide();if(g.errorCode){return c(g.error,"warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(g),challenge:JSON.stringify(e),keyName:$("#keyName").val()},dataType:"json",success:function(h){if(h.error){return c("u2fFailed","warning")}else{if(h.result){return c("yourKeyIsRegistered","positive")}}},error:a})}})}})};d=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(e){c("touchU2fDevice","positive");return u2f.sign(e.appId,e.challenge,e.registeredKeys,function(f){if(f.errorCode){return c("unableToGetKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(f),challenge:e.challenge},dataType:"json",success:function(g){if(g.error){return c("u2fFailed","warning")}else{if(g.result){return c("yourKeyIsVerified","positive")}}},error:function(h,g,i){return console.log("error",i)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",b);$("#verify").on("click",d);return $("#goback").attr("href",portal)})}).call(this);
|
|
@ -15,7 +15,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
|
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
|
||||||
<span class="glyphicon glyphicon-home"></span>
|
<span class="glyphicon glyphicon-home"></span>
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
</TMPL_IF>
|
</TMPL_IF>
|
||||||
|
<br>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<TMPL_LOOP NAME="MODULES">
|
<TMPL_LOOP NAME="MODULES">
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,14 @@
|
||||||
<span trspan="connect">Connect</span>
|
<span trspan="connect">Connect</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
|
||||||
|
<span class="glyphicon glyphicon-home"></span>
|
||||||
|
<span trspan="goToPortal">Go to portal</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<TMPL_INCLUDE NAME="footer.tpl">
|
<TMPL_INCLUDE NAME="footer.tpl">
|
||||||
|
|
|
@ -49,6 +49,6 @@
|
||||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">/common/js/totpregistration.min.js"></script>
|
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">/common/js/totpregistration.min.js"></script>
|
||||||
//else -->
|
//else -->
|
||||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">bwr/qrious/dist/qrious.js"></script>
|
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">bwr/qrious/dist/qrious.js"></script>
|
||||||
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">/common/js/totpregistration.js"></script>
|
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">common/js/totpregistration.js"></script>
|
||||||
<!-- //endif -->
|
<!-- //endif -->
|
||||||
<TMPL_INCLUDE NAME="footer.tpl">
|
<TMPL_INCLUDE NAME="footer.tpl">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user