Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2019-08-09 23:33:59 +02:00
commit 866d5457f3
46 changed files with 453 additions and 76 deletions

View File

@ -3,7 +3,7 @@ server {
server_name manager.__DNSDOMAIN__;
root __MANAGERSITEDIR__;
# Use "lm_app" format to get username in nginx.log (see nginx-lmlog.conf)
#access_log /var/log/nginx/portal.log lm_app;
#access_log /var/log/nginx/manager.log lm_app;
# Uncomment this if you are running behind a reverse proxy and want
# LemonLDAP::NG to see the real IP address of the end user

View File

@ -370,6 +370,10 @@ languages = fr, en, it, vi, ar
; The first will be used as default module displayed
enabledModules = conf, sessions, notifications, 2ndFA, viewer
; To avoid restricted users to edit configuration, defaulModule MUST be different than 'conf'
; 'viewer' is set by default
;defaultModule = viewer
; Viewer module allows us to edit configuration in read-only mode
; Options can be set with specific rules like this :
;viewerAllowBrowser = $uid eq 'dwho'

View File

@ -568,7 +568,7 @@ sub substitute {
$expr =~ s/\$ip\b/\$ENV{REMOTE_ADDR}/sg;
# substitute vars with session data, excepts special vars $_ and $\d+
$expr =~ s/\$(?!(?:ENV|env)\b)([_a-zA-Z]\w*)/\$s->{$1}/sg;
$expr =~ s/\$(?!(?:ENV|env)\b)(_\w+|[a-zA-Z]\w*)/\$s->{$1}/sg;
$expr =~ s/\$ENV\{/\$r->{env}->\{/g;
$expr =~ s/\$env->\{/\$r->{env}->\{/g;

View File

@ -52,7 +52,7 @@ sub init {
return 0;
}
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA";
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, viewer";
my @links;
my @enabledModules =
map { push @links, $_; "Lemonldap::NG::Manager::" . ucfirst($_) }
@ -87,7 +87,14 @@ sub init {
"default-src 'self' $portal;frame-ancestors 'none';form-action 'self';"
);
$self->defaultRoute( $working[0]->defaultRoute );
# Avoid restricted users to access configuration by default route
my $defaultMod = $self->{defaultModule} || 'viewer';
$self->logger->debug("Default module -> $defaultMod");
my ($index) =
grep { $working[$_] =~ /::$defaultMod$/ } ( 0 .. $#working );
$index //= $#working;
$self->logger->debug("Default index -> $index");
$self->defaultRoute( $working[$index]->defaultRoute );
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
my $linksIcons = {

View File

@ -137,7 +137,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
eval {
do {
qr/$_[0]/;
}
}
};
return $@ ? ( 0, "__badRegexp__: $@" ) : 1;
}
@ -165,7 +165,7 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'RSAPrivateKey' => {
'test' => sub {
return $_[0] =~
m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\r\n]*)?[a-zA-Z0-9/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$]s
m[^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\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__' );
}
@ -218,7 +218,8 @@ m[^(?:(?:\-+\s*BEGIN\s+(?:PUBLIC\s+KEY|CERTIFICATE)\s*\-+\r?\n)?[a-zA-Z0-9/\+\r\
},
'select' => {
'test' => sub {
my $test = grep( { $_ eq $_[0]; }
my $test =
grep( { $_ eq $_[0]; }
map( { $_->{'k'}; } @{ $_[2]{'select'}; } ) );
return $test
? 1
@ -1592,7 +1593,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
eval {
do {
qr/$_[0]/;
}
}
};
return $@ ? 0 : 1;
},

View File

@ -146,7 +146,7 @@ sub types {
test => sub {
return (
$_[0] =~
/^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\r\n]*)?[a-zA-Z0-9\/\+\r\n]+={0,2}(?:\r?\n\-+\s*END\s+(?:RSA\s+)PRIVATE\s+KEY\s*\-+)?[\r\n]*)?$/s
/^(?:(?:\-+\s*BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY\s*\-+\r?\n)?(?:Proc-Type:.*\r?\nDEK-Info:.*\r?\n[\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__' )
);

View File

@ -682,11 +682,11 @@
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fSecret":"سر مشترك",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fLogo":"Logo",
"radius2fLogo":"شعار",
"radius2fLabel":"Label",
"radiusAuthnLevel":"مستوى إثبات الهوية",
"radiusParams":"معايير راديوس",
@ -819,7 +819,7 @@
"totp2fActivation":"تفعيل",
"totp2fAuthnLevel":"TOTP authentication level",
"totp2fLabel":"Label",
"totp2fLogo":"ﺶﻋﺍﺭ",
"totp2fLogo":"شعار",
"totp2fDigits":"Number of digits",
"totp2fDisplayExistingSecret":"Display existing secret",
"totp2fInterval":"Interval",
@ -842,7 +842,7 @@
"u2fActivation":"تفعيل",
"u2fAuthnLevel":"U2F مستوى إثبات الهوية",
"u2fLabel":"Label",
"u2fLogo":"ﺶﻋﺍﺭ",
"u2fLogo":"شعار",
"u2fSelfRegistration":"التسجيل الذاتي",
"u2fTTL":"Lifetime",
"u2fUserCanRemoveKey":"Allow user to remove U2F key",
@ -870,7 +870,7 @@
"utotp2fActivation":"تفعيل",
"utotp2fAuthnLevel":"مستوى إثبات الهوية",
"utotp2fLabel":"Label",
"utotp2fLogo":"ﺶﻋﺍﺭ",
"utotp2fLogo":"شعار",
"value":"القيمة",
"values":"القيم",
"variables":"المتغيرات",
@ -905,7 +905,7 @@
"yubikey2fActivation":"تفعيل",
"yubikey2fAuthnLevel":"مستوى إثبات الهوية",
"yubikey2fLabel":"Label",
"yubikey2fLogo":"ﺶﻋﺍﺭ",
"yubikey2fLogo":"شعار",
"yubikey2fClientID":"API العميل ID",
"yubikey2fNonce":"Nonce",
"yubikey2fPublicIDSize":"حجم الجزء العام لي OTP آي دي",
@ -1050,4 +1050,4 @@
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -1049,4 +1049,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -679,12 +679,12 @@
"publicKey":"Chiave pubblica",
"purgeNotification":"Elimina definitivamente la notifica",
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fActivation":"Attivazione",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fSecret":"Segreto condiviso",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
"radius2fAuthnLevel":"Livello di autenticazione",
"radius2fLogo":"Logo",
"radius2fLabel":"Label",
"radiusAuthnLevel":"Livello di autenticazione",
@ -1049,4 +1049,4 @@
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
}
}

View File

@ -681,7 +681,7 @@
"radius2f":"Radius second factor",
"radius2fActivation":"Activation",
"radius2fServer":"Server hostname",
"radius2fSecret":"Shared secret",
"radius2fSecret":"Chia sẻ bí mật",
"radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout",
"radius2fAuthnLevel":"Authentication level",
@ -1049,4 +1049,4 @@
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -1049,4 +1049,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -82,6 +82,7 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
PREFIX => $self->prefix,
TARGET => '/' . $self->prefix . '2fcheck',
CHECKLOGINS => $checkLogins
}

View File

@ -112,7 +112,9 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
PREFIX => $self->prefix,
TARGET => '/' . $self->prefix . '2fcheck',
LEGEND => 'enterMail2fCode',
CHECKLOGINS => $checkLogins
}
);

View File

@ -93,7 +93,9 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
PREFIX => $self->prefix,
TARGET => '/' . $self->prefix . '2fcheck',
LEGEND => 'enterRest2fCode',
CHECKLOGINS => $checkLogins
}
);

View File

@ -67,6 +67,7 @@ sub run {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
TOKEN => $token,
PREFIX => $self->prefix,
TARGET => '/' . $self->prefix . '2fcheck',
LEGEND => 'enterRadius2fCode',
CHECKLOGINS => $checkLogins

View File

@ -66,11 +66,11 @@ sub authenticate {
my ( $self, $req ) = @_;
my $res = $self->SUPER::authenticate($req);
my $pls = $self->ldap->getLdapValue( $req->data->{entry}, 'pwdLastSet' );
my $computed = $self->ldap->getLdapValue( $req->data->{entry},
my $pls = $self->ldap->getLdapValue( $req->data->{ldapentry}, 'pwdLastSet' );
my $computed = $self->ldap->getLdapValue( $req->data->{ldapentry},
'msDS-User-Account-Control-Computed' );
my $_adUac =
$self->ldap->getLdapValue( $req->data->{entry}, 'userAccountControl' )
$self->ldap->getLdapValue( $req->data->{ldapentry}, 'userAccountControl' )
|| 0;
unless ( $res == PE_OK ) {

View File

@ -128,6 +128,14 @@ sub getForm {
my ( $self, $req ) = @_;
return [ split /[, ]\s*/, $self->conf->{combinationForms} ]
if ( $self->conf->{combinationForms} );
if ( $req->{error} > PE_OK ) {
$self->logger->notice('Start over combination schema');
my $stack = $self->stackSub->( $req->env );
my ( $res, $name ) = $stack->[0]->[0]->( 'getDisplayType', $req );
return $res;
}
my ( $nb, $stack ) = (
$req->data->{dataKeep}->{combinationTry},
$req->data->{combinationStack}

View File

@ -124,6 +124,9 @@ sub extractFormInfo {
);
unless ($status) {
$self->logger->error('Unable to accept security context');
foreach ( $status->generic_message(), $status->specific_message() ) {
$self->logger->error($_);
}
return PE_ERROR;
}
my $client_name;

View File

@ -602,6 +602,7 @@ sub run {
{
redirect_uri => $oidc_request->{'redirect_uri'},
scope => $oidc_request->{'scope'},
client_id => $client_id,
user_session_id => $req->id,
_utime => time,
nonce => $oidc_request->{'nonce'},
@ -770,6 +771,7 @@ sub run {
undef,
{
redirect_uri => $oidc_request->{'redirect_uri'},
client_id => $client_id,
scope => $oidc_request->{'scope'},
user_session_id => $req->id,
_utime => time,
@ -1071,6 +1073,14 @@ sub token {
}
}
# Check we have the same client_id value
unless ( $client_id eq $codeSession->data->{client_id} )
{
$self->userLogger->error( "Provided client_id does not match "
. $codeSession->data->{client_id} );
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
# Check we have the same redirect_uri value
unless ( $req->param("redirect_uri") eq $codeSession->data->{redirect_uri} )
{

View File

@ -134,12 +134,12 @@ sub getUser {
eval { $self->p->_authentication->setSecurity($req) };
return PE_BADCREDENTIALS;
}
unless ( $req->data->{entry} = $mesg->entry(0) ) {
unless ( $req->data->{ldapentry} = $mesg->entry(0) ) {
$self->userLogger->warn("$req->{user} was not found in LDAP directory");
eval { $self->p->_authentication->setSecurity($req) };
return PE_BADCREDENTIALS;
}
$req->data->{dn} = $req->data->{entry}->dn();
$req->data->{dn} = $req->data->{ldapentry}->dn();
PE_OK;
}

View File

@ -201,7 +201,7 @@ sub getNotifBack {
# launch 'controlUrl' to restore "urldc" using do()
$self->logger->debug('All pending notifications have been accepted');
$self->p->rebuildCookies($req);
return $self->p->do( $req, ['controlUrl'] );
return $self->p->do( $req, [ 'controlUrl', @{ $self->p->endAuth } ] );
}
else {
# No notifications checked here, this entry point must not be called.

View File

@ -260,7 +260,7 @@ sub getNotifBack {
# launch 'controlUrl' to restore "urldc" using do()
$self->logger->debug('All pending notifications have been accepted');
$self->p->rebuildCookies($req);
return $self->p->do( $req, ['controlUrl'] );
return $self->p->do( $req, ['controlUrl', @{ $self->p->endAuth }] );
}
else {
# No notifications checked here, this entry point must not be called.

View File

@ -12,7 +12,10 @@ use Lemonldap::NG::Portal::Main::Constants qw(
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Plugin';
extends qw(
Lemonldap::NG::Portal::Main::Plugin
Lemonldap::NG::Portal::Auth::_WebForm
);
# INITIALIZATION
@ -27,12 +30,9 @@ has ott => (
}
);
has prefix => ( is => 'rw' );
has logo => ( is => 'rw', default => '2f.png' );
has label => ( is => 'rw' );
has prefix => ( is => 'rw' );
has logo => ( is => 'rw', default => '2f.png' );
has label => ( is => 'rw' );
has noRoute => ( is => 'ro' );
sub init {
@ -86,6 +86,7 @@ sub _verify {
my $token;
unless ( $token = $req->param('token') ) {
$self->userLogger->error( $self->prefix . ' 2F access without token' );
eval { $self->setSecurity($req) };
$req->mustRedirect(1);
return $self->p->do( $req, [ sub { PE_NOTOKEN } ] );
}
@ -93,6 +94,7 @@ sub _verify {
my $session;
unless ( $session = $self->ott->getToken($token) ) {
$self->userLogger->info('Token expired');
$self->setSecurity($req);
return $self->p->do( $req, [ sub { PE_TOKENEXPIRED } ] );
}

View File

@ -38,7 +38,7 @@ sub getUser {
eval { $self->p->_authentication->setSecurity($req) };
return PE_ERROR;
}
unless ( $req->data->{entry} = $sth->fetchrow_hashref() ) {
unless ( $req->data->{dbientry} = $sth->fetchrow_hashref() ) {
$self->userLogger->warn("User $user not found");
eval { $self->p->_authentication->setSecurity($req) };
return PE_BADCREDENTIALS;
@ -54,8 +54,8 @@ sub setSessionInfo {
foreach my $var ( keys %{ $self->exportedVars } ) {
my $attr = $self->exportedVars->{$var};
$req->{sessionInfo}->{$var} = $req->data->{entry}->{$attr}
if ( defined $req->data->{entry}->{$attr} );
$req->{sessionInfo}->{$var} = $req->data->{dbientry}->{$attr}
if ( defined $req->data->{dbientry}->{$attr} );
}
PE_OK;
}

View File

@ -40,7 +40,7 @@ sub setSessionInfo {
%{ $self->conf->{ldapExportedVars} } );
while ( my ( $k, $v ) = each %vars ) {
$req->sessionInfo->{$k} =
$self->ldap->getLdapValue( $req->data->{entry}, $v ) || "";
$self->ldap->getLdapValue( $req->data->{ldapentry}, $v ) || "";
}
PE_OK;
@ -56,7 +56,7 @@ sub setGroups {
if ( $self->conf->{ldapGroupBase} ) {
# Get value for group search
my $group_value = $self->ldap->getLdapValue( $req->data->{entry},
my $group_value = $self->ldap->getLdapValue( $req->data->{ldapentry},
$self->conf->{ldapGroupAttributeNameUser} );
if ( $self->conf->{ldapGroupDecodeSearchedValue} ) {

View File

@ -127,8 +127,10 @@
"date":"تاريخ",
"enterCred":"الرجاء إدخال بيانات الاعتماد الخاصة بك",
"enterExt2fCode":"تم إرسال رمز إليك. الرجاء إدخاله",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"الرجاء إدخال تسجيل الدخول الأوبين إيدي الخاص بك",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"يرجى استخدام يوبي كي الخاص بك",
"errorMsg":"رسالة خاطئة",
@ -267,4 +269,4 @@
"yourProfile":"ملفك الشخصي",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Datum",
"enterCred":"Bitte geben deine Zugangsdaten ein",
"enterExt2fCode":"Ein Code wurde an dich gesendet. Bitte gebe diesen ein",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Bitte geben deinen OpenID-Login ein",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Gebe den TOTP Code ein",
"enterYubikey":"Benutze bitte deinen Yubikey",
"errorMsg":"Fehlermeldung",
@ -267,4 +269,4 @@
"yourProfile":"Ihr Profil",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -267,4 +269,4 @@
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Päivämäärä",
"enterCred":"Syötä käyttäjätietosi",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Virhe viesti",
@ -267,4 +269,4 @@
"yourProfile":"Profiilisi",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Merci de vous authentifier",
"enterExt2fCode":"Un code vous a été envoyé, entrez-le ici",
"enterMail2fCode":"Un code vous a été envoyé par mail, entrez-le ici",
"enterOpenIDLogin":"Entrez votre identifiant OpenID",
"enterRadius2fCode":"Entrez votre code OTP",
"enterRest2fCode":"Entrez votre code OTP",
"enterTotpCode":"Entrez le code TOTP",
"enterYubikey":"Utilisez votre Yubikey",
"errorMsg":"Message d'erreur",

View File

@ -127,8 +127,10 @@
"date":"Data",
"enterCred":"Inserisci le tue credenziali",
"enterExt2fCode":"Un codice vi é stato inviato. Inseritelo",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Inserisci il tuo login OpenID",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Inserisci il codice TOTP",
"enterYubikey":"Utilizza il tuo Yubikey",
"errorMsg":"Messaggio di errore",
@ -267,4 +269,4 @@
"yourProfile":"Il tuo profilo",
"yourTotpKey":"La tua chiave TOTP",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -267,4 +269,4 @@
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -267,4 +269,4 @@
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Date",
"enterCred":"Please enter your credentials",
"enterExt2fCode":"A code has been sent to you. Please enter it",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Please enter your OpenID login",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Please use your Yubikey",
"errorMsg":"Error Message",
@ -267,4 +269,4 @@
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"Ngày",
"enterCred":"Vui lòng nhập thông tin đăng nhập của bạn",
"enterExt2fCode":"Một mã đã được gửi cho bạn. Hãy nhập nó",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"Hãy nhập thông tin đăng nhập OpenID của bạn",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"Vui lòng sử dụng Yubikey của bạn",
"errorMsg":"Thông báo lỗi",
@ -267,4 +269,4 @@
"yourProfile":"Profile của bạn",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -127,8 +127,10 @@
"date":"日期",
"enterCred":"请输入您的认证信息",
"enterExt2fCode":"验证法已发送,请输入",
"enterMail2fCode":"A code has been sent to your email address. Please enter it",
"enterOpenIDLogin":"请输入您的 OpenID 认证",
"enterRadius2fCode":"Please enter your OTP code",
"enterRest2fCode":"Please enter your OTP code",
"enterTotpCode":"Enter TOTP code",
"enterYubikey":"请使用您的Yubikey",
"errorMsg":"错误消息",
@ -267,4 +269,4 @@
"yourProfile":"您的档案",
"yourTotpKey":"Your TOTP key",
"yubikey2f":"Yubikey"
}
}

View File

@ -18,7 +18,16 @@ my $client = LLNG::Manager::Test->new( {
}
);
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu' );
ok(
$res = $client->_get(
'/',
query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==',
accept => 'text/html'
),
'Get Menu'
);
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
ok(
$res->[2]->[0] =~
m%<script type="application/init">\s*\{"sslHost":"https://authssl.example.com:19876"\}\s*</script>%s,
@ -32,12 +41,19 @@ ok( $res->[2]->[0] =~ /ssl\.(?:min\.)?js/, 'Get sslChoice javascript' )
count(4);
ok(
$res = $client->_get( '/', custom => { SSL_CLIENT_S_DN_Custom => 'dwho' } ),
$res = $client->_get(
'/',
cookie => $pdata,
accept => 'text/html',
custom => { SSL_CLIENT_S_DN_Custom => 'dwho' }
),
'Auth query'
);
expectOK($res);
expectCookie($res);
count(1);
expectRedirection( $res, 'http://test1.example.com/' );
$pdata = expectCookie( $res, 'lemonldappdata' );
ok( $pdata eq '', 'pdata is empty' );
count(2);
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
$client = LLNG::Manager::Test->new( {

View File

@ -0,0 +1,178 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
}
my $debug = 'error';
# Initialization
my $op = LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.op.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBOpenIDConnectActivation => 1,
issuerDBOpenIDConnectRule => '$uid eq "french"',
oidcRPMetaDataExportedVars => {
rp => {
email => "mail",
family_name => "cn",
name => "cn"
},
rp2 => {
email => "mail",
family_name => "cn",
name => "cn"
}
},
oidcServiceMetaDataIssuer => "http://auth.op.com",
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",
oidcServiceMetaDataEndSessionURI => "logout",
oidcServiceMetaDataRegistrationURI => "register",
oidcServiceMetaDataTokenURI => "token",
oidcServiceMetaDataUserInfoURI => "userinfo",
oidcServiceAllowHybridFlow => 1,
oidcServiceAllowImplicitFlow => 1,
oidcServiceAllowDynamicRegistration => 1,
oidcServiceAllowAuthorizationCodeFlow => 1,
oidcRPMetaDataOptions => {
rp => {
oidcRPMetaDataOptionsDisplayName => "RP",
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
oidcRPMetaDataOptionsClientID => "rpid",
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
},
rp2 => {
oidcRPMetaDataOptionsDisplayName => "RP2",
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
oidcRPMetaDataOptionsClientID => "rp2id",
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rp2secret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
oidcRPMetaDataOptionsBypassConsent => 1,
oidcRPMetaDataOptionsRule => '$uid eq "dwho"',
}
},
oidcOPMetaDataOptions => {},
oidcOPMetaDataJSON => {},
oidcOPMetaDataJWKS => {},
oidcServiceMetaDataAuthnContext => {
'loa-4' => 4,
'loa-1' => 1,
'loa-5' => 5,
'loa-2' => 2,
'loa-3' => 3
},
oidcServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs2jsmIoFuWzMkilJaA8//5/T30cnuzX9GImXUrFR2k9EKTMt
GMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8TrH1PHFmHpy8/qE/S5OhinIpIi7eb
ABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH1caJ8lmiERFj7IvNKqEhzAk0pyDr
8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdykX5rx0h5SslG3jVWYhZ/SOb2aIzO
r0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO8093X5VVk9vaPRg0zxJQ0Do0YLyzkR
isSAIFb0tdKuDnjRGK6y/N2j6At2HjkxntbtGQIDAQABAoIBADYq6LxJd977LWy3
0HT9nboFPIf+SM2qSEc/S5Po+6ipJBA4ZlZCMf7dHa6znet1TDpqA9iQ4YcqIHMH
6xZNQ7hhgSAzG9TrXBHqP+djDlrrGWotvjuy0IfS9ixFnnLWjrtAH9afRWLuG+a/
NHNC1M6DiiTE0TzL/lpt/zzut3CNmWzH+t19X6UsxUg95AzooEeewEYkv25eumWD
mfQZfCtSlIw1sp/QwxeJa/6LJw7KcPZ1wXUm1BN0b9eiKt9Cmni1MS7elgpZlgGt
xtfGTZtNLQ7bgDiM8MHzUfPBhbceNSIx2BeCuOCs/7eaqgpyYHBbAbuBQex2H61l
Lcc3Tz0CgYEA4Kx/avpCPxnvsJ+nHVQm5d/WERuDxk4vH1DNuCYBvXTdVCGADf6a
F5No1JcTH3nPTyPWazOyGdT9LcsEJicLyD8vCM6hBFstG4XjqcAuqG/9DRsElpHQ
yi1zc5DNP7Vxmiz9wII0Mjy0abYKtxnXh9YK4a9g6wrcTpvShhIcIb8CgYEAzGzG
lorVCfX9jXULIznnR/uuP5aSnTEsn0xJeqTlbW0RFWLdj8aIL1peirh1X89HroB9
GeTNqEJXD+3CVL2cx+BRggMDUmEz4hR59meZCDGUyT5fex4LIsceb/ESUl2jo6Sw
HXwWbN67rQ55N4oiOcOppsGxzOHkl5HdExKidycCgYEAr5Qev2tz+fw65LzfzHvH
Kj4S/KuT/5V6He731cFd+sEpdmX3vPgLVAFPG1Q1DZQT/rTzDDQKK0XX1cGiLG63
NnaqOye/jbfzOF8Z277kt51NFMDYhRLPKDD82IOA4xjY/rPKWndmcxwdob8yAIWh
efY76sMz6ntCT+xWSZA9i+ECgYBWMZM2TIlxLsBfEbfFfZewOUWKWEGvd9l5vV/K
D5cRIYivfMUw5yPq2267jPUolayCvniBH4E7beVpuPVUZ7KgcEvNxtlytbt7muil
5Z6X3tf+VodJ0Swe2NhTmNEB26uwxzLe68BE3VFCsbSYn2y48HAq+MawPZr18bHG
ZfgMxwKBgHHRg6HYqF5Pegzk1746uH2G+OoCovk5ylGGYzcH2ghWTK4agCHfBcDt
EYqYAev/l82wi+OZ5O8U+qjFUpT1CVeUJdDs0o5u19v0UJjunU1cwh9jsxBZAWLy
PAGd6SWf4S3uQCTw6dLeMna25YIlPh5qPA6I/pAahe8e3nSu2ckl
-----END RSA PRIVATE KEY-----
",
oidcServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2jsmIoFuWzMkilJaA8/
/5/T30cnuzX9GImXUrFR2k9EKTMtGMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8T
rH1PHFmHpy8/qE/S5OhinIpIi7ebABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH
1caJ8lmiERFj7IvNKqEhzAk0pyDr8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdy
kX5rx0h5SslG3jVWYhZ/SOb2aIzOr0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO80
93X5VVk9vaPRg0zxJQ0Do0YLyzkRisSAIFb0tdKuDnjRGK6y/N2j6At2Hjkxntbt
GQIDAQAB
-----END PUBLIC KEY-----
",
}
}
);
my $res;
# Authenticate to LLNG
my $url = "/";
my $query = "user=french&password=french";
ok(
$res = $op->_post(
"/",
IO::String->new($query),
accept => 'text/html',
length => length($query),
),
"Post authentication"
);
count(1);
my $idpId = expectCookie($res);
# Get code for RP1
my $query="response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_get(
"/oauth2/authorize",
query => "$query",
accept => 'text/html',
cookie => "lemonldap=$idpId",
),
"Get authorization code"
);
count(1);
my ( $code ) = expectRedirection( $res, qr#http://rp2\.com/.*code=([^\&]*)#);
# Play code on RP2
$query="grant_type=authorization_code&code=$code&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_post(
"/oauth2/token",
IO::String->new($query),
accept => 'text/html',
length => length($query),
custom => {
HTTP_AUTHORIZATION => "Basic ". encode_base64("rp2id:rp2secret"),
},
),
"Post token"
);
count(1);
# Expect an invalid request
ok ($res->[0] = 400);
count(1);
clean_sessions();
done_testing( count() );

View File

@ -244,6 +244,8 @@ sub issuer {
userDB => 'Same',
restSessionServer => 1,
restConfigServer => 1,
templateDir => 'site/templates',
staticPrefix => '/static',
}
}
);

View File

@ -3,7 +3,7 @@ use strict;
use IO::String;
require 't/test-lib.pm';
my $maintests = 16;
my $maintests = 19;
SKIP: {
eval { require Convert::Base32 };
@ -109,6 +109,23 @@ SKIP: {
ok( $res->{result} == 1, 'Key is registered' );
$client->logout($id);
# Try to sign-in with bad password
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=badpasswd/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query with bad password'
);
# Try to sign-in
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
@ -127,14 +144,15 @@ SKIP: {
);
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token', 'checkLogins' );
expectForm( $res, undef, '/totp2fcheck', 'token' );
ok( $code = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 6 ),
'Code' );
$query =~ s/code=/code=$code/;
# Expired token -->> TO BE FIXED
#diag 'Waiting';
#sleep 3;
# Expired token
diag 'Waiting';
sleep 3;
ok(
$res = $client->_post(
'/totp2fcheck', IO::String->new($query),
@ -143,10 +161,10 @@ SKIP: {
),
'Post code'
);
expectRedirection( $res, 'http://auth.example.com/' );
$id = expectCookie($res);
$client->logout($id);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
ok( $res->[2]->[0] =~ /<span trmsg="82"><\/span>/, 'Token expired' )
or print STDERR Dumper( $res->[2]->[0] );
}
count($maintests);

View File

@ -47,7 +47,7 @@ sub try {
my $user = shift;
my $res;
# Gat token
# Get token
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Unauth request' );
count(1);
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' );

View File

@ -4,7 +4,7 @@ use IO::String;
my $res;
my $file = 't/notifications.db';
my $maintests = 8;
my $maintests = 9;
eval { unlink $file };
require 't/test-lib.pm';
my $file = tempdb();
@ -128,6 +128,11 @@ q{INSERT INTO notifications VALUES ('dwho','testref2','2016-05-30 00:00:00',?,nu
"Accept notifications"
);
expectRedirection( $res, 'http://test1.example.com/' );
my $cookies = getCookies($res);
ok(
!defined( $cookies->{lemonldappdata} ),
" Make sure no pdata is returned"
);
# Verify that notification was tagged as 'done'
my $sth =

View File

@ -4,8 +4,9 @@ use IO::String;
my $res;
my $file = 't/notifications.db';
my $maintests = 7;
#my $maintests = 8;
my $maintests = 8;
#my $maintests = 9;
eval { unlink $file };
require 't/test-lib.pm';
@ -127,6 +128,11 @@ qq{INSERT INTO notifications VALUES ('dwho','testref2','2016-05-30 00:00:00','<?
"Accept notifications"
);
expectRedirection( $res, 'http://test1.example.com/' );
my $cookies = getCookies($res);
ok(
!defined( $cookies->{lemonldappdata} ),
" Make sure no pdata is returned"
);
# Verify that notification was tagged as 'done'
my $sth =

View File

@ -3,7 +3,7 @@ use strict;
use IO::String;
require 't/test-lib.pm';
my $maintests = 18;
my $maintests = 27;
SKIP: {
eval { require Convert::Base32 };
@ -23,6 +23,8 @@ SKIP: {
totp2fActivation => 1,
totp2fDigits => 8,
totp2fTTL => -1,
formTimeout => 2,
requireToken => 1,
}
}
);
@ -30,15 +32,23 @@ SKIP: {
# Try to authenticate
# -------------------
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
my ( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=dwho/;
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# TOTP form
ok(
@ -99,16 +109,23 @@ SKIP: {
# Try to sign-in
$client->logout($id);
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=dwho/;
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
my ( $host, $url, $query ) =
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token' );
# Generate TOTP with LLNG
@ -132,6 +149,65 @@ SKIP: {
);
$id = expectCookie($res);
$client->logout($id);
# Try to sign-in with an expired OTT
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=dwho/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token' );
# Generate TOTP with LLNG
ok( $totp = Lemonldap::NG::Common::TOTP::_code( undef, $key, 0, 30, 8 ),
'LLNG Code' );
$query =~ s/code=/code=$code/;
diag 'Waiting';
sleep 3;
ok(
$res = $client->_post(
'/totp2fcheck', IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Post code'
);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
ok( $res->[2]->[0] =~ /<span trmsg="82"><\/span>/, 'Token expired' )
or print STDERR Dumper( $res->[2]->[0] );
# Try to sign-in
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
$query =~ s/user=/user=dwho/;
$query =~ s/password=/password=dwho/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
( $host, $url, $query ) =
expectForm( $res, undef, '/totp2fcheck', 'token' );
}
count($maintests);

View File

@ -20,6 +20,7 @@ my $client = LLNG::Manager::Test->new( {
mail2fCodeRegex => '\w{4}',
},
'logo' => 'home.jpg',
'label' => "Home Label",
'rule' => '$uid eq "dwho" or $uid eq "msmith"',
'type' => 'Mail2F'
},
@ -113,6 +114,16 @@ ok(
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
ok(
$res->[2]->[0] =~ qq%<h4 class="mb-0" trspan="work2f"></h4>%, 'Found translation label'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
ok(
$res->[2]->[0] =~ qq%<h4 class="mb-0">Home Label</h4>%, 'Found overriden label'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
$query .= '&sf=home';
ok(
$res = $client->_post(