This commit is contained in:
Christophe Maudoux 2019-06-14 21:05:00 +02:00
parent 5fbff01b27
commit fde6ff2cc8
28 changed files with 244 additions and 9 deletions

View File

@ -277,6 +277,7 @@ sub defaultValues {
'samlSPSSODescriptorWantAssertionsSigned' => 1,
'securedCookie' => 0,
'sfEngine' => '::2F::Engines::Default',
'sfRemovedMsg' => 0,
'sfRequired' => 0,
'showLanguages' => 1,
'slaveAuthnLevel' => 2,

View File

@ -3186,6 +3186,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => '::2F::Engines::Default',
'type' => 'text'
},
'sfRemovedMsg' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'sfRequired' => {
'default' => 0,
'type' => 'boolOrExpr'

View File

@ -2575,6 +2575,12 @@ sub attributes {
help => 'secondfactor.html',
documentation => 'Second factor required',
},
sfRemovedMsg => {
type => 'boolOrExpr',
default => 0,
help => 'secondfactor.html',
documentation => 'Display a message if at leat one expired SF has been removed',
},
available2F => {
type => 'text',
default => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey',

View File

@ -751,7 +751,7 @@ sub tree {
'yubikey2fTTL',
],
},
'sfRequired',
'sfRequired', 'sfRemovedMsg',
]
},
{

View File

@ -729,6 +729,7 @@
"sessionTitle":"محتوى الجلسة",
"sfaTitle":"Second Factors Authentication",
"sfRequired":"Require 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"عرض",
"showHelp":"عرض المساعدة",
"showLanguages":"Show languages choice",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Session content",
"sfaTitle":"Second Factors Authentication",
"sfRequired":"Require 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"Show",
"showHelp":"Show help",
"showLanguages":"Show languages choice",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Session content",
"sfaTitle":"Second Factors Authentication",
"sfRequired":"Require 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"Show",
"showHelp":"Show help",
"showLanguages":"Show languages choice",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Contenu de la session",
"sfaTitle":"Seconds Facteurs d'Authentification",
"sfRequired":"Exiger 2FA",
"sfRemovedMsg":"Afficher un message si un SF expiré est supprimé",
"show":"Montrer",
"showHelp":"Montrer l'aide",
"showLanguages":"Afficher le choix des langues",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Contenuto della sessione",
"sfaTitle":"Autenticazione a due fattori",
"sfRequired":"Richiedi 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"Mostra",
"showHelp":"Mostra aiuto",
"showLanguages":"Mostra la scelta delle lingue",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Nội dung phiên",
"sfaTitle":"Second Factors Authentication",
"sfRequired":"Require 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"Hiển thị",
"showHelp":"Hiển thị trợ giúp",
"showLanguages":"Show languages choice",

View File

@ -729,6 +729,7 @@
"sessionTitle":"Session content",
"sfaTitle":"Second Factors Authentication",
"sfRequired":"Require 2FA",
"sfRemovedMsg":"Display a message if an expired SF is removed",
"show":"Show",
"showHelp":"Show help",
"showLanguages":"Show languages choice",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,6 +18,7 @@ use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_SENDRESPONSE
PE_TOKENEXPIRED
PE_INFO
);
our $VERSION = '2.0.5';
@ -26,11 +27,10 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
has sfModules => ( is => 'rw', default => sub { [] } );
has sfModules => ( is => 'rw', default => sub { [] } );
has sfRModules => ( is => 'rw', default => sub { [] } );
has sfReq => ( is => 'rw' );
has sfReq => ( is => 'rw' );
has sfMsg => ( is => 'rw' );
has ott => (
is => 'rw',
@ -106,6 +106,19 @@ sub init {
return 0;
}
unless (
$self->sfMsg(
$self->p->HANDLER->buildSub(
$self->p->HANDLER->substitute( $self->conf->{sfRemovedMsg} )
)
)
)
{
$self->error( 'Error in sfRemovedMsg rule'
. $self->p->HANDLER->tsv->{jail}->error );
return 0;
}
# Enable REST request only if more than 1 2F module is enabled
if ( @{ $self->{sfModules} } > 1 ) {
$self->addUnauthRoute( '2fchoice' => '_choice', ['POST'] );
@ -174,6 +187,33 @@ sub run {
$device->{type} = 'EXPIRED';
$removed++;
last;
$self->userLogger->notice(
" -> $removed EXPIRED 2F device(s) removed");
@$_2fDevices =
map { $_->{type} =~ /\bEXPIRED\b/ ? () : $_ } @$_2fDevices;
$self->p->updatePersistentSession( $req,
{ _2fDevices => to_json($_2fDevices) } );
# Display message if required
if ( $self->sfMsg->( $req, $req->sessionInfo ) ) {
$req->info(
$self->loadTemplate(
'simpleInfo',
(
$removed > 1
? (
params => {
trspan => "expired2Fremoved, $removed"
}
)
: ( params =>
{ trspan => "oneExpired2Fremoved" } )
)
)
);
return PE_INFO;
}
}
}
if ($removed) {

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"يرجى استخدام يوبي كي الخاص بك",
"errorMsg":"رسالة خاطئة",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"الاسم الاول",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"التطبيق ٪s هل ترغب في معرفة:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"هل توافق على تقديم الإعدادات التالية؟",
"openIdExample":"فمثلا:http://myopenid.org/toto",
"openidExchange":"هل تريد مصادقة نفسك على٪ s؟",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Gebe den TOTP Code ein",
"enterYubikey":"Benutze bitte deinen Yubikey",
"errorMsg":"Fehlermeldung",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fülle das Formular aus",
"firstName":"Vorname",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"Die Anwendung %s möchte wissen:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Stimmst du folgenden Parametern zu ?",
"openIdExample":"zum Beispiel: http://myopenid.org/toto",
"openidExchange":"Willst du dich bei %s authentifizieren ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"First name",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents": "OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"First name",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Virhe viesti",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"Etunimi",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Entrez le code TOTP",
"enterYubikey":"Utilisez votre Yubikey",
"errorMsg":"Message d'erreur",
"expired2Fremoved":"%s seconds facteurs expirés ont été supprimés !",
"fillTheForm":"Remplissez le formulaire",
"forbidden":"Accès INTERDIT",
"firstName":"Prénom",
@ -174,6 +175,7 @@
"oidcConsent":"L'application %s voudrait connaître :",
"oidcConsents": "Accords OIDC",
"oidcConsentsFull":"Accords OpenID Connect",
"oneExpired2Fremoved":"Un second facteur expiré a été supprimé !",
"openidAp":"Consentez-vous à communiquer les paramètres suivants ?",
"openIdExample":"par exemple : http://myopenid.org/toto",
"openidExchange":"Souhaitez-vous vous identifier sur le site %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Inserisci il codice TOTP",
"enterYubikey":"Utilizza il tuo Yubikey",
"errorMsg":"Messaggio di errore",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Compila il modulo",
"firstName":"Nome",
"forbidden":"Accesso VIETATO",
@ -174,6 +175,7 @@
"oidcConsent":"L'applicazione %s vorrebbe sapere:",
"oidcConsents":"Consensi OIDC",
"oidcConsentsFull":"Consensi OpenID Connect",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Accetti di fornire i seguenti parametri?",
"openIdExample":"per esempio:http://myopenid.org/toto",
"openidExchange":"Vuoi autenticarti su% s?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"First name",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"First name",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"First name",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
"openIdExample":"for example:http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Vui lòng sử dụng Yubikey của bạn",
"errorMsg":"Thông báo lỗi",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"Tên",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"Ứng dụng %s muốn biết:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Bạn đồng ý cung cấp các thông số sau?",
"openIdExample":"ví dụ: http: //myopenid.org/toto",
"openidExchange":"Bạn có muốn chứng thực mình trên%s không?",

View File

@ -131,6 +131,7 @@
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"请使用您的Yubikey",
"errorMsg":"错误消息",
"expired2Fremoved":"%s expired 2F devices have been removed!",
"fillTheForm":"Fill the form",
"firstName":"名",
"forbidden":"Access FORBIDDEN",
@ -174,6 +175,7 @@
"oidcConsent":"The application %s would like to know:",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"您是否同意提供以下参数?",
"openIdExample":"例如http://myopenid.org/toto",
"openidExchange":"Do you want to authenticate yourself on %s ?",

View File

@ -0,0 +1,154 @@
use Test::More;
use strict;
use IO::String;
require 't/test-lib.pm';
my $maintests = 20;
SKIP: {
eval { require Convert::Base32 };
if ($@) {
skip 'Convert::Base32 is missing', $maintests;
}
require Lemonldap::NG::Common::TOTP;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
totp2fSelfRegistration => 1,
totp2fActivation => 1,
totp2fTTL => 2,
sfRemovedMsg => '$uid eq "dwho"',
portalMainLogo => 'common/logos/logo_llng_old.png',
}
}
);
my $res;
# Try to authenticate
# -------------------
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23
),
'Auth query'
);
my $id = expectCookie($res);
# TOTP form
ok(
$res = $client->_get(
'/2fregisters',
cookie => "lemonldap=$id",
accept => 'text/html',
),
'Form registration'
);
expectRedirection( $res, qr#/2fregisters/totp$# );
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' );
# 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'
);
my ( $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);
diag 'Waiting';
sleep 3;
# Try to sign-in
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
expectOK($res);
ok(
$res->[2]->[0] =~
qr%<h3 trspan="oneExpired2Fremoved">oneExpired2Fremoved</h3>%,
'Found expired 2F message'
) or print STDERR Dumper( $res->[2]->[0] );
my $c = getCookies($res);
ok( not(%$c), 'No cookie' );
}
count($maintests);
clean_sessions();
done_testing( count() );

View File

@ -3,7 +3,7 @@ use strict;
use IO::String;
require 't/test-lib.pm';
my $maintests = 17;
my $maintests = 18;
SKIP: {
eval { require Convert::Base32 };
@ -59,7 +59,6 @@ SKIP: {
$res->[2]->[0] =~ qr%<img src="/static/common/logos/logo_llng_old.png"%,
'Found custom Main Logo'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
# JS query
ok(