Merge branch 'manager-u2f-module_add-u2f-key' into 'master'
Manager u2f module add u2f key See merge request lemonldap-ng/lemonldap-ng!16
This commit is contained in:
commit
ec85664131
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
.gitignore
|
||||
node_modules
|
||||
e2e-tests/conf/
|
||||
e2e-tests/conf
|
||||
lemonldap-ng-common/MYMETA.json
|
||||
lemonldap-ng-common/MYMETA.yml
|
||||
lemonldap-ng-common/Makefile
|
||||
|
@ -52,7 +52,7 @@ sub delSession {
|
||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
||||
}
|
||||
|
||||
sub delU2FKey {
|
||||
sub deleteU2FKey {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->sendJSONresponse( $req, { result => 1 } )
|
||||
if ( $self->{demoMode} );
|
||||
@ -77,6 +77,56 @@ sub delU2FKey {
|
||||
return $self->sendJSONresponse( $req, { result => 1 } );
|
||||
}
|
||||
|
||||
sub addU2FKey {
|
||||
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} = 'TOF';
|
||||
$session->data->{_u2fUserKey} = 'TOF';
|
||||
$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 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 {
|
||||
my ( $self, $req, $id, $skey ) = @_;
|
||||
my ( %h, $res );
|
||||
|
@ -8,12 +8,12 @@ sub types {
|
||||
'authParamsText' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'blackWhiteList' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'bool' => {
|
||||
'msgFail' => '__notABoolean__',
|
||||
@ -36,17 +36,17 @@ sub types {
|
||||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'catAndAppList' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'file' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'hostname' => {
|
||||
'form' => 'text',
|
||||
@ -80,48 +80,48 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
||||
if $_ =~ /exportedvars$/i and defined $conf->{$_}{$val};
|
||||
}
|
||||
return 1, "__unknownAttrOrMacro__: $val";
|
||||
}
|
||||
}
|
||||
},
|
||||
'longtext' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'menuApp' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'menuCat' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcmetadatajson' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcmetadatajwks' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcOPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'oidcRPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'password' => {
|
||||
'msgFail' => '__malformedValue__',
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'pcre' => {
|
||||
'form' => 'text',
|
||||
@ -132,7 +132,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
||||
}
|
||||
};
|
||||
return $@ ? ( 0, "__badRegexp__: $@" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'PerlModule' => {
|
||||
'form' => 'text',
|
||||
@ -142,17 +142,17 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
||||
'portalskin' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'portalskinbackground' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'post' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPrivateKey' => {
|
||||
'test' => sub {
|
||||
@ -160,7 +160,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
|
||||
m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPublicKey' => {
|
||||
'test' => sub {
|
||||
@ -168,7 +168,7 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+=
|
||||
m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+PUBLIC\s+KEY\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'RSAPublicKeyOrCertificate' => {
|
||||
'test' => sub {
|
||||
@ -176,37 +176,37 @@ m[^(?:(?:\-+\s*BEGIN\s+PUBLIC\s+KEY\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\
|
||||
m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+)?[\r\n]*)?$]s
|
||||
? 1
|
||||
: ( 1, '__badPemEncoding__' );
|
||||
}
|
||||
}
|
||||
},
|
||||
'rule' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlAssertion' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlAttribute' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlIDPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlService' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'samlSPMetaDataNode' => {
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'select' => {
|
||||
'test' => sub {
|
||||
@ -216,19 +216,19 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\
|
||||
return $test
|
||||
? 1
|
||||
: ( 1, "Invalid value '$_[0]' for this select" );
|
||||
}
|
||||
}
|
||||
},
|
||||
'subContainer' => {
|
||||
'keyTest' => qr/\w/,
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'text' => {
|
||||
'msgFail' => '__malformedValue__',
|
||||
'test' => sub {
|
||||
1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'trool' => {
|
||||
'msgFail' => '__authorizedValues__: -1, 0, 1',
|
||||
@ -1036,7 +1036,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
||||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
@ -1208,7 +1208,7 @@ qr/^(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-
|
||||
and defined $conf->{$_}{$val};
|
||||
}
|
||||
return 1, "__unknownAttrOrMacro__: $val";
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'doubleHash'
|
||||
},
|
||||
@ -1490,7 +1490,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
||||
split( /\n/, $@, 0 ) )
|
||||
);
|
||||
return $err ? ( 1, "__badExpression__: $err" ) : 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
'type' => 'ruleContainer'
|
||||
},
|
||||
@ -2986,19 +2986,19 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
||||
'default' => 0,
|
||||
'select' => [
|
||||
{
|
||||
'k' => '0',
|
||||
'k' => 0,
|
||||
'v' => 'unsecuredCookie'
|
||||
},
|
||||
{
|
||||
'k' => '1',
|
||||
'k' => 1,
|
||||
'v' => 'securedCookie'
|
||||
},
|
||||
{
|
||||
'k' => '2',
|
||||
'k' => 2,
|
||||
'v' => 'doubleCookie'
|
||||
},
|
||||
{
|
||||
'k' => '3',
|
||||
'k' => 3,
|
||||
'v' => 'doubleCookieForSingleSession'
|
||||
}
|
||||
],
|
||||
|
@ -4,12 +4,14 @@ use 5.10.0;
|
||||
use utf8;
|
||||
use strict;
|
||||
use Mouse;
|
||||
use MIME::Base64 qw(encode_base64 decode_base64);
|
||||
use Crypt::U2F::Server::Simple;
|
||||
|
||||
use Lemonldap::NG::Common::Session;
|
||||
use Lemonldap::NG::Common::Conf::Constants;
|
||||
use Lemonldap::NG::Common::PSGI::Constants;
|
||||
use Lemonldap::NG::Common::Conf::ReConstants;
|
||||
use Lemonldap::NG::Common::IPv6;
|
||||
#use Lemonldap::NG::Common::IPv6;
|
||||
|
||||
use feature 'state';
|
||||
|
||||
@ -33,21 +35,27 @@ sub addRoutes {
|
||||
|
||||
# READ
|
||||
->addRoute(
|
||||
u2f => { ':sessionType' => 'sessions' },
|
||||
u2f => { ':sessionType' => 'u2f' },
|
||||
['GET']
|
||||
)
|
||||
|
||||
# DELETE U2F KEY ATTRIBUTS
|
||||
# DELETE U2F KEY
|
||||
->addRoute(
|
||||
u2f => { ':sessionType' => { ':sessionId' => 'delU2FKey' } },
|
||||
u2f => { ':sessionType' => { ':sessionId' => 'deleteU2FKey' } },
|
||||
['DELETE']
|
||||
);
|
||||
)
|
||||
|
||||
# UPDATE U2F KEY ATTRIBUTS
|
||||
# ->addRoute(
|
||||
# u2f => { ':sessionType' => { ':sessionId' => { ':updateSession' } },
|
||||
# ['PUT']
|
||||
#);
|
||||
# ADD U2F KEY
|
||||
->addRoute(
|
||||
u2f => { ':sessionType' => { ':sessionId' => 'registerU2FKey' } },
|
||||
['PUT']
|
||||
)
|
||||
|
||||
# VERIFY U2F KEY
|
||||
->addRoute(
|
||||
u2f => { ':sessionType' => { ':sessionId' => 'verifyU2FKey' } },
|
||||
['POST']
|
||||
);
|
||||
|
||||
$self->setTypes($conf);
|
||||
|
||||
@ -56,9 +64,28 @@ sub addRoutes {
|
||||
$self->{hiddenAttributes} //= "_password";
|
||||
}
|
||||
|
||||
#######################
|
||||
# II. DISPLAY METHODS #
|
||||
#######################
|
||||
|
||||
############################
|
||||
# II. REGISTRATION METHODS #
|
||||
############################
|
||||
|
||||
sub registerU2FKey {
|
||||
|
||||
my ( $self, $req, $session, $skey ) = @_;
|
||||
|
||||
eval 'use Crypt::U2F::Server::Simple';
|
||||
if ($@) {
|
||||
$self->error("Can't load U2F library: $@");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $self->addU2FKey( $req, $session, $skey );
|
||||
}
|
||||
|
||||
|
||||
########################
|
||||
# III. DISPLAY METHODS #
|
||||
########################
|
||||
|
||||
sub u2f {
|
||||
my ( $self, $req, $session, $skey ) = @_;
|
||||
@ -72,7 +99,7 @@ sub u2f {
|
||||
or return $self->sendError( $req, undef, 400 );
|
||||
my $params = $req->parameters();
|
||||
my $type = delete $params->{sessionType};
|
||||
$type = $type eq 'global' ? 'SSO' : ucfirst($type);
|
||||
$type = ucfirst($type);
|
||||
|
||||
my $res;
|
||||
|
||||
@ -81,18 +108,12 @@ sub u2f {
|
||||
my $whatToTrace = Lemonldap::NG::Handler::PSGI::Main->tsv->{whatToTrace};
|
||||
|
||||
# 2.1 Get fields to require
|
||||
my @fields = ( '_httpSessionType', $self->{ipField}, $whatToTrace );
|
||||
my @fields = ( '_httpSessionType', $self->{ipField}, $whatToTrace, '_u2fKeyHandle' );
|
||||
if ( my $groupBy = $params->{groupBy} ) {
|
||||
$groupBy =~ s/^substr\((\w+)(?:,\d+(?:,\d+)?)?\)$/$1/
|
||||
or $groupBy =~ s/^net(?:4|6|)\(([\w:]+),\d+(?:,\d+)?\)$/$1/;
|
||||
$groupBy =~ s/^substr\((\w+)(?:,\d+(?:,\d+)?)?\)$/$1/;
|
||||
$groupBy =~ s/^_whatToTrace$/$whatToTrace/o
|
||||
or push @fields, $groupBy;
|
||||
}
|
||||
elsif ( my $order = $params->{orderBy} ) {
|
||||
$order =~ s/^net(?:4|6|)\(([\w:]+)\)$/$1/;
|
||||
$order =~ s/^_whatToTrace$/$whatToTrace/o
|
||||
or push @fields, split( /, /, $order );
|
||||
}
|
||||
else {
|
||||
push @fields, '_utime';
|
||||
}
|
||||
@ -109,10 +130,12 @@ sub u2f {
|
||||
: ( $s => $params->{$_} );
|
||||
} keys %$params;
|
||||
$filters{_session_kind} = $type;
|
||||
# $filters{_u2fKeyHandle} = '';
|
||||
push @fields, keys(%filters);
|
||||
{
|
||||
my %seen;
|
||||
@fields = grep { !$seen{$_}++ } @fields;
|
||||
# @fields = grep { !/\w+/ } @fields;
|
||||
}
|
||||
|
||||
# For now, only one argument can be passed to
|
||||
@ -172,62 +195,15 @@ sub u2f {
|
||||
}
|
||||
}
|
||||
|
||||
# Display sessions with U2F key registered only
|
||||
foreach my $session ( keys %$res ) {
|
||||
delete $res->{$session}
|
||||
unless ( defined $res->{$session}->{_u2fKeyHandle} and length $res->{$session}->{_u2fKeyHandle} )
|
||||
}
|
||||
|
||||
my $total = ( keys %$res );
|
||||
|
||||
# 2.4 Special case doubleIp (users connected from more than 1 IP)
|
||||
if ( defined $params->{doubleIp} ) {
|
||||
my %r;
|
||||
|
||||
# 2.4.1 Store user IP addresses in %r
|
||||
foreach my $id ( keys %$res ) {
|
||||
my $entry = $res->{$id};
|
||||
next if ( $entry->{_httpSessionType} );
|
||||
$r{ $entry->{$whatToTrace} }->{ $entry->{ $self->{ipField} } }++;
|
||||
}
|
||||
|
||||
# 2.4.2 Store sessions owned by users that has more than one IP address in $r
|
||||
my $r;
|
||||
$total = 0;
|
||||
foreach my $k ( keys %$res ) {
|
||||
my @tmp = keys %{ $r{ $res->{$k}->{$whatToTrace} } };
|
||||
if ( @tmp > 1 ) {
|
||||
$total += 1;
|
||||
$res->{$k}->{_sessionId} = $k;
|
||||
push @{ $r->{ $res->{$k}->{$whatToTrace} } }, $res->{$k};
|
||||
}
|
||||
}
|
||||
|
||||
# 2.4.3 Store these session in an array. Array elements are :
|
||||
# {
|
||||
# uid => whatToTraceFieldValue,
|
||||
# sessions => [
|
||||
# { session => <session-id-1>, date => <_utime> },
|
||||
# { session => <session-id-2>, date => <_utime> },
|
||||
# ]
|
||||
# }
|
||||
$res = [];
|
||||
foreach my $uid ( sort keys %$r ) {
|
||||
push @$res, {
|
||||
value => $uid,
|
||||
count => scalar( @{ $r->{$uid} } ),
|
||||
sessions => [
|
||||
map {
|
||||
{
|
||||
session => $_->{_sessionId},
|
||||
date => $_->{_utime}
|
||||
}
|
||||
} @{ $r->{$uid} }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
# 2.4 Order and group by
|
||||
# $res will become an array ref here (except for doubleIp, already done below).
|
||||
|
||||
# If "groupBy" is asked, elements will be like:
|
||||
# { uid => 'foo.bar', count => 3 }
|
||||
elsif ( my $group = $req->params('groupBy') ) {
|
||||
if ( my $group = $req->params('groupBy') ) {
|
||||
my $r;
|
||||
$group =~ s/\b_whatToTrace\b/$whatToTrace/o;
|
||||
|
||||
@ -243,46 +219,6 @@ sub u2f {
|
||||
$group = $field;
|
||||
}
|
||||
|
||||
# Subnets IPv4
|
||||
elsif ( $group =~ /^net4\((\w+),(\d)\)$/ ) {
|
||||
my $field = $1;
|
||||
my $nb = $2 - 1;
|
||||
foreach my $k ( keys %$res ) {
|
||||
if ( $res->{$k}->{$field} =~ /^((((\d+)\.\d+)\.\d+)\.\d+)$/ ) {
|
||||
my @d = ( $4, $3, $2, $1 );
|
||||
$r->{ $d[$nb] }++;
|
||||
}
|
||||
}
|
||||
$group = $field;
|
||||
}
|
||||
|
||||
# Subnets IPv6
|
||||
elsif ( $group =~ /^net6\(([\w:]+),(\d)\)$/ ) {
|
||||
my $field = $1;
|
||||
my $bits = $2;
|
||||
foreach my $k ( keys %$res ) {
|
||||
$r->{ net6( $res->{$k}->{$field}, $bits ) . "/$bits" }++
|
||||
if ( isIPv6( $res->{$k}->{$field} ) );
|
||||
}
|
||||
}
|
||||
|
||||
# Both IPv4 and IPv6
|
||||
elsif ( $group =~ /^net\(([\w:]+),(\d+),(\d+)\)$/ ) {
|
||||
my $field = $1;
|
||||
my $bits = $2;
|
||||
my $nb = $3 - 1;
|
||||
foreach my $k ( keys %$res ) {
|
||||
if ( isIPv6( $res->{$k}->{$field} ) ) {
|
||||
$r->{ net6( $res->{$k}->{$field}, $bits ) . "/$bits" }++;
|
||||
}
|
||||
elsif ( $res->{$k}->{$field} =~ /^((((\d+)\.\d+)\.\d+)\.\d+)$/ )
|
||||
{
|
||||
my @d = ( $4, $3, $2, $1 );
|
||||
$r->{ $d[$nb] }++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Simple field groupBy query
|
||||
elsif ( $group =~ /^\w+$/ ) {
|
||||
eval {
|
||||
@ -316,47 +252,6 @@ qq{Use of an uninitialized attribute "$group" to group sessions},
|
||||
];
|
||||
}
|
||||
|
||||
# Else if "orderBy" is asked, $res elements will be like:
|
||||
# { uid => 'foo.bar', session => <sessionId> }
|
||||
elsif ( my $f = $req->params('orderBy') ) {
|
||||
my @fields = split /,/, $f;
|
||||
my @r = map {
|
||||
my $tmp = { session => $_ };
|
||||
foreach my $f (@fields) {
|
||||
my $s = $f;
|
||||
$s =~ s/^net(?:4|6|)\(([\w:]+)\)$/$1/;
|
||||
$tmp->{$s} = $res->{$_}->{$s};
|
||||
}
|
||||
$tmp
|
||||
} keys %$res;
|
||||
while ( my $f = pop @fields ) {
|
||||
if ( $f =~ s/^net4\((\w+)\)$/$1/ ) {
|
||||
@r = sort { cmpIPv4( $a->{$f}, $b->{$f} ); } @r;
|
||||
}
|
||||
elsif ( $f =~ s/^net6\(([:\w]+)\)$/$1/ ) {
|
||||
@r = sort { expand6( $a->{$f} ) cmp expand6( $b->{$f} ); } @r;
|
||||
}
|
||||
elsif ( $f =~ s/^net\(([:\w]+)\)$/$1/ ) {
|
||||
@r = sort {
|
||||
my $ip1 = $a->{$f};
|
||||
my $ip2 = $b->{$f};
|
||||
isIPv6($ip1)
|
||||
? (
|
||||
isIPv6($ip2)
|
||||
? expand6($ip1) cmp expand6($ip2)
|
||||
: -1
|
||||
)
|
||||
: isIPv6($ip2) ? 1
|
||||
: cmpIPv4( $ip1, $ip2 );
|
||||
} @r;
|
||||
}
|
||||
else {
|
||||
@r = sort { $a->{$f} cmp $b->{$f} } @r;
|
||||
}
|
||||
}
|
||||
$res = [@r];
|
||||
}
|
||||
|
||||
# Else, $res elements will be like:
|
||||
# { session => <sessionId>, date => <timestamp> }
|
||||
else {
|
||||
@ -378,17 +273,4 @@ qq{Use of an uninitialized attribute "$group" to group sessions},
|
||||
);
|
||||
}
|
||||
|
||||
sub cmpIPv4 {
|
||||
my @a = split /\./, $_[0];
|
||||
my @b = split /\./, $_[1];
|
||||
my $cmp = 0;
|
||||
F: for ( my $i = 0 ; $i < 4 ; $i++ ) {
|
||||
if ( $a[$i] != $b[$i] ) {
|
||||
$cmp = $a[$i] <=> $b[$i];
|
||||
last F;
|
||||
}
|
||||
}
|
||||
$cmp;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -196,6 +196,8 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
session[key] = $scope.localeDate value
|
||||
else if key.match /^(_startTime|_updateTime)$/
|
||||
session[key] = _stToStr value
|
||||
else if key.match /^(_u2fKeyHandle|_u2fUserKey)$/
|
||||
session[key] = '########'
|
||||
res = []
|
||||
|
||||
# 2. Push session keys in reuslt, grouped by categories
|
||||
|
@ -87,10 +87,18 @@ categories =
|
||||
|
||||
# Menu entries
|
||||
menu =
|
||||
session: [
|
||||
delU2FKey: [
|
||||
title: 'deleteU2FKey'
|
||||
icon: 'trash'
|
||||
]
|
||||
addU2FKey: [
|
||||
title: 'addU2FKey'
|
||||
icon: 'plus'
|
||||
]
|
||||
verifyU2FKey: [
|
||||
title: 'verifyU2FKey'
|
||||
icon: 'check'
|
||||
]
|
||||
home: []
|
||||
|
||||
###
|
||||
@ -138,7 +146,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
|
||||
# SESSION MANAGEMENT
|
||||
|
||||
# Delete U2F key attributs
|
||||
# Delete U2F key
|
||||
$scope.deleteU2FKey = ->
|
||||
$scope.waiting = true
|
||||
$http['delete']("#{scriptname}u2f/#{sessionType}/#{$scope.currentSession.id}").then (response) ->
|
||||
@ -151,6 +159,32 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
$scope.waiting = false
|
||||
$scope.showT = true
|
||||
|
||||
# Add U2F key
|
||||
$scope.addU2FKey = ->
|
||||
$scope.waiting = true
|
||||
$http['put']("#{scriptname}u2f/#{sessionType}/#{$scope.currentSession.id}").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 U2F key
|
||||
$scope.verifyU2FKey = ->
|
||||
$scope.waiting = true
|
||||
$http['post']("#{scriptname}u2f/#{sessionType}/#{$scope.currentSession.id}").then (response) ->
|
||||
$scope.currentSession = null
|
||||
#$scope.currentScope.remove()
|
||||
$scope.waiting = false
|
||||
, (resp) ->
|
||||
$scope.currentSession = null
|
||||
#$scope.currentScope.remove()
|
||||
$scope.waiting = false
|
||||
$scope.showT = true
|
||||
|
||||
# Open node
|
||||
$scope.stoggle = (scope) ->
|
||||
node = scope.$modelValue
|
||||
@ -197,6 +231,8 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
session[key] = $scope.localeDate value
|
||||
else if key.match /^(_startTime|_updateTime)$/
|
||||
session[key] = _stToStr value
|
||||
else if key.match /^(_u2fKeyHandle|_u2fUserKey)$/
|
||||
session[key] = '########'
|
||||
res = []
|
||||
|
||||
# 2. Push session keys in result, grouped by categories
|
||||
@ -261,7 +297,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
|
||||
$scope.currentScope = scope
|
||||
sessionId = scope.$modelValue.session
|
||||
$http.get("#{scriptname}sessions/#{sessionType}/#{sessionId}").then (response) ->
|
||||
$http.get("#{scriptname}u2f/#{sessionType}/#{sessionId}").then (response) ->
|
||||
$scope.currentSession = transformSession response.data
|
||||
$scope.showT = false
|
||||
|
||||
@ -325,7 +361,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
||||
over = 0
|
||||
|
||||
# Launch HTTP query
|
||||
$http.get("#{scriptname}sessions/#{sessionType}?#{query}").then (response) ->
|
||||
$http.get("#{scriptname}u2f/#{sessionType}?#{query}").then (response) ->
|
||||
data = response.data
|
||||
if data.result
|
||||
for n in data.values
|
||||
|
@ -227,6 +227,8 @@
|
||||
session[key] = $scope.localeDate(value);
|
||||
} else if (key.match(/^(_startTime|_updateTime)$/)) {
|
||||
session[key] = _stToStr(value);
|
||||
} else if (key.match(/^(_u2fKeyHandle|_u2fUserKey)$/)) {
|
||||
session[key] = '########';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -104,12 +104,24 @@
|
||||
};
|
||||
|
||||
menu = {
|
||||
session: [
|
||||
delU2FKey: [
|
||||
{
|
||||
title: 'deleteU2FKey',
|
||||
icon: 'trash'
|
||||
}
|
||||
],
|
||||
addU2FKey: [
|
||||
{
|
||||
title: 'addU2FKey',
|
||||
icon: 'plus'
|
||||
}
|
||||
],
|
||||
verifyU2FKey: [
|
||||
{
|
||||
title: 'verifyU2FKey',
|
||||
icon: 'check'
|
||||
}
|
||||
],
|
||||
home: []
|
||||
};
|
||||
|
||||
@ -173,6 +185,28 @@
|
||||
});
|
||||
return $scope.showT = true;
|
||||
};
|
||||
$scope.addU2FKey = function() {
|
||||
$scope.waiting = true;
|
||||
$http['put'](scriptname + "u2f/" + sessionType + "/" + $scope.currentSession.id).then(function(response) {
|
||||
$scope.currentSession = null;
|
||||
return $scope.waiting = false;
|
||||
}, function(resp) {
|
||||
$scope.currentSession = null;
|
||||
return $scope.waiting = false;
|
||||
});
|
||||
return $scope.showT = true;
|
||||
};
|
||||
$scope.verifyU2FKey = function() {
|
||||
$scope.waiting = true;
|
||||
$http['post'](scriptname + "u2f/" + sessionType + "/" + $scope.currentSession.id).then(function(response) {
|
||||
$scope.currentSession = null;
|
||||
return $scope.waiting = false;
|
||||
}, function(resp) {
|
||||
$scope.currentSession = null;
|
||||
return $scope.waiting = false;
|
||||
});
|
||||
return $scope.showT = true;
|
||||
};
|
||||
$scope.stoggle = function(scope) {
|
||||
var node;
|
||||
node = scope.$modelValue;
|
||||
@ -226,6 +260,8 @@
|
||||
session[key] = $scope.localeDate(value);
|
||||
} else if (key.match(/^(_startTime|_updateTime)$/)) {
|
||||
session[key] = _stToStr(value);
|
||||
} else if (key.match(/^(_u2fKeyHandle|_u2fUserKey)$/)) {
|
||||
session[key] = '########';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -315,7 +351,7 @@
|
||||
};
|
||||
$scope.currentScope = scope;
|
||||
sessionId = scope.$modelValue.session;
|
||||
$http.get(scriptname + "sessions/" + sessionType + "/" + sessionId).then(function(response) {
|
||||
$http.get(scriptname + "u2f/" + sessionType + "/" + sessionId).then(function(response) {
|
||||
return $scope.currentSession = transformSession(response.data);
|
||||
});
|
||||
return $scope.showT = false;
|
||||
@ -363,7 +399,7 @@
|
||||
} else {
|
||||
over = 0;
|
||||
}
|
||||
return $http.get(scriptname + "sessions/" + sessionType + "?" + query).then(function(response) {
|
||||
return $http.get(scriptname + "u2f/" + sessionType + "?" + query).then(function(response) {
|
||||
var data, i, len, n, ref;
|
||||
data = response.data;
|
||||
if (data.result) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -33,6 +33,7 @@
|
||||
"addSamlAttribute":"إضافة صفة",
|
||||
"addSPSamlPartner":"إضافة SP SAML",
|
||||
"addSrvCasPartner":"إضافة سرفر كاس",
|
||||
"addU2FKey":"Add U2F key",
|
||||
"addVhost":"إضافة خادم افتراضي",
|
||||
"adParams":"معاييرأكتيف ديريكتوري",
|
||||
"ADPwdExpireWarning":"تحذير انتهاء صلاحية كلمة المرور",
|
||||
@ -728,6 +729,7 @@
|
||||
"u2fActivation":"تفعيل",
|
||||
"u2fAuthnLevel":"U2F مستوى إثبات الهوية",
|
||||
"u2fSelfRegistration":"التسجيل الذاتي",
|
||||
"u2fSessions":"U2F sessions explorer",
|
||||
"uid":"المعرف",
|
||||
"unknownAttrOrMacro":"سمة غير معروفة أو ماكرو",
|
||||
"unknownError":"خطأ غير معروف",
|
||||
@ -752,6 +754,7 @@
|
||||
"values":"القيم",
|
||||
"variables":"المتغيرات",
|
||||
"version":"الإصدار",
|
||||
"verifyU2FKey":"Verify U2F key",
|
||||
"vhostAliases":"اسماء مستعارة",
|
||||
"vhostAuthnLevel":"مستوى إثبات الهوية واجب",
|
||||
"vhostHttps":"إتش تي تي بي س",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"addSamlAttribute":"Add attribute",
|
||||
"addSPSamlPartner":"Add SAML SP",
|
||||
"addSrvCasPartner":"Add CAS server",
|
||||
"addU2FKey":"Add U2F key",
|
||||
"addVhost":"Add virtualhost",
|
||||
"adParams":"Active Directory Parameters",
|
||||
"ADPwdExpireWarning":"Password expire warning",
|
||||
@ -728,6 +729,7 @@
|
||||
"u2fActivation":"Activation",
|
||||
"u2fAuthnLevel":"U2F authentication level",
|
||||
"u2fSelfRegistration":"Self registration",
|
||||
"u2fSessions":"U2F sessions explorer",
|
||||
"uid":"Identifier",
|
||||
"unknownAttrOrMacro":"Unknown attribute or macro",
|
||||
"unknownError":"Unknown error",
|
||||
@ -752,6 +754,7 @@
|
||||
"values":"Values",
|
||||
"variables":"Variables",
|
||||
"version":"Version",
|
||||
"verifyU2FKey":"Verify U2F key",
|
||||
"vhostAliases":"Aliases",
|
||||
"vhostAuthnLevel":"Required authentication level",
|
||||
"vhostHttps":"HTTPS",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"addSamlAttribute":"Ajouter un attribut",
|
||||
"addSPSamlPartner":"Ajouter un SP SAML",
|
||||
"addSrvCasPartner":"Ajouter un serveur CAS",
|
||||
"addU2FKey":"Ajouter une clef U2F",
|
||||
"addVhost":"Ajouter un hôte virtuel",
|
||||
"adParams":"Paramètres Active Directory",
|
||||
"ADPwdExpireWarning":"Avertissement avant expiration du mot de passe",
|
||||
@ -728,6 +729,7 @@
|
||||
"u2fActivation":"Activation",
|
||||
"u2fAuthnLevel":"Niveau d'authentification U2F",
|
||||
"u2fSelfRegistration":"Auto-enregistrement",
|
||||
"u2fSessions":"Explorateur de sessions U2F",
|
||||
"uid":"Identifiant",
|
||||
"unknownAttrOrMacro":"Attribut ou macro inconnu",
|
||||
"unknownError":"Erreur inconnue",
|
||||
@ -751,6 +753,7 @@
|
||||
"value":"Valeur",
|
||||
"values":"Valeurs",
|
||||
"variables":"Variables",
|
||||
"verifyU2FKey":"Vérifier la clef U2F",
|
||||
"version":"Version",
|
||||
"vhostAliases":"Alias",
|
||||
"vhostAuthnLevel":"Niveau d'authentication requis",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"addSamlAttribute":"Aggiungi attributo",
|
||||
"addSPSamlPartner":"Aggiungi SAML SP",
|
||||
"addSrvCasPartner":"Aggiungi server CAS",
|
||||
"addU2FKey":"Add U2F key",
|
||||
"addVhost":"Aggiungi virtualhost",
|
||||
"adParams":"Parametri di Active Directory",
|
||||
"ADPwdExpireWarning":"Avviso di scadenza password",
|
||||
@ -728,6 +729,7 @@
|
||||
"u2fActivation":"Attivazione",
|
||||
"u2fAuthnLevel":"Livello di autenticazione U2F",
|
||||
"u2fSelfRegistration":"Auto-registrazione",
|
||||
"u2fSessions":"U2F sessions explorer",
|
||||
"uid":"Identificatore",
|
||||
"unknownAttrOrMacro":"Attributo o macro sconosciuti",
|
||||
"unknownError":"Errore sconosciuto",
|
||||
@ -752,6 +754,7 @@
|
||||
"values":"Valori",
|
||||
"variables":"Variabili",
|
||||
"version":"Versioni",
|
||||
"verifyU2FKey":"Verify U2F key",
|
||||
"vhostAliases":"Alias",
|
||||
"vhostAuthnLevel":"Livello di autenticazione richiesto",
|
||||
"vhostHttps":"HTTPS",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"addSamlAttribute":"Thêm thuộc tính",
|
||||
"addSPSamlPartner":"Thêm SAML SP",
|
||||
"addSrvCasPartner":"Thêm máy chủ CAS",
|
||||
"addU2FKey":"Add U2F key",
|
||||
"addVhost":"Thêm host ảo",
|
||||
"adParams":"Tham số của Active Directory",
|
||||
"ADPwdExpireWarning":"Cảnh báo mật khẩu hết hạn",
|
||||
@ -728,6 +729,7 @@
|
||||
"u2fActivation":"Kích hoạt",
|
||||
"u2fAuthnLevel":"Mức xác thực U2F",
|
||||
"u2fSelfRegistration":"Tự đăng ký ",
|
||||
"u2fSessions":"U2F sessions explorer",
|
||||
"uid":"Trình định danh",
|
||||
"unknownAttrOrMacro":"Thuộc tính hoặc macro chưa xác định",
|
||||
"unknownError":"Lỗi không xác định",
|
||||
@ -752,6 +754,7 @@
|
||||
"values":"Giá trị",
|
||||
"variables":"biến",
|
||||
"version":"Phiên bản",
|
||||
"verifyU2FKey":"Verify U2F key",
|
||||
"vhostAliases":"Bí danh",
|
||||
"vhostAuthnLevel":"Mức xác thực bắt buộc",
|
||||
"vhostHttps":"HTTPS",
|
||||
|
@ -15,7 +15,7 @@
|
||||
<div class="navbar navbar-default">
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav" role="grid">
|
||||
<li><a id="a-persistent" href="#/persistent" role="row"><i class="glyphicon glyphicon-exclamation-sign"></i> {{translate('persistentSessions')}}</a></li>
|
||||
<li><a id="a-persistent" href="#/persistent" role="row"><i class="glyphicon glyphicon-exclamation-sign"></i> {{translate('u2fSessions')}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,7 +41,12 @@
|
||||
<div class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
|
||||
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-if="currentSession" ng-repeat="button in menu.session" ng-include="'menubutton.html'"></li>
|
||||
<!--
|
||||
<li ng-if="currentSession" ng-repeat="button in menu.addU2FKey" 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.delU2FKey" 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>
|
||||
|
@ -116,7 +116,7 @@ sub run {
|
||||
elsif ( $err == 0 ) {
|
||||
return $self->p->sendError( $req, "noU2FKeyFound" );
|
||||
}
|
||||
my $challenge = $req->data->{crypter}->authenticationChallenge;
|
||||
my $challenge = $req->datas->{crypter}->authenticationChallenge;
|
||||
return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
|
||||
}
|
||||
if ( $action eq 'signature' ) {
|
||||
@ -133,7 +133,7 @@ sub run {
|
||||
return $self->p->sendError( $req, "noU2FKeyFound" );
|
||||
}
|
||||
my $res =
|
||||
( $req->data->{crypter}->authenticationVerify($resp) ? 1 : 0 );
|
||||
( $req->datas->{crypter}->authenticationVerify($resp) ? 1 : 0 );
|
||||
return [
|
||||
200, [ 'Content-Type' => 'application/json' ],
|
||||
[qq'{"result":$res}']
|
||||
@ -150,11 +150,11 @@ sub loadUser {
|
||||
unless ( $kh and $uk ) {
|
||||
return 0;
|
||||
}
|
||||
$req->data->{crypter} = $self->crypter(
|
||||
$req->datas->{crypter} = $self->crypter(
|
||||
keyHandle => $self->decode_base64url($kh),
|
||||
publicKey => $self->decode_base64url($uk)
|
||||
);
|
||||
unless ( $req->data->{crypter} ) {
|
||||
unless ( $req->datas->{crypter} ) {
|
||||
my $error = Crypt::U2F::Server::Simple::lastError();
|
||||
return ( -1, $error );
|
||||
}
|
||||
|
@ -197,7 +197,7 @@
|
||||
"touchU2fDevice": "Please touch the flashing U2F device now.",
|
||||
"u2fFailed": "U2F verification failed. Retry or contact your administrator",
|
||||
"u2fPermission": "You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.",
|
||||
"u2fRegistered": "Your key is registered. Click to verify.",
|
||||
"u2fRegistered": "Your key has been registered.",
|
||||
"u2fUnregistered": "Your key has been unregistered.",
|
||||
"u2fSuccess": "Your key is successfully tested",
|
||||
"u2fWelcome": "U2F device management",
|
||||
|
@ -197,7 +197,7 @@
|
||||
"touchU2fDevice": "Poser votre doigt sur le périphérique U2F",
|
||||
"u2fFailed": "La vérification U2F a échoué, réessayez ou contactez votre administrateur",
|
||||
"u2fPermission": "Il est possible qu'on vous demande d'autoriser le site à accéder à votre clef. Après votre accord, la clef clignotera.",
|
||||
"u2fRegistered": "Votre clef est enregistrée. Cliquez sur vérifier",
|
||||
"u2fRegistered": "Votre clef a été enregistrée.",
|
||||
"u2fUnregistered": "Votre clef a été supprimée.",
|
||||
"u2fSuccess": "Votre clef est vérifiée",
|
||||
"u2fWelcome": "Gestion du périphérique U2F",
|
||||
@ -206,7 +206,7 @@
|
||||
"upgradeSession":"Se réauthentifier",
|
||||
"user":"Utilisateur",
|
||||
"useYubikey":"utilisez votre Yubikey",
|
||||
"verify": "Verifier",
|
||||
"verify": "Vérifier",
|
||||
"wait":"Attendre",
|
||||
"warning":"Attention",
|
||||
"welcomeOnPortal":"Bienvenue sur votre portail d'authentification sécurisée.",
|
||||
|
Loading…
Reference in New Issue
Block a user