Merge branch 'v2.0'
This commit is contained in:
commit
01424a71ac
|
@ -130,10 +130,14 @@
|
|||
.\"
|
||||
.IX Title "llng-fastcgi-server 8"
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
.TH llng-fastcgi-server 8 "2020-04-03" "perl v5.26.1" "User Contributed Perl Documentation"
|
||||
=======
|
||||
.TH llng-fastcgi-server 8 "2020-04-01" "perl v5.26.1" "User Contributed Perl Documentation"
|
||||
>>>>>>> v2.0
|
||||
=======
|
||||
.TH llng-fastcgi-server 8 "2020-04-05" "perl v5.26.1" "User Contributed Perl Documentation"
|
||||
>>>>>>> v2.0
|
||||
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||
.\" way too many mistakes in technical documents.
|
||||
.if n .ad l
|
||||
|
|
|
@ -190,7 +190,7 @@ staticPrefix = __PORTALSTATICDIR__
|
|||
templateDir = __PORTALTEMPLATESDIR__
|
||||
|
||||
; languages: available languages for portal interface
|
||||
languages = en, fr, vi, it, ar, de, fi, tr
|
||||
languages = en, fr, vi, it, ar, de, fi, tr, pl
|
||||
|
||||
; II - Optional parameters (overwrite configuration)
|
||||
|
||||
|
|
|
@ -100,9 +100,7 @@ sub getAccepted {
|
|||
$self->_execute(
|
||||
"SELECT * FROM "
|
||||
. $self->dbiTable
|
||||
. " WHERE done IS NOT NULL AND uid=?"
|
||||
. ( $ref ? " AND ref=?" : '' )
|
||||
. " ORDER BY date",
|
||||
. " WHERE uid=? AND ref=? ORDER BY date",
|
||||
$uid,
|
||||
( $ref ? $ref : () )
|
||||
) or return ();
|
||||
|
|
|
@ -35,8 +35,8 @@ has fileNameSeparator => ( is => 'rw', default => '_' );
|
|||
# If $ref is set, returns only notification corresponding to this reference.
|
||||
sub get {
|
||||
my ( $self, $uid, $ref ) = @_;
|
||||
my $ext = $self->extension;
|
||||
return () unless ($uid);
|
||||
my $ext = $self->extension;
|
||||
my $fns = $self->{fileNameSeparator};
|
||||
my $identifier = &getIdentifier( $self, $uid, $ref );
|
||||
|
||||
|
@ -61,11 +61,12 @@ sub get {
|
|||
sub getAccepted {
|
||||
my ( $self, $uid, $ref ) = @_;
|
||||
return () unless ($uid);
|
||||
my $ext = $self->extension;
|
||||
my $fns = $self->{fileNameSeparator};
|
||||
my $identifier = &getIdentifier( $self, $uid, $ref );
|
||||
|
||||
opendir D, $self->{dirName};
|
||||
my @notif = grep /^\d{8}${fns}${identifier}\S*\.done$/, readdir(D);
|
||||
my @notif = grep /^\d{8}${fns}${identifier}\S*\.(?:done|$ext)$/, readdir(D);
|
||||
closedir D;
|
||||
|
||||
my $files;
|
||||
|
|
|
@ -95,8 +95,7 @@ sub getAccepted {
|
|||
|
||||
my $filter =
|
||||
'(&(objectClass=applicationProcess)(description={done}*)'
|
||||
. "(description={uid}$uid)"
|
||||
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
|
||||
. "(description={uid}$uid)(description={ref}$ref))";
|
||||
my @entries = _search( $self, $filter );
|
||||
|
||||
my $result;
|
||||
|
|
|
@ -312,6 +312,7 @@ llapp.controller 'TreeCtrl', [
|
|||
data:
|
||||
description: "New app description"
|
||||
uri: "https://test.example.com/"
|
||||
tooltip: "New app tooltip"
|
||||
logo: "network.png"
|
||||
display: "auto"
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
<th><span trspan="uri"></span></th>
|
||||
<td><input id="iuri" class="form-control" ng-model="currentNode.data.uri"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span trspan="tooltip"></span></th>
|
||||
<td><input id="itooltip" class="form-control" ng-model="currentNode.data.tooltip"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span trspan="logo"></span></th>
|
||||
<td>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Generated by CoffeeScript 1.12.8
|
||||
// Generated by CoffeeScript 1.12.7
|
||||
|
||||
/*
|
||||
LemonLDAP::NG Manager client
|
||||
|
@ -362,6 +362,7 @@ This file contains:
|
|||
data: {
|
||||
description: "New app description",
|
||||
uri: "https://test.example.com/",
|
||||
tooltip: "New app tooltip",
|
||||
logo: "network.png",
|
||||
display: "auto"
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"فترة تحديث الجلسات",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"استخدام سعة التخزين العامة",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"تفعيل",
|
||||
"totp2fAuthnLevel":"TOTP authentication level",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Sessions update interval",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"Use global storage",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Activation",
|
||||
"totp2fAuthnLevel":"TOTP authentication level",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Sessions update interval",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"Use global storage",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Activation",
|
||||
"totp2fAuthnLevel":"TOTP authentication level",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Intervalle de mise à jour des sessions",
|
||||
"title":"Titre",
|
||||
"tokenUseGlobalStorage":"Utiliser le cache global",
|
||||
"tooltip":"Info-bulle",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Activation",
|
||||
"totp2fAuthnLevel":"Niveau d'authentification TOTP",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Intervallo di aggiornamento delle sessioni",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"Utilizza lo storage globale",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Attivazione",
|
||||
"totp2fAuthnLevel":"Livello di autenticazione TOTP",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Oturum güncellenme sıklığı",
|
||||
"title":"Başlık",
|
||||
"tokenUseGlobalStorage":"Global depolamayı kullan",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Aktivasyon",
|
||||
"totp2fAuthnLevel":"TOTP doğrulama seviyesi",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Khoảng thời gian cập nhật phiên",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"Sử dụng lưu trữ toàn cục",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"Kích hoạt",
|
||||
"totp2fAuthnLevel":"TOTP authentication level",
|
||||
|
|
|
@ -884,6 +884,7 @@
|
|||
"timeoutActivityInterval":"Sessions update interval",
|
||||
"title":"Title",
|
||||
"tokenUseGlobalStorage":"Use global storage",
|
||||
"tooltip":"Tooltip",
|
||||
"totp2f":"TOTP",
|
||||
"totp2fActivation":"激活",
|
||||
"totp2fAuthnLevel":"TOTP authentication level",
|
||||
|
|
|
@ -654,6 +654,7 @@ t/68-Impersonation-with-TOTP.t
|
|||
t/68-Impersonation.t
|
||||
t/69-FavApps.t
|
||||
t/70-2F-TOTP-8-with-global-storage.t
|
||||
t/70-2F-TOTP-and-U2F-with-TTL-and-JSON.t
|
||||
t/70-2F-TOTP-with-History.t
|
||||
t/70-2F-TOTP-with-TTL-and-JSON.t
|
||||
t/70-2F-TOTP-with-TTL-and-XML.t
|
||||
|
|
|
@ -311,10 +311,11 @@ sub _buildApplicationHash {
|
|||
my $applications;
|
||||
|
||||
# Get application items
|
||||
my $appname = $apphash->{options}->{name} || $appid;
|
||||
my $appuri = $apphash->{options}->{uri} || "";
|
||||
my $appdesc = $apphash->{options}->{description};
|
||||
my $applogo = $apphash->{options}->{logo};
|
||||
my $appname = $apphash->{options}->{name} || $appid;
|
||||
my $appuri = $apphash->{options}->{uri} || "";
|
||||
my $appdesc = $apphash->{options}->{description};
|
||||
my $applogo = $apphash->{options}->{logo};
|
||||
my $apptip = $apphash->{options}->{tooltip} || $appname;
|
||||
my $appIsFavApp = $apphash->{options}->{isFavApp} || "0";
|
||||
|
||||
# Detect sub applications
|
||||
|
@ -349,6 +350,7 @@ sub _buildApplicationHash {
|
|||
appdesc => $appdesc,
|
||||
applogo => $applogo,
|
||||
appid => $appid,
|
||||
apptip => $apptip,
|
||||
appisfav => $appIsFavApp,
|
||||
};
|
||||
$applicationHash->{applications} = $applications if $applications;
|
||||
|
|
|
@ -541,6 +541,7 @@ sub updateSession {
|
|||
# Update sessionInfo data
|
||||
## sessionInfo updated if $id defined : quite strange !!
|
||||
## See https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues/430
|
||||
$self->logger->debug("Update session $id");
|
||||
foreach ( keys %$infos ) {
|
||||
$self->logger->debug("Update sessionInfo $_");
|
||||
$self->_dump( $infos->{$_} );
|
||||
|
|
|
@ -78,7 +78,6 @@ sub _redirect {
|
|||
|
||||
sub _verify {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
my $checkLogins = $req->param('checkLogins');
|
||||
$self->logger->debug("checkLogins set") if ($checkLogins);
|
||||
|
||||
|
@ -124,18 +123,44 @@ sub _verify {
|
|||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
|
||||
if ( my $l = $self->conf->{ $self->prefix . '2fAuthnLevel' } ) {
|
||||
$self->logger->debug("Update sessionInfo with new authenticationLevel: $l");
|
||||
$self->logger->debug(
|
||||
"Update sessionInfo with new authenticationLevel: $l");
|
||||
$req->sessionInfo->{authenticationLevel} = $l;
|
||||
delete $req->sessionInfo->{groups};
|
||||
|
||||
# Compute groups & macros again with new authenticationLevel
|
||||
$req->steps( [ $self->p->groupsAndMacros, 'setLocalGroups'] );
|
||||
# Compute macros & local groups again with new authenticationLevel
|
||||
$self->logger->debug("Compute macros and local groups...");
|
||||
$req->steps( [ 'setMacros', 'setLocalGroups' ] );
|
||||
if ( my $error = $self->p->process($req) ) {
|
||||
$self->logger->debug("SFA: Process returned error: $error");
|
||||
$req->error($error);
|
||||
return $self->p->do( $req, [ sub { $error } ] );
|
||||
}
|
||||
$self->p->updateSession( $req, $req->sessionInfo );
|
||||
$self->logger->debug("De-duplicate groups...");
|
||||
$req->sessionInfo->{groups} = join $self->conf->{multiValuesSeparator},
|
||||
keys %{ {
|
||||
map { $_ => 1 } split $self->conf->{multiValuesSeparator},
|
||||
$req->sessionInfo->{groups}
|
||||
}
|
||||
};
|
||||
|
||||
$self->logger->debug("Filter macros...");
|
||||
my %macros = (
|
||||
map { $_ => $req->sessionInfo->{$_} }
|
||||
keys %{ $self->{conf}->{macros} }
|
||||
);
|
||||
|
||||
$self->logger->debug(
|
||||
"Update session with new authenticationLevel, groups, hGroups and macros"
|
||||
);
|
||||
$self->p->updateSession(
|
||||
$req,
|
||||
{
|
||||
authenticationLevel => $l,
|
||||
groups => $req->sessionInfo->{groups},
|
||||
hGroups => $req->sessionInfo->{hGroups},
|
||||
%macros
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$req->authResult(PE_SENDRESPONSE);
|
||||
|
|
|
@ -167,7 +167,7 @@ sub myNotifs {
|
|||
my ( $self, $req, $ref ) = @_;
|
||||
|
||||
if ($ref) {
|
||||
return $self->sendJSONresponse( $req, { error => 'Missing parameter' } )
|
||||
return $self->sendJSONresponse( $req, { error => 'Missing epoch parameter' } )
|
||||
unless $req->param('epoch');
|
||||
|
||||
# Retrieve notification reference=$ref with epoch
|
||||
|
|
|
@ -89,7 +89,7 @@ sub setSessionInfo {
|
|||
# @return Lemonldap::NG::Portal constant
|
||||
sub setGroups {
|
||||
my ( $self, $req ) = @_;
|
||||
my $user = $req->user || $req->sessionInfo->{ $self->conf->{whatToTrace} };
|
||||
my $user = $req->user;
|
||||
my $groups = $req->sessionInfo->{groups} || '';
|
||||
my $hGroups = $req->sessionInfo->{hGroups} || {};
|
||||
for my $grp ( keys %demoGroups ) {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -180,7 +180,7 @@
|
|||
<div class="category cat-level-<TMPL_VAR NAME="catlevel"> <TMPL_VAR NAME="catid"> card border-secondary" id="sort_<TMPL_VAR NAME="__counter__">">
|
||||
|
||||
<div class="card-header text-white bg-secondary">
|
||||
<h4 class="catname card-title"><TMPL_VAR NAME="catname"></h4>
|
||||
<h4 class="catname card-title"><TMPL_VAR NAME="catname"><span><i class="fa fa-arrows-v float-right" ></i></span></h4>
|
||||
</div>
|
||||
|
||||
<TMPL_IF applications>
|
||||
|
@ -204,8 +204,7 @@
|
|||
</div>
|
||||
</TMPL_IF>
|
||||
<div class="application <TMPL_VAR NAME="appid"> card">
|
||||
|
||||
<a href="<TMPL_VAR NAME="appuri">" title="<TMPL_VAR NAME="appname">" >
|
||||
<a href="<TMPL_VAR NAME="appuri">" title="<TMPL_VAR NAME="apptip">" >
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
|
|
|
@ -6,12 +6,12 @@ use JSON qw(from_json);
|
|||
require 't/test-lib.pm';
|
||||
|
||||
my $res;
|
||||
my $file = "$main::tmpDir/20160530_dwho_dGVzdHJlZg==.json";
|
||||
my $file = "$main::tmpDir/20160530_allusers_dGVzdHJlZg==.json";
|
||||
|
||||
open F, "> $file" or die($!);
|
||||
print F '[
|
||||
{
|
||||
"uid": "dwho",
|
||||
"uid": "allusers",
|
||||
"date": "2016-05-30",
|
||||
"reference": "testref",
|
||||
"title": "Test title",
|
||||
|
@ -104,8 +104,8 @@ ok(
|
|||
),
|
||||
"Accept notification"
|
||||
);
|
||||
$file =~ s/json$/done/;
|
||||
ok( -e $file, 'Notification was deleted' );
|
||||
|
||||
ok( -e $file, 'Notification was not deleted' );
|
||||
count(2);
|
||||
|
||||
$id = expectCookie($res);
|
||||
|
@ -216,8 +216,8 @@ ok(
|
|||
|
||||
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
|
||||
or print STDERR "$@\n" . Dumper($res);
|
||||
ok( $json->{error} eq 'Missing parameter', ' Missing parameter' )
|
||||
or explain( $json, "Missing parameter" );
|
||||
ok( $json->{error} eq 'Missing epoch parameter', ' Missing epoch parameter' )
|
||||
or explain( $json, "Missing epoch parameter" );
|
||||
count(3);
|
||||
|
||||
# Bad request
|
||||
|
|
|
@ -235,8 +235,8 @@ m%<span notif=\'testref\' epoch=\'(\d{10})\' class="btn btn-success" role="butto
|
|||
);
|
||||
ok( $json = eval { from_json( $res->[2]->[0] ) }, 'Response is JSON' )
|
||||
or print STDERR "$@\n" . Dumper($res);
|
||||
ok( $json->{error} eq 'Missing parameter', ' Missing parameter' )
|
||||
or explain( $json, "Missing parameter" );
|
||||
ok( $json->{error} eq 'Missing epoch parameter', ' Missing epoch parameter' )
|
||||
or explain( $json, "Missing epoch parameter" );
|
||||
|
||||
# Bad request
|
||||
ok(
|
||||
|
|
|
@ -36,6 +36,7 @@ my $client = LLNG::Manager::Test->new( {
|
|||
contextSwitchingStopWithLogout => 0,
|
||||
checkUser => 1,
|
||||
notification => 1,
|
||||
tokenUseGlobalStorage => 1,
|
||||
notificationStorage => 'File',
|
||||
notificationStorageOptions => { dirName => $main::tmpDir },
|
||||
}
|
||||
|
@ -392,8 +393,8 @@ ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
|
|||
or explain( $res->[2]->[0], 'trspan="checkUser"' );
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">authMode</td>%, 'Found macro authMode' )
|
||||
or explain( $res->[2]->[0], 'Macro Key authMode' );
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">TOTP</td>%, 'Found TOTP' )
|
||||
or explain( $res->[2]->[0], 'Macro Value TOTP' );
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">TOTP</td>%, 'Found macro value "TOTP"' )
|
||||
or explain( $res->[2]->[0], 'Macro value "TOTP"' );
|
||||
count(4);
|
||||
|
||||
# Request not connected user
|
||||
|
@ -411,7 +412,8 @@ ok(
|
|||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/checkuser', 'user', 'url' );
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="checkUserComputeSession">%, 'Found trspan="checkUserComputeSession"' )
|
||||
ok( $res->[2]->[0] =~ m%<span trspan="checkUserComputeSession">%,
|
||||
'Found trspan="checkUserComputeSession"' )
|
||||
or explain( $res->[2]->[0], 'trspan="checkUserComputeSession"' );
|
||||
ok( $res->[2]->[0] =~ m%<td scope="row">authMode</td>%, 'Found macro authMode' )
|
||||
or explain( $res->[2]->[0], 'Macro Key authMode' );
|
||||
|
|
|
@ -61,7 +61,15 @@ ok( $res->[2]->[0] =~ qr%<span id="languages"></span>%, 'Found language flags' )
|
|||
expectAuthenticatedAs( $res, 'rtyler' );
|
||||
ok( $res->[2]->[0] !~ m%contextSwitching_ON%, 'Connected as dwho' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(3);
|
||||
ok( $res->[2]->[0] =~ qr%href="http://test1\.example\.com/" title="Application Test 1"%, 'Found test1 & title' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok( $res->[2]->[0] =~ qr%href="http://test2\.example\.com/" title="A nice application!"%, 'Found test2 & title' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
my @appdesc = ($res->[2]->[0] =~ qr%class="appdesc%);
|
||||
ok( @appdesc == 1 , 'Found only one description' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(6);
|
||||
|
||||
$client->logout($id);
|
||||
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
|
||||
require 't/test-lib.pm';
|
||||
my $maintests = 39;
|
||||
|
||||
SKIP: {
|
||||
eval {
|
||||
require Convert::Base32;
|
||||
require Crypt::U2F::Server;
|
||||
require Authen::U2F::Tester;
|
||||
};
|
||||
if ( $@ or $Crypt::U2F::Server::VERSION < 0.42 ) {
|
||||
skip 'Missing libraries', $maintests;
|
||||
}
|
||||
require Lemonldap::NG::Common::TOTP;
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
totp2fSelfRegistration => 1,
|
||||
totp2fActivation => 1,
|
||||
totp2fTTL => 180,
|
||||
u2fSelfRegistration => 1,
|
||||
u2fActivation => 1,
|
||||
u2fTTL => 60,
|
||||
sfRemovedMsgRule => 1,
|
||||
sfRemovedUseNotif => 1,
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
notification => 1,
|
||||
notificationStorage => 'File',
|
||||
notificationStorageOptions => { dirName => 't' },
|
||||
oldNotifFormat => 0,
|
||||
}
|
||||
}
|
||||
);
|
||||
my $res;
|
||||
|
||||
# Try to authenticate
|
||||
# -------------------
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
my $id = expectCookie($res);
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/2fregisters/totp',
|
||||
cookie => "lemonldap=$id",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Form registration'
|
||||
);
|
||||
ok( $res->[2]->[0] =~ /totpregistration\.(?:min\.)?js/, 'Found TOTP js' );
|
||||
ok(
|
||||
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
|
||||
'Found custom Main Logo'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
# JS query
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/2fregisters/totp/getkey', IO::String->new(''),
|
||||
cookie => "lemonldap=$id",
|
||||
length => 0,
|
||||
),
|
||||
'Get new key'
|
||||
);
|
||||
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
||||
ok( not($@), 'Content is JSON' )
|
||||
or explain( $res->[2]->[0], 'JSON content' );
|
||||
my ( $key, $token );
|
||||
ok( $key = $res->{secret}, 'Found secret' );
|
||||
ok( $token = $res->{token}, 'Found token' );
|
||||
$key = Convert::Base32::decode_base32($key);
|
||||
|
||||
# Post code
|
||||
my $code;
|
||||
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
||||
'Code' );
|
||||
ok( $code =~ /^\d{6}$/, 'Code contains 6 digits' );
|
||||
my $s = "code=$code&token=$token&TOTPName=myTOTP";
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/2fregisters/totp/verify',
|
||||
IO::String->new($s),
|
||||
length => length($s),
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
eval { $res = JSON::from_json( $res->[2]->[0] ) };
|
||||
ok( not($@), 'Content is JSON' )
|
||||
or explain( $res->[2]->[0], 'JSON content' );
|
||||
ok( $res->{result} == 1, 'Key is registered' );
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/2fregisters/u',
|
||||
cookie => "lemonldap=$id",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Form registration'
|
||||
);
|
||||
ok( $res->[2]->[0] =~ /u2fregistration\.(?:min\.)?js/, 'Found U2F js' );
|
||||
ok(
|
||||
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
|
||||
'Found custom Main Logo'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
# Ajax registration request
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/2fregisters/u/register', IO::String->new(''),
|
||||
accept => 'application/json',
|
||||
cookie => "lemonldap=$id",
|
||||
length => 0,
|
||||
),
|
||||
'Get registration challenge'
|
||||
);
|
||||
expectOK($res);
|
||||
my $data;
|
||||
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||
ok( not($@), ' Content is JSON' )
|
||||
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||
ok( ( $data->{challenge} and $data->{appId} ), ' Get challenge and appId' )
|
||||
or explain( $data, 'challenge and appId' );
|
||||
|
||||
# Build U2F tester
|
||||
my $tester = Authen::U2F::Tester->new(
|
||||
certificate => Crypt::OpenSSL::X509->new_from_string(
|
||||
'-----BEGIN CERTIFICATE-----
|
||||
MIIB6DCCAY6gAwIBAgIJAJKuutkN2sAfMAoGCCqGSM49BAMCME8xCzAJBgNVBAYT
|
||||
AlVTMQ4wDAYDVQQIDAVUZXhhczEaMBgGA1UECgwRVW50cnVzdGVkIFUyRiBPcmcx
|
||||
FDASBgNVBAMMC3ZpcnR1YWwtdTJmMB4XDTE4MDMyODIwMTc1OVoXDTI3MTIyNjIw
|
||||
MTc1OVowTzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRowGAYDVQQKDBFV
|
||||
bnRydXN0ZWQgVTJGIE9yZzEUMBIGA1UEAwwLdmlydHVhbC11MmYwWTATBgcqhkjO
|
||||
PQIBBggqhkjOPQMBBwNCAAQTij+9mI1FJdvKNHLeSQcOW4ob3prvIXuEGJMrQeJF
|
||||
6OYcgwxrVqsmNMl5w45L7zx8ryovVOti/mtqkh2pQjtpo1MwUTAdBgNVHQ4EFgQU
|
||||
QXKKf+rrZwA4WXDCU/Vebe4gYXEwHwYDVR0jBBgwFoAUQXKKf+rrZwA4WXDCU/Ve
|
||||
be4gYXEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiEAiCdOEmw5
|
||||
hknzHR1FoyFZKRrcJu17a1PGcqTFMJHTC70CIHeCZ8KVuuMIPjoofQd1l1E221rv
|
||||
RJY1Oz1fUNbrIPsL
|
||||
-----END CERTIFICATE-----', Crypt::OpenSSL::X509::FORMAT_PEM()
|
||||
),
|
||||
key => Crypt::PK::ECC->new(
|
||||
\'-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIOdbZw1swQIL+RZoDQ9zwjWY5UjA1NO81WWjwbmznUbgoAoGCCqGSM49
|
||||
AwEHoUQDQgAEE4o/vZiNRSXbyjRy3kkHDluKG96a7yF7hBiTK0HiRejmHIMMa1ar
|
||||
JjTJecOOS+88fK8qL1TrYv5rapIdqUI7aQ==
|
||||
-----END EC PRIVATE KEY-----'
|
||||
),
|
||||
);
|
||||
my $r = $tester->register( $data->{appId}, $data->{challenge} );
|
||||
ok( $r->is_success, ' Good challenge value' )
|
||||
or diag( $r->error_message );
|
||||
|
||||
my $registrationData = JSON::to_json( {
|
||||
clientData => $r->client_data,
|
||||
errorCode => 0,
|
||||
registrationData => $r->registration_data,
|
||||
version => "U2F_V2"
|
||||
}
|
||||
);
|
||||
my ( $host, $url, $query );
|
||||
$query = Lemonldap::NG::Common::FormEncode::build_urlencoded(
|
||||
registration => $registrationData,
|
||||
challenge => $res->[2]->[0],
|
||||
);
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/2fregisters/u/registration', IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'application/json',
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Push registration data'
|
||||
);
|
||||
expectOK($res);
|
||||
eval { $data = JSON::from_json( $res->[2]->[0] ) };
|
||||
ok( not($@), ' Content is JSON' )
|
||||
or explain( [ $@, $res->[2] ], 'JSON content' );
|
||||
ok( $data->{result} == 1, 'Key is registered' )
|
||||
or explain( $data, '"result":1' );
|
||||
|
||||
# Try to sign-in
|
||||
$client->logout($id);
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
( $host, $url, $query ) = expectForm( $res, undef, '/2fchoice', 'token' );
|
||||
$query .= '&sf=totp';
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/2fchoice',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post TOTP choice'
|
||||
);
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/totp2fcheck', 'token' );
|
||||
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
||||
'Code' );
|
||||
$query =~ s/code=/code=$code/;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/totp2fcheck', IO::String->new($query),
|
||||
length => length($query),
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
$id = expectCookie($res);
|
||||
$client->logout($id);
|
||||
|
||||
# Skipping time until TOTP token expiration
|
||||
Time::Fake->offset("+2m");
|
||||
|
||||
# Try to sign-in
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/totp2fcheck', 'token' );
|
||||
|
||||
# Generate TOTP with LLNG
|
||||
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
|
||||
'LLNG Code' );
|
||||
$query =~ s/code=/code=$code/;
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/totp2fcheck', IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input type="hidden" name="reference1x1" value="RemoveSF-(\d{10})">%,
|
||||
'Notification reference found'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok( time() + 120 <= $1 && $1 <= time() + 125, 'Right reference found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<p class="notifText">1 expired second factor\(s\) has/have been removed!</p>%,
|
||||
'Notification message found'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
$id = expectCookie($res);
|
||||
$client->logout($id);
|
||||
|
||||
# Skipping time until TOTP token expiration
|
||||
Time::Fake->offset("+5m");
|
||||
|
||||
# Try to sign-in
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new('user=dwho&password=dwho'),
|
||||
length => 23,
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input type="hidden" name="reference1x1" value="RemoveSF-(\d{10})">%,
|
||||
'Notification reference found'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok( time() + 120 <= $1 && $1 <= time() + 125, 'Right reference found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input type="hidden" name="reference1x2" value="RemoveSF-(\d{10})">%,
|
||||
'Notification reference found'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
ok( time() + 300 <= $1 && $1 <= time() + 305, 'Right reference found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
my @notifs =
|
||||
( $res->[2]->[0] =~
|
||||
m%<p class="notifText">1 expired second factor\(s\) has/have been removed!</p>%gs
|
||||
);
|
||||
ok( 2 == @notifs, '2 notifications found' )
|
||||
or print STDERR Dumper( $res->[2]->[0] );
|
||||
}
|
||||
|
||||
count($maintests);
|
||||
system 'rm -f t/*_dwho_*.json';
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
|
@ -8,17 +8,19 @@
|
|||
"display" : "auto",
|
||||
"logo" : "demo.png",
|
||||
"name" : "Application Test 1",
|
||||
"uri" : "http://test1.example.com/"
|
||||
"uri" : "http://test1.example.com/",
|
||||
"tooltip": ""
|
||||
},
|
||||
"type" : "application"
|
||||
},
|
||||
"test2" : {
|
||||
"options" : {
|
||||
"description" : "The same simple application displaying authenticated user",
|
||||
"description" : "",
|
||||
"display" : "auto",
|
||||
"logo" : "thumbnail.png",
|
||||
"name" : "Application Test 2",
|
||||
"uri" : "http://test2.example.com/"
|
||||
"uri" : "http://test2.example.com/",
|
||||
"tooltip": "A nice application!"
|
||||
},
|
||||
"type" : "application"
|
||||
},
|
||||
|
@ -74,7 +76,7 @@
|
|||
"LockDirectory": "t/sessions/lock",
|
||||
"generateModule": "Lemonldap::NG::Common::Apache::Session::Generate::SHA256"
|
||||
},
|
||||
"groups": { "su":"$uid and $uid eq \"rtyler\"", "test_su": "$uid and $uid eq \"rtyler\"", "su_test": "$uid and $uid eq \"rtyler\"" },
|
||||
"groups": { "su":"$uid and $uid =~ /(?:rtyler|dwho)/", "test_su": "$uid and $uid eq \"rtyler\"", "su_test": "$uid and $uid eq \"rtyler\"" },
|
||||
"key": "qwertyui",
|
||||
"locationRules": {
|
||||
"auth.example.com" : {
|
||||
|
|
Loading…
Reference in New Issue