Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2020-04-08 23:25:27 +02:00
commit 01424a71ac
32 changed files with 409 additions and 42 deletions

View File

@ -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

View File

@ -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)

View File

@ -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 ();

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -884,6 +884,7 @@
"timeoutActivityInterval":"فترة تحديث الجلسات",
"title":"Title",
"tokenUseGlobalStorage":"استخدام سعة التخزين العامة",
"tooltip":"Tooltip",
"totp2f":"TOTP",
"totp2fActivation":"تفعيل",
"totp2fAuthnLevel":"TOTP authentication level",

View File

@ -884,6 +884,7 @@
"timeoutActivityInterval":"Sessions update interval",
"title":"Title",
"tokenUseGlobalStorage":"Use global storage",
"tooltip":"Tooltip",
"totp2f":"TOTP",
"totp2fActivation":"Activation",
"totp2fAuthnLevel":"TOTP authentication level",

View File

@ -884,6 +884,7 @@
"timeoutActivityInterval":"Sessions update interval",
"title":"Title",
"tokenUseGlobalStorage":"Use global storage",
"tooltip":"Tooltip",
"totp2f":"TOTP",
"totp2fActivation":"Activation",
"totp2fAuthnLevel":"TOTP authentication level",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -884,6 +884,7 @@
"timeoutActivityInterval":"Sessions update interval",
"title":"Title",
"tokenUseGlobalStorage":"Use global storage",
"tooltip":"Tooltip",
"totp2f":"TOTP",
"totp2fActivation":"激活",
"totp2fAuthnLevel":"TOTP authentication level",

View File

@ -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

View File

@ -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;

View File

@ -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->{$_} );

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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">

View File

@ -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

View File

@ -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(

View File

@ -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' );

View File

@ -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);

View File

@ -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() );

View File

@ -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" : {