Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2020-03-02 17:41:09 +01:00
commit a81dbb108b
63 changed files with 879 additions and 215 deletions

View File

@ -1,4 +1,5 @@
LemonLDAP::NG Core team:
* Maxime BESSON
* David COUTADEUR
* Xavier GUIMARD
* Christophe MAUDOUX
@ -9,17 +10,21 @@ Past and present contributors:
* Casimir ANTUNES
* Sébastien BAHLOUL
* Oliver BOIREAU
* Sandro CAZZANIGA
* Jean-Thomas CHECCO
* Sandro CAZZANIGA
* Thomas CHEMINEAU
* François-Xavier DELTOMBE
* Sebastien DIAZ
* Hubert GAULTIER
* Soisik FROGIER
* Gaultier HUBERT
* Eric GERMAN
* Mounir GZADY
* Jérémy KESPITE
* Erwan LEGALL
* Pascal PEJAC
* Daniel RIVIERE
* Antoine ROSIER
* Mame Dieynaba SENE
* Habib ZITOUNI
See http://lemonldap-ng.org/contact#the_team

18
COPYING
View File

@ -4,18 +4,22 @@ Upstream-Contact: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues
Source: https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/tags?sort=updated_desc
Files: *
Copyright: 2005-2019, Xavier Guimard <yadd@debian.org>
2006-2019, Clement Oudot <clem.oudot@gmail.com>
Copyright: 2005-2020, Xavier Guimard <yadd@debian.org>
2006-2020, Clement Oudot <clem.oudot@gmail.com>
2008, Mikael Ates <mikael.ates@univ-st-etienne.fr>
2008-2011, Thomas Chemineau <thomas.chemineau@gmail.com>
2012-2013, Sandro Cazzaniga <cazzaniga.sandro@gmail.com>
2012-2015, François-Xavier Deltombe <fxdeltombe@gmail.com>
2012-2015, David Coutadeur <david.coutadeur@gmail.com>
2018-2019, Christophe Maudoux <chrmdx@gmail.com>
2005-2019, Gendarmerie nationale <https://www.gendarmerie.interieur.gouv.fr>
2006-2015, LINAGORA <info@linagora.com>
2012-2019, David Coutadeur <david.coutadeur@gmail.com>
2018-2020, Christophe Maudoux <chrmdx@gmail.com>
2019-2020, Maxime Besson <maxime.besson@worteks.com>
2019, Soisik Frogier <soisik.froger@worteks.com>
2019, Mame Dieynaba Sene <msene@linagora.com>
2019, Antoine Rosier <lemonldap@mon-refuge.fr>
2005-2020, Gendarmerie nationale <https://www.gendarmerie.interieur.gouv.fr>
2006-2019, LINAGORA <info@linagora.com>
2015-2018, Savoir-faire Linux <contact@savoirfairelinux.com>
2018-2019, Worteks <info@worteks.com>
2018-2020, Worteks <info@worteks.com>
License: GPL-2+
Files: lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/PAM.pm

View File

@ -92,6 +92,10 @@ print
print "<li>Connected user: <ul>\n";
print "<li><tt>\$ENV{HTTP_AUTH_USER}</tt>: $ENV{HTTP_AUTH_USER}</li>\n";
print "<li><tt>\$ENV{REMOTE_USER}</tt>: $ENV{REMOTE_USER}</li>\n";
print "</ul><li>Groups: <ul>\n";
for my $grp (split /; /, $ENV{HTTP_AUTH_GROUPS}) {
print "<li>$grp</li>\n";
}
print "</ul></li>\n";
print "</ul>\n";
print

View File

@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "llng-fastcgi-server 8"
.TH llng-fastcgi-server 8 "2020-01-24" "perl v5.26.1" "User Contributed Perl Documentation"
.TH llng-fastcgi-server 8 "2020-03-02" "perl v5.26.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@ -178,16 +178,10 @@ file to load for custom functions
Plack::Handler engine, default to \s-1FCGI\s0 (see below)
.IP "\-\-plackOptions:" 4
.IX Item "--plackOptions:"
other options to pass to Plack. This multi-valued parameter must have
\&\*(L"key=value\*(R" values.
other options to pass to the Plack handler. This multi-valued parameter must
have \*(L"key=value\*(R" values.
.Sp
Example to use FCGI::ProcManager::Constrained instead of default \s-1FCGI\s0 manager
(FCGI::ProcManager):
.Sp
.Vb 2
\& llng\-fastcgi\-server \-u nobody \-g nobody \-s /run/llng.sock \-e FCGI \-n 10 \e
\& \-\-plackOptions manager=FCGI::ProcManager::Constrained
.Ve
See Plack::Handler::FCGI for a list of options for the default \s-1FCGI\s0 engine
.SH "ENGINES"
.IX Header "ENGINES"
By default, llng-fastcgi-server uses \s-1FCGI\s0 (= Plack::Handler::FCGI). Some
@ -195,23 +189,6 @@ other engines can be used:
.SS "\s-1FCGI\s0 (default)"
.IX Subsection "FCGI (default)"
It uses FCGI::ProcManager as manager. Other managers:
.IP "FCGI::ProcManager::Constrained" 4
.IX Item "FCGI::ProcManager::Constrained"
Example to launch it:
.Sp
.Vb 2
\& llng\-fastcgi\-server \-u nobody \-g nobody \-s /run/llng.sock \-e FCGI \-n 10 \e
\& \-\-plackOptions manager=FCGI::ProcManager::Constrained
.Ve
.Sp
You can then set environment values (in /etc/default/llng\-fastcgi\-server file
for example):
.Sp
.Vb 3
\& PM_MAX_REQUESTS=10000
\& PM_SIZECHECK_NUM_REQUESTS=100
\& PM_MAX_SIZE=300000
.Ve
.IP "FCGI::ProcManager::Dynamic" 4
.IX Item "FCGI::ProcManager::Dynamic"
.Vb 2

View File

@ -24,7 +24,7 @@ use constant MANAGERSECTION => "manager";
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
use constant APPLYSECTION => "apply";
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:Empty(?:Header|Value)s|PersistentInfo))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -108,7 +108,7 @@ sub portalConsts {
}
# EXPORTER PARAMETERS
our @EXPORT_OK = ('portalConsts');
our @EXPORT_OK = ('portalConsts');
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK, 'import' ], );
1;

View File

@ -596,6 +596,9 @@ sub substitute {
$expr =~ s/\$_rulematch\[/\$m->\[/g;
$expr =~ s/\bskip\b/q\{999_SKIP\}/g;
# handle inGroup
$expr =~ s/\binGroup\(([^)]*)\)/listMatch(\$s->{'hGroups'},\1,1),/g;
return $expr;
}

View File

@ -90,6 +90,23 @@ ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
# Required "timelords" group
ok(
$res =
$client->_get( '/fortimelords', undef, undef, "lemonldap=$sessionId" ),
'Require Timelords group'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Required "dalek" group
ok(
$res = $client->_get( '/fordaleks', undef, undef, "lemonldap=$sessionId" ),
'Require Dalek group'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res, 403 );
count(2);
# Required AuthnLevel = 1
ok( $res = $client->_get( '/AuthWeak', undef, undef, "lemonldap=$sessionId" ),
'Weak Authentified query' );

View File

@ -47,6 +47,8 @@
"^/test-uri2": "varIsInUri($ENV{REQUEST_URI}, '/test-uri2/', $uid)",
"^/test-restricted_uri": "varIsInUri($ENV{REQUEST_URI}, '/test-restricted_uri/', \"$uid/\", 1)",
"^/skipif": "$ENV{REQUEST_URI} =~ /zz/ ? skip : 1",
"^/fortimelords": "inGroup('timelords')",
"^/fordaleks": "inGroup('daleks')",
"^/logout": "logout_sso",
"^/deny": "deny",
"^/user_(\\w+)/": "$uid eq $_rulematch[1]",

View File

@ -46,17 +46,42 @@ sub init {
my $now = time;
my $ts = strftime "%Y%m%d%H%M%S", localtime;
print F '{"_updateTime":"'
. $ts
. '","_timezone":"1","_session_kind":"SSO","_passwordDB":"Demo","_startTime":"'
. $ts
. '","ipAddr":"127.0.0.1","UA":"Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1","_user":"dwho","_userDB":"Demo","_lastAuthnUTime":'
. $now
. ',"uid":"dwho","_issuerDB":"Null","_session_id":"f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545","authenticationLevel":1,"_whatToTrace":"dwho","_auth":"Demo","_utime":'
. $now
. ',"_loginHistory":{"successLogin":[{"ipAddr":"127.0.0.1","_utime":'
. $now
. '}]},"cn":"Doctor Who","mail":"dwho@badwolf.org"}';
print F <<EOF;
{
"_startTime" : "$ts",
"_session_kind" : "SSO",
"UA" : "Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1",
"cn" : "Doctor Who",
"_utime" : $now,
"_whatToTrace" : "dwho",
"mail" : "dwho\@badwolf.org",
"_passwordDB" : "Demo",
"_lastAuthnUTime" : $now,
"uid" : "dwho",
"_issuerDB" : "Null",
"_userDB" : "Demo",
"_user" : "dwho",
"_session_id" : "f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545",
"authenticationLevel" : 1,
"_auth" : "Demo",
"_updateTime" : "$ts",
"_loginHistory" : {
"successLogin" : [
{
"ipAddr" : "127.0.0.1",
"_utime" : $now
}
]
},
"ipAddr" : "127.0.0.1",
"_timezone" : "1",
"groups" : "users; timelords",
"hGroups" : {
"users" : {},
"timelords" : {}
}
}
EOF
close F;
}

View File

@ -142,7 +142,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;
}
@ -223,7 +223,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
@ -839,6 +840,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 0,
'type' => 'bool'
},
'checkUserDisplayEmptyHeaders' => {
'default' => 0,
'type' => 'bool'
},
'checkUserDisplayEmptyValues' => {
'default' => 0,
'type' => 'bool'
@ -1709,7 +1714,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

@ -476,6 +476,12 @@ sub attributes {
documentation => 'Display session empty values',
flags => 'p',
},
checkUserDisplayEmptyHeaders => {
default => 0,
type => 'bool',
documentation => 'Display empty headers',
flags => 'p',
},
globalLogoutRule => {
type => 'boolOrExpr',
default => 0,

View File

@ -736,8 +736,9 @@ sub tree {
'checkUserIdRule',
'checkUserHiddenAttributes',
'checkUserSearchAttributes',
'checkUserDisplayPersistentInfo',
'checkUserDisplayEmptyHeaders',
'checkUserDisplayEmptyValues',
'checkUserDisplayPersistentInfo',
]
},
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"السمات المخفية",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"اختيارالإعدادات",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Règle d'utilisation des identités",
"checkUserHiddenAttributes":"Attributs masqués",
"checkUserDisplayPersistentInfo":"Afficher les données de session persistante",
"checkUserDisplayEmptyHeaders":"Afficher les entêtes nuls",
"checkUserDisplayEmptyValues":"Afficher les valeurs nulles",
"checkUserSearchAttributes":"Attributs utilisés pour rechercher les sessions",
"choiceParams":"Paramètres des choix",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Uso della regola delle identità",
"checkUserHiddenAttributes":"Attributi nascosti",
"checkUserDisplayPersistentInfo":"Mostra sessione persistente",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Mostra valori vuoti",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Scelta parametri",

View File

@ -104,8 +104,8 @@
"browserIdSiteName":"Site adı",
"browserIdVerificationURL":"Doğrulama URL'i",
"browseTree":"Ağaca göz at",
"bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtection":"Aktivasyon",
"bruteForceAttackProtection":"Kaba kuvvet saldırı koruması",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"İptal Et",
"captcha_login_enabled":"Giriş formunda aktivasyon",
@ -188,6 +188,7 @@
"checkUserIdRule":"Kimlik kullanım kuralı",
"checkUserHiddenAttributes":"Gizli nitelikler",
"checkUserDisplayPersistentInfo":"Kalıcı oturumu görüntüle",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Boş değerleri görüntüle",
"checkUserSearchAttributes":"Arama oturumlarında kullanılan nitelikler",
"choiceParams":"Tercih parametreleri",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Thuộc tính ẩn",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Các tham số lựa chọn",

View File

@ -188,6 +188,7 @@
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -618,6 +618,7 @@ t/59-Double-cookies-for-Double-sessions.t
t/59-Double-cookies-Refresh-and-Logout.t
t/59-Secured-cookie-Refresh-and-Logout.t
t/60-Status.t
t/61-BruteForceProtection-with-Incremental-lockTimes-and-TOTP.t
t/61-BruteForceProtection-with-Incremental-lockTimes.t
t/61-BruteForceProtection.t
t/61-ForceAuthn.t
@ -641,6 +642,7 @@ t/67-CheckUser-with-issuer-SAML-POST.t
t/67-CheckUser-with-token.t
t/67-CheckUser.t
t/68-ContextSwitching-with-Logout.t
t/68-ContextSwitching-with-TOTP-and-Notification.t
t/68-ContextSwitching.t
t/68-Impersonation-with-doubleCookies.t
t/68-Impersonation-with-filtered-merge.t

View File

@ -14,7 +14,7 @@ use constant CommonPrms => {
LANGS => 'showLanguages',
};
has skinRules => ( is => 'rw' );
has skinRules => ( is => 'rw' );
has favAppsRule => ( is => 'rw', default => sub { 1 } );
sub displayInit {
@ -124,7 +124,7 @@ sub display {
&& $req->data->{login},
ASK_LOGINS => $req->param('checkLogins') || 0,
CONFIRMKEY => $self->stamp(),
LIST => $req->data->{list} || [],
LIST => $req->data->{list} || [],
(
$req->data->{customScript}
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
@ -194,6 +194,7 @@ sub display {
$skinfile = "redirect";
%templateParams = (
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
URL => $req->{urldc},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
FORM_METHOD => $req->data->{redirectFormMethod} || 'get',
@ -312,14 +313,15 @@ sub display {
ASK_LOGINS => $req->param('checkLogins') || 0,
DISPLAY_RESETPASSWORD => $self->conf->{portalDisplayResetPassword},
DISPLAY_REGISTER => $self->conf->{portalDisplayRegister},
DISPLAY_UPDATECERTIF => $self->conf->{portalDisplayCertificateResetByMail},
MAILCERTIF_URL => $self->conf->{certificateResetByMailURL},
MAIL_URL => $self->conf->{mailUrl},
REGISTER_URL => $self->conf->{registerUrl},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
STAYCONNECTED => $self->conf->{stayConnected},
REQUIRE_OLDPASSWORD => $self->conf->{portalRequireOldPassword},
SPOOFID => $self->conf->{impersonationRule},
DISPLAY_UPDATECERTIF =>
$self->conf->{portalDisplayCertificateResetByMail},
MAILCERTIF_URL => $self->conf->{certificateResetByMailURL},
MAIL_URL => $self->conf->{mailUrl},
REGISTER_URL => $self->conf->{registerUrl},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
STAYCONNECTED => $self->conf->{stayConnected},
REQUIRE_OLDPASSWORD => $self->conf->{portalRequireOldPassword},
SPOOFID => $self->conf->{impersonationRule},
(
$req->data->{customScript}
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
@ -338,7 +340,11 @@ sub display {
or $req->{error} == PE_BADURL )
{
$skinfile = 'error';
%templateParams = ( %templateParams, MSG => $req->info(), );
%templateParams = (
%templateParams,
MSG => $req->info(),
LOCKTIME => $req->lockTime()
);
}
else {

View File

@ -9,8 +9,7 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Plugin';
# INITIALIZATION
use constant afterData => 'run';
use constant afterSub => { storeHistory => 'run' };
has lockTimes => (
is => 'rw',
@ -53,15 +52,21 @@ sub init {
grep { /\d+/ }
split /\s+/, $self->conf->{bruteForceProtectionLockTimes};
@{ $self->lockTimes } = ( 5, 15, 60, 300, 600 )
unless $lockTimes;
$self->logger->warn( 'Number of incremental lock time values ('
. "$lockTimes) is higher than failed logins history ("
. $self->conf->{failedLoginNumber}
. ')' )
if ( $lockTimes > $self->conf->{failedLoginNumber} );
unless ($lockTimes) {
@{ $self->lockTimes } = ( 5, 15, 60, 300, 600 );
$lockTimes = 5;
}
if ( $lockTimes > $self->conf->{failedLoginNumber} ) {
$self->logger->warn( 'Number of incremental lock time values ('
. "$lockTimes) is higher than failed logins history ("
. $self->conf->{failedLoginNumber}
. ')' );
splice @{ $self->lockTimes }, $self->conf->{failedLoginNumber};
$lockTimes = $self->conf->{failedLoginNumber};
}
my $sum = $self->conf->{bruteForceProtectionMaxAge};
my $sum = $self->conf->{bruteForceProtectionMaxAge} * ( 1 + $self->conf->{failedLoginNumber} - $lockTimes );
$sum += $_ foreach @{ $self->lockTimes };
$self->maxAge($sum);
}

View File

@ -60,7 +60,7 @@ sub changeUrldc {
$self->logger->debug( "CDA redirection to " . $req->{urldc} );
}
PE_OK;
return PE_OK;
}
1;

View File

@ -22,23 +22,22 @@ sub init {
return 0;
}
$self->addUnauthRoute( checkstate => 'check', ['GET'] );
$self->addAuthRoute( checkstate => 'check', ['GET'] );
$self->addAuthRoute( checkstate => 'check', ['GET'] );
return 1;
}
sub check {
my ( $self, $req ) = @_;
my @rep;
unless ($req->param('secret')
and $req->param('secret') eq $self->conf->{checkStateSecret} )
{
return $self->p->sendError( $req, 'Bad secret' );
}
return $self->p->sendError( $req, 'Bad secret' )
unless ( $req->param('secret')
and $req->param('secret') eq $self->conf->{checkStateSecret} );
$req->steps( [ 'controlUrl', @{ $self->p->beforeAuth } ] );
my $res = $self->p->process($req);
if ( $res > 0 ) {
push @rep, "Bad result before auth: $res";
}
if ( my $user = $req->param('user') and my $pwd = $req->param('password') )
{
$req->user($user);
@ -59,12 +58,9 @@ sub check {
}
$self->p->deleteSession($req);
}
if (@rep) {
return $self->p->sendError( $req, join( ",\n", @rep ), 500 );
}
else {
return $self->p->sendJSONresponse( $req, { result => 1 } );
}
return $self->p->sendError( $req, join( ",\n", @rep ), 500 ) if (@rep);
return $self->p->sendJSONresponse( $req, { result => 1 } );
}
1;

View File

@ -440,7 +440,14 @@ sub _headers {
$self->p->HANDLER->headersInit( $self->{conf} );
$self->logger->debug(
"Return \"$attrs->{ $self->{conf}->{whatToTrace} }\" headers");
return $self->p->HANDLER->checkHeaders( $req, $attrs );
return $self->p->HANDLER->checkHeaders( $req, $attrs )
if ( $self->conf->{checkUserDisplayEmptyHeaders} );
$self->logger->debug("Remove empty headers");
my @headers = grep $_->{value} =~ /.+/,
@{ $self->p->HANDLER->checkHeaders( $req, $attrs ) };
return \@headers;
}
sub _splitAttributes {
@ -456,7 +463,7 @@ sub _splitAttributes {
if ( $element->{key} eq 'groups' ) {
$self->logger->debug('Key "groups" found');
my $separator = $self->{conf}->{multiValuesSeparator};
my @tmp = split /\Q$separator/, $element->{value};
my @tmp = split /\Q$separator/, $element->{value};
$grps = [ map { { value => $_ } } sort @tmp ];
next;
}
@ -499,8 +506,8 @@ sub _splitAttributes {
sub _removePersistentAttributes {
my ( $self, $attrs ) = @_;
my $regex = join '|', split /\s+/, $self->persistentAttrs;
my @keys = grep /$regex/, keys %$attrs;
my $regex = join '|', split /\s+/, $self->persistentAttrs;
my @keys = grep /$regex/, keys %$attrs;
$self->logger->debug("Remove persistent session attributes");
delete @$attrs{@keys};

View File

@ -81,7 +81,7 @@ sub display {
sub run {
my ( $self, $req ) = @_;
my ( $msg, $decryptedValue ) = ( '', '' );
my $msg = my $decryptedValue = '';
# Check access rules
unless ( $self->rule->( $req, $req->userData ) ) {

View File

@ -21,12 +21,12 @@ sub run {
if ( $req->env->{HTTP_HOST}
and $self->conf->{portal} =~ /\Q$req->{env}->{HTTP_HOST}/ )
{
my $delta = time() - $req->{sessionInfo}->{_utime};
my $delta = time - $req->{sessionInfo}->{_utime};
$self->logger->debug( "Delta with last Authn -> " . $delta );
$delta <= $self->conf->{portalForceAuthnInterval}
? return PE_OK
: return PE_MUSTAUTHN;
return $delta <= $self->conf->{portalForceAuthnInterval}
? PE_OK
: PE_MUSTAUTHN;
}
}

View File

@ -37,9 +37,8 @@ sub run {
: ""
)
);
unless ( $req->info ) {
$req->info( $self->loadTemplate( $req, 'noHistory' ) );
}
$req->info( $self->loadTemplate( $req, 'noHistory' ) )
unless ( $req->info );
return PE_INFO;
}
return PE_OK;

View File

@ -77,7 +77,7 @@ sub run {
. $self->_mkRemoveOtherLink($req) )
if ( $self->conf->{notifyOther} and @$otherSessions );
PE_OK;
return PE_OK;
}
# Build the removeOther link

View File

@ -20,7 +20,7 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
sub init {
my ($self) = @_;
$self->addUnauthRoute( portalStatus => 'status', ['GET'] );
$self->addAuthRoute( portalStatus => 'status', ['GET'] );
$self->addAuthRoute( portalStatus => 'status', ['GET'] );
return 1;
}
@ -41,9 +41,8 @@ sub status {
}
}
}
unless ($out) {
return $self->p->sendError( $req, 'No status connection' );
}
return $self->p->sendError( $req, 'No status connection' )
unless ($out);
$p->print("STATUS json=1$args\n");
while ( $_ = $out->getline ) {

View File

@ -32,6 +32,12 @@ our %demoAccounts = (
},
);
our %demoGroups = (
'timelords' => [qw(dwho)],
'earthlings' => [qw(msmith rtyler)],
'users' => [qw(dwho msmith rtyler)],
);
# INITIALIZATION
sub init {
@ -82,6 +88,21 @@ sub setSessionInfo {
# Do nothing
# @return Lemonldap::NG::Portal constant
sub setGroups {
my ( $self, $req ) = @_;
my $groups = $req->sessionInfo->{groups} || '';
my $hGroups = $req->sessionInfo->{hGroups} || {};
for my $grp ( keys %demoGroups ) {
if ( grep { $_ eq $req->user } @{ $demoGroups{$grp} } ) {
$hGroups->{$grp} = {};
$groups =
($groups)
? $groups . $self->conf->{multiValuesSeparator} . $grp
: $grp;
}
}
$req->sessionInfo->{groups} = $groups;
$req->sessionInfo->{hGroups} = $hGroups;
PE_OK;
}

View File

@ -1 +1,2 @@
(function(){var r,e,n,t,o;n=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},r=function(e,r,t){var o;if(console.log("Error",t),(o=JSON.parse(e.responseText))&&o.error)return o=o.error.replace(/.* /,""),console.log("Returned error",o),n(o,"warning")},t="",e=function(e){return n("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:e},error:r,success:function(e){var r;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),n(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),$("#serialized").text(r),e.newkey?n("yourNewTotpKey","warning"):n("yourTotpKey","success"),t=e.token):n("PE24","danger")}})},o=function(){var e;return(e=$("#code").val())?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:t,code:e,TOTPName:$("#TOTPName").val()},error:r,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?n(e.error,"warning"):n(e.error,"danger"):n("yourKeyIsRegistered","success")}}):n("fillTheForm","warning")},$(document).ready(function(){return e(0),$("#changekey").on("click",function(){return e(1)}),$("#verify").on("click",function(){return o()})})}).call(this);
(function(){var e,r,t,o,n;t=function(e,r){return $("#msg").html(window.translate(e)),$("#color").removeClass("message-positive message-warning message-danger alert-success alert-warning alert-danger"),$("#color").addClass("message-"+r),"positive"===r&&(r="success"),$("#color").addClass("alert-"+r)},e=function(e,r,o){var n;if(console.log("Error",o),(n=JSON.parse(e.responseText))&&n.error)return n=n.error.replace(/.* /,""),console.log("Returned error",n),t(n,"warning")},o="",r=function(r){return t("yourTotpKey","warning"),$.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:r},error:e,success:function(e){var r;return e.error?(e.error.match(/totpExistingKey/)&&$("#divToHide").hide(),t(e.error,"warning")):e.portal&&e.user&&e.secret?($("#divToHide").show(),r="otpauth://totp/"+escape(e.portal)+":"+escape(e.user)+"?secret="+e.secret+"&issuer="+escape(e.portal),6!==e.digits&&(r+="&digits="+e.digits),30!==e.interval&&(r+="&period="+e.interval),new QRious({element:document.getElementById("qr"),value:r,size:150}),$("#serialized").text(r),e.newkey?t("yourNewTotpKey","warning"):t("yourTotpKey","success"),o=e.token):t("PE24","danger")}})},n=function(){var r;return r=$("#code").val(),r?$.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:o,code:r,TOTPName:$("#TOTPName").val()},error:e,success:function(e){return e.error?e.error.match(/bad(Code|Name)/)?t(e.error,"warning"):t(e.error,"danger"):t("yourKeyIsRegistered","success")}}):t("fillTheForm","warning")},$(document).ready(function(){return r(0),$("#changekey").on("click",function(){return r(1)}),$("#verify").on("click",function(){return n()})})}).call(this);
//# sourceMappingURL=lemonldap-ng-portal/site/htdocs/static/common/js/totpregistration.min.js.map

View File

@ -1 +1 @@
{"version":3,"sources":["lemonldap-ng-portal/site/htdocs/static/common/js/totpregistration.js"],"names":["displayError","getKey","setMsg","token","verify","msg","level","$","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","reset","ajax","type","url","portal","dataType","data","newkey","success","s","match","hide","user","secret","show","escape","digits","interval","QRious","element","document","getElementById","value","size","text","val","code","TOTPName","ready","on","call","this"],"mappings":"CAMA,WACE,IAAIA,EAAcC,EAAQC,EAAQC,EAAOC,EAEzCF,EAAS,SAASG,EAAKC,GAOrB,OANAC,EAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,4FACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCN,EAAe,SAASa,EAAGC,EAAQC,GACjC,IAAIC,EAGJ,GAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,OAFAN,EAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GACvBd,EAAOc,EAAK,YAIvBb,EAAQ,GAERF,EAAS,SAASuB,GAEhB,OADAtB,EAAO,cAAe,WACfK,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJC,OAAQP,GAEVF,MAAOtB,EACPgC,QAAS,SAASF,GAChB,IAAQG,EACR,OAAIH,EAAKR,OACHQ,EAAKR,MAAMY,MAAM,oBACnB3B,EAAE,cAAc4B,OAEXjC,EAAO4B,EAAKR,MAAO,YAEtBQ,EAAKF,QAAUE,EAAKM,MAAQN,EAAKO,QAGvC9B,EAAE,cAAc+B,OAChBL,EAAI,kBAAqBM,OAAOT,EAAKF,QAAW,IAAOW,OAAOT,EAAKM,MAAS,WAAaN,EAAKO,OAAS,WAAcE,OAAOT,EAAKF,QAC7G,IAAhBE,EAAKU,SACPP,GAAK,WAAaH,EAAKU,QAEH,KAAlBV,EAAKW,WACPR,GAAK,WAAaH,EAAKW,UAEpB,IAAIC,OAAO,CACdC,QAASC,SAASC,eAAe,MACjCC,MAAOb,EACPc,KAAM,MAERxC,EAAE,eAAeyC,KAAKf,GAClBH,EAAKC,OACP7B,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBC,EAAQ2B,EAAK3B,OArBXD,EAAO,OAAQ,cA0B9BE,EAAS,WACP,IAAI6C,EAEJ,OADAA,EAAM1C,EAAE,SAAS0C,OAIR1C,EAAEkB,KAAK,CACZC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,KAAM,CACJ3B,MAAOA,EACP+C,KAAMD,EACNE,SAAU5C,EAAE,aAAa0C,OAE3B3B,MAAOtB,EACPgC,QAAS,SAASF,GAChB,OAAIA,EAAKR,MACHQ,EAAKR,MAAMY,MAAM,kBACZhC,EAAO4B,EAAKR,MAAO,WAEnBpB,EAAO4B,EAAKR,MAAO,UAGrBpB,EAAO,sBAAuB,cApBpCA,EAAO,cAAe,YA2BjCK,EAAEqC,UAAUQ,MAAM,WAKhB,OAJAnD,EAAO,GACPM,EAAE,cAAc8C,GAAG,QAAS,WAC1B,OAAOpD,EAAO,KAETM,EAAE,WAAW8C,GAAG,QAAS,WAC9B,OAAOjD,UAIVkD,KAAKC"}
{"version":3,"sources":["lemonldap-ng-portal/site/htdocs/static/common/js/totpregistration.js"],"names":["displayError","getKey","setMsg","token","verify","msg","level","$","html","window","translate","removeClass","addClass","j","status","err","res","console","log","JSON","parse","responseText","error","replace","reset","ajax","type","url","portal","dataType","data","newkey","success","s","match","hide","user","secret","show","escape","digits","interval","QRious","element","document","getElementById","value","size","text","val","code","TOTPName","ready","on","call","this"],"mappings":"CAMA,WACE,GAAIA,GAAcC,EAAQC,EAAQC,EAAOC,CAEzCF,GAAS,SAASG,EAAKC,GAOrB,MANAC,GAAE,QAAQC,KAAKC,OAAOC,UAAUL,IAChCE,EAAE,UAAUI,YAAY,4FACxBJ,EAAE,UAAUK,SAAS,WAAaN,GACpB,aAAVA,IACFA,EAAQ,WAEHC,EAAE,UAAUK,SAAS,SAAWN,IAGzCN,EAAe,SAASa,EAAGC,EAAQC,GACjC,GAAIC,EAGJ,IAFAC,QAAQC,IAAI,QAASH,IACrBC,EAAMG,KAAKC,MAAMP,EAAEQ,gBACRL,EAAIM,MAGb,MAFAN,GAAMA,EAAIM,MAAMC,QAAQ,MAAO,IAC/BN,QAAQC,IAAI,iBAAkBF,GACvBd,EAAOc,EAAK,YAIvBb,EAAQ,GAERF,EAAS,SAASuB,GAEhB,MADAtB,GAAO,cAAe,WACfK,EAAEkB,MACPC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,MACEC,OAAQP,GAEVF,MAAOtB,EACPgC,QAAS,SAASF,GAChB,GAAQG,EACR,OAAIH,GAAKR,OACHQ,EAAKR,MAAMY,MAAM,oBACnB3B,EAAE,cAAc4B,OAEXjC,EAAO4B,EAAKR,MAAO,YAEtBQ,EAAKF,QAAUE,EAAKM,MAAQN,EAAKO,QAGvC9B,EAAE,cAAc+B,OAChBL,EAAI,kBAAqBM,OAAOT,EAAKF,QAAW,IAAOW,OAAOT,EAAKM,MAAS,WAAaN,EAAKO,OAAS,WAAcE,OAAOT,EAAKF,QAC7G,IAAhBE,EAAKU,SACPP,GAAK,WAAaH,EAAKU,QAEH,KAAlBV,EAAKW,WACPR,GAAK,WAAaH,EAAKW,UAEpB,GAAIC,SACPC,QAASC,SAASC,eAAe,MACjCC,MAAOb,EACPc,KAAM,MAERxC,EAAE,eAAeyC,KAAKf,GAClBH,EAAKC,OACP7B,EAAO,iBAAkB,WAEzBA,EAAO,cAAe,WAEjBC,EAAQ2B,EAAK3B,OArBXD,EAAO,OAAQ,cA0B9BE,EAAS,WACP,GAAI6C,EAEJ,OADAA,GAAM1C,EAAE,SAAS0C,MACZA,EAGI1C,EAAEkB,MACPC,KAAM,OACNC,IAAKC,OAAS,2BACdC,SAAU,OACVC,MACE3B,MAAOA,EACP+C,KAAMD,EACNE,SAAU5C,EAAE,aAAa0C,OAE3B3B,MAAOtB,EACPgC,QAAS,SAASF,GAChB,MAAIA,GAAKR,MACHQ,EAAKR,MAAMY,MAAM,kBACZhC,EAAO4B,EAAKR,MAAO,WAEnBpB,EAAO4B,EAAKR,MAAO,UAGrBpB,EAAO,sBAAuB,cApBpCA,EAAO,cAAe,YA2BjCK,EAAEqC,UAAUQ,MAAM,WAKhB,MAJAnD,GAAO,GACPM,EAAE,cAAc8C,GAAG,QAAS,WAC1B,MAAOpD,GAAO,KAETM,EAAE,WAAW8C,GAAG,QAAS,WAC9B,MAAOjD,WAIVkD,KAAKC","file":"lemonldap-ng-portal/site/htdocs/static/common/js/totpregistration.min.js"}

View File

@ -83,7 +83,7 @@
"PE91":"Zugang zum OID-Service nicht genehmigt",
"PE92":"Zugang zum GET-Service nicht genehmigt",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE94":"Ein gefordertes Attribut ist nicht verfügbar",
"PE95":"Access not granted on DECRYPT service",
"PE96":"Invalid verification code",
"PE97":"Please select your new certificate",
@ -166,12 +166,12 @@
"gplSoft":"Freie Software, die von der GPL-Lizenz abgedeckt wird",
"groups_sso":"SSO GROUPS",
"headers":"HEADERS",
"hello":"Hello",
"hello":"Hallo",
"hide":"Hide",
"id":"ID",
"imSure":"Ich bin sicher",
"info":"Information",
"ipAddr":"IP Adresse",
"ipAddr":"IP address",
"key":"Key",
"lastFailedLogins":"Letzte fehlgeschlagene Anmeldungen",
"lastLogins":"Letzte Anmeldungen",
@ -180,7 +180,7 @@
"linkValidUntilCertif":"This message contains a link to reset your certificate, this link is valid until ",
"loginHistory":"Anmeldeverlauf",
"login":"Anmelden",
"logout":"Abmelden",
"logout":"Logout",
"logoutConfirm":"Willst du dich abmelden ?",
"logoutFromOtherApp":"Von anderen Anwendungen abmelden ...",
"logoutFromSP":"Von Dienstanbietern abmelden ...",
@ -271,7 +271,7 @@
"totpExistingKey":"Es existiert bereits ein TOTP-Secret",
"touchU2fDevice":"Please touch the flashing U2F device now.",
"touchU2fDeviceOrEnterTotp":"Please touch the flashing U2F device or enter TOTP code.",
"type":"Typ",
"type":"Type",
"UA":"User agent",
"u2f":"U2F Key",
"u2fFailed":"U2F-Überprüfung fehlgeschlagen. Versuchen Sie es erneut oder wenden Sie sich an Ihren Administrator",

View File

@ -115,7 +115,7 @@
"cancel":"Cancel",
"captcha":"Captcha",
"certificateReset":"Reset my certificate",
"changeKey": "Generate new key",
"changeKey":"Generate new key",
"changePwd":"Change your password",
"checkLastLogins":"Check my last logins",
"checkUser":"Check user SSO profile",
@ -202,14 +202,14 @@
"noNotification":"None accepted notification found",
"notAnEncryptedValue":"It is not an encrypted value",
"notAuthorized":"You're not authorized to do this",
"notFound": "Not found: you try to access to an unavailable page",
"notFound":"Not found: you try to access to an unavailable page",
"noTOTPFound":"No TOTP found",
"noU2FKeyFound": "No U2F key found",
"noU2FKeyFound":"No U2F key found",
"notificationNotFound":"Notification not found in DataBase",
"notificationRetreiveFailed":"Unable to retreive notification",
"notificationsExplorer":"Notifications explorer",
"oidcConsent":"The application %s would like to:",
"oidcConsents": "OIDC consents",
"oidcConsents":"OIDC consents",
"oidcConsentsFull":"OpenID Connect consents",
"oneExpired2Fremoved":"An expired 2F device has been removed!",
"openidAp":"Do you agree to provide the following parameters?",
@ -220,14 +220,14 @@
"openSessionSpace":"This space allow you to open a SSO session. This will help you to securely access to all applications authorized by your profile.",
"openSSOSession":"Open your SSO session",
"otherSessions":"Other active sessions",
"password": "Password",
"passwordPolicy": "Please respect the following policy:",
"passwordPolicyMinSize": "Minimal size:",
"passwordPolicyMinLower": "Minimal lower characters:",
"passwordPolicyMinUpper": "Minimal upper characters:",
"passwordPolicyMinDigit": "Minimal digit characters:",
"ppGrace": "authentications remaining, change your password!",
"proxyError": "Bad gateway: unable to join remote server",
"password":"Password",
"passwordPolicy":"Please respect the following policy:",
"passwordPolicyMinSize":"Minimal size:",
"passwordPolicyMinLower":"Minimal lower characters:",
"passwordPolicyMinUpper":"Minimal upper characters:",
"passwordPolicyMinDigit":"Minimal digit characters:",
"ppGrace":"authentications remaining, change your password!",
"proxyError":"Bad gateway: unable to join remote server",
"pwd":"Password",
"pwdChange":"Password change",
"pwdChanged":"Your password has been successfully changed!",
@ -239,9 +239,9 @@
"redirectionInProgress":"Redirection in progress...",
"redirectionToIdp":"Redirection to your Identity Provider",
"reference":"Reference",
"refreshrights": "Refresh my rights",
"refreshrights":"Refresh my rights",
"refuse":"Refuse",
"register": "Register",
"register":"Register",
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
"rememberChoice":"Remember my choice",
"removeOtherSessions":"Remove other sessions",
@ -251,7 +251,7 @@
"resetCertificateOK":"Your certificate was reset sucessfully",
"resetPwd":"Reset my password",
"rest2f":"Verification code",
"rightsReloadNeedsLogout": "Rights reloads need to logout and login again",
"rightsReloadNeedsLogout":"Rights reloads need to logout and login again",
"scope":"Scope",
"search":"Search",
"seconds":"seconds",
@ -265,23 +265,23 @@
"spoofId":"Spoofed Id",
"SSOSessionInactive":"SSO session inactive",
"startTime":"Creation date",
"stayConnected": "Stay connected on this device",
"stayConnected":"Stay connected on this device",
"submit":"Submit",
"switchContext":"Switch context",
"totp2f":"OTP App",
"totpExistingKey":"A TOTP secret already exists",
"touchU2fDevice": "Please touch the flashing U2F device now.",
"touchU2fDeviceOrEnterTotp": "Please touch the flashing U2F device or enter TOTP code.",
"touchU2fDevice":"Please touch the flashing U2F device now.",
"touchU2fDeviceOrEnterTotp":"Please touch the flashing U2F device or enter TOTP code.",
"type":"Type",
"UA":"User agent",
"u2f":"U2F Key",
"u2fFailed": "U2F verification failed. Retry or contact your administrator",
"u2fPermission": "You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.",
"u2fWelcome": "U2F device management",
"unableToGetKey": "Unable to access to your key. Retry or contact your administrator",
"u2fFailed":"U2F verification failed. Retry or contact your administrator",
"u2fPermission":"You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.",
"u2fWelcome":"U2F device management",
"unableToGetKey":"Unable to access to your key. Retry or contact your administrator",
"unknownAction":"Unknown action",
"unregister": "Unregister",
"updateCdc": "Update Common Domain Cookie",
"unregister":"Unregister",
"updateCdc":"Update Common Domain Cookie",
"updateTime":"Update date",
"upgradeSession":"Upgrade session",
"user":"User",
@ -289,7 +289,7 @@
"utotp2f":"TOTP-or-U2F",
"value":"Value",
"validationDate":"Validation date",
"verify": "Verify",
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
@ -307,7 +307,7 @@
"yourKeyIsUnregistered":"Your key has been unregistered",
"yourKeyIsVerified":"Your key is verified",
"yourNewTotpKey":"Your new TOTP key, please test it and enter the code",
"yourOffline": "Access your account while you are offline",
"yourOffline":"Access your account while you are offline",
"yourPhone":"Know your phone number",
"yourProfile":"Know your profile",
"yourTotpKey":"Your TOTP key",

View File

@ -167,7 +167,7 @@
"groups_sso":"GRUPPI SSO",
"headers":"INTESTAZIONI",
"hello":"Salve",
"hide":"Hide",
"hide":"Nascondi",
"id":"Id",
"imSure":"Sono sicuro",
"info":"Informazioni",
@ -204,7 +204,7 @@
"notFound":"Non trovato: si tenta di accedere ad una pagina non disponibile",
"noTOTPFound":"Nessun TOTP trovato",
"noU2FKeyFound":"Nessuna chiave U2F trovata",
"notificationNotFound":"Notification not found in DataBase",
"notificationNotFound":"La notifica non é stata trovata",
"notificationRetreiveFailed":"Unable to retreive notification",
"notificationsExplorer":"Notifications explorer",
"oidcConsent":"L'applicazione %s vorrebbe sapere:",
@ -237,7 +237,7 @@
"redirectedIn":"Sarai reindirizzato in 30 secondi",
"redirectionInProgress":"Reindirizzamento in corso ...",
"redirectionToIdp":"Reindirizzamento al tuo provider di identità",
"reference":"Reference",
"reference":"Riferimento",
"refreshrights":"Aggiorna i miei diritti",
"refuse":"Rifiuta",
"register":"Registra",

View File

@ -83,7 +83,7 @@
"PE91":"Onbevoegde toegang tot de OID-service",
"PE92":"Onbevoegde toegang tot de GET-service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE94":"Een vereist attribuut is niet beschikbaar",
"PE95":"Access not granted on DECRYPT service",
"PE96":"Invalid verification code",
"PE97":"Please select your new certificate",

View File

@ -83,7 +83,7 @@
"PE91":"Access not granted on OID service",
"PE92":"Access not granted on GET service",
"PE93":"Access not granted on IMPERSONATION service",
"PE94":"A required attribute is not available",
"PE94":"Un atribut solicitate nu sunt disponibile",
"PE95":"Access not granted on DECRYPT service",
"PE96":"Invalid verification code",
"PE97":"Please select your new certificate",

View File

@ -168,7 +168,7 @@
"groups_sso":"TOA GRUPLARI",
"headers":"BAŞLIKLAR",
"hello":"Merhaba",
"hide":"Hide",
"hide":"Gizle",
"id":"ID",
"imSure":"Eminim",
"info":"Bilgi",
@ -205,7 +205,7 @@
"notFound":"Bulunamadı: mevcut olmayan bir sayfaya erişmeyi deniyorsunuz",
"noTOTPFound":"TOTP bulunamadi",
"noU2FKeyFound":"U2F anahtarı bulunamadı",
"notificationNotFound":"Notification not found in DataBase",
"notificationNotFound":"Bildirim bulunamadı",
"notificationRetreiveFailed":"Unable to retreive notification",
"notificationsExplorer":"Notifications explorer",
"oidcConsent":"%s uygulaması şunları yapmak istiyor:",
@ -223,9 +223,9 @@
"password":"Parola",
"passwordPolicy":"Lütfen aşağıdaki ilkeye uyun:",
"passwordPolicyMinSize":"Minimum parola uzunluğu",
"passwordPolicyMinLower":"Minimum küçük harf karakter sayısı:",
"passwordPolicyMinUpper":"Minimum büyük harf karakter sayısı:",
"passwordPolicyMinDigit":"Minimum rakam karakter sayısı:",
"passwordPolicyMinLower":"Minimum küçük harf karakter sayısı :",
"passwordPolicyMinUpper":"Minimum büyük harf karakter sayısı :",
"passwordPolicyMinDigit":"Minimum rakam karakter sayısı :",
"ppGrace":"kimlik doğrulaması kaldı, parolanızı değiştirin!",
"proxyError":"Kötü ağ geçidi: uzak sunucuya katılamıyor",
"pwd":"Parola",
@ -238,10 +238,10 @@
"redirectedIn":"30 saniye içerisinde yönlendirileceksiniz",
"redirectionInProgress":"Yeniden yönlendirme işlemi devam ediyor...",
"redirectionToIdp":"Kimlik sağlayıcınıza yönlendirme",
"reference":"Reference",
"reference":"Referans",
"refreshrights":"Yetkilerimi yenile",
"refuse":"Reddet",
"register":"Kayıt Ol",
"register":"Yeni hesap kaydet",
"registerRequestAlreadyIssued":"Bu hesap için kayıt olma isteği zaten şu tarihte alındı:",
"rememberChoice":"Seçimimi hatırla",
"removeOtherSessions":"Diğer oturumları sil",
@ -250,7 +250,7 @@
"resetFavApps":"Reset my favorite Apps.",
"resetCertificateOK":"Your certificate was reset sucessfully",
"resetPwd":"Parolamı sıfırla",
"rest2f":"Doğrulama kodu",
"rest2f":"Doğrulama Kodu",
"rightsReloadNeedsLogout":"Yetkiler yeniden yüklendiğinde çıkış yapıp tekrar giriş yapmanız gerekir",
"scope":"Kapsam",
"search":"Ara",

View File

@ -167,7 +167,7 @@
"groups_sso":"SSO GROUPS",
"headers":"HEADERS",
"hello":"Hello",
"hide":"Hide",
"hide":"Ẩn",
"id":"Id",
"imSure":"Tôi chắc chắn",
"info":"Thông tin",
@ -204,7 +204,7 @@
"notFound":"Không tìm thấy: bạn cố gắng truy cập vào một trang không có sẵn",
"noTOTPFound":"Đăng xuất khỏi các ứng dụng khác",
"noU2FKeyFound":"No U2F key found",
"notificationNotFound":"Notification not found in DataBase",
"notificationNotFound":"Không tìm thấy thông báo",
"notificationRetreiveFailed":"Unable to retreive notification",
"notificationsExplorer":"Notifications explorer",
"oidcConsent":"Ứng dụng %s muốn biết:",
@ -237,7 +237,7 @@
"redirectedIn":"Bạn sẽ được chuyển hướng trong 30 giây",
"redirectionInProgress":"Đang tiến hành chuyển hướng...",
"redirectionToIdp":"Chuyển hướng tới Bộ cung cấp Nhận dạng của bạn",
"reference":"Reference",
"reference":"Tham khảo",
"refreshrights":"Làm mới lại quyền của tôi",
"refuse":"Từ chối",
"register":"Đăng ký",

View File

@ -5,7 +5,11 @@
<TMPL_INCLUDE NAME="customLoginHeader.tpl">
<TMPL_IF NAME="AUTH_ERROR">
<div class="message message-<TMPL_VAR NAME="AUTH_ERROR_TYPE"> alert"><span trmsg="<TMPL_VAR NAME="AUTH_ERROR">"></span></div>
<div class="message message-<TMPL_VAR NAME="AUTH_ERROR_TYPE"> alert"><span trmsg="<TMPL_VAR NAME="AUTH_ERROR">"></span>
<TMPL_IF LOCKTIME>
<TMPL_VAR NAME="LOCKTIME"> <span trspan="seconds">seconds</span>.
</TMPL_IF>
</div>
</TMPL_IF>
<div class="card">

View File

@ -41,7 +41,7 @@ ok(
),
'Get Menu'
);
ok( $res->[2]->[0] =~ /<span trmsg="9"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="9">/,
'Rejected with PE_FIRSTACCESS' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%<span id="languages"></span>%, ' Language icons found' )
@ -73,7 +73,7 @@ ok(
),
'Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="5">/,
'jdoe rejected with PE_BADCREDENTIALS' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%<span trspan="connect">Connect</span>%,
@ -93,7 +93,7 @@ ok(
'Auth query'
);
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="5">/,
'dwho rejected with PE_BADCREDENTIALS' )
or print STDERR Dumper( $res->[2]->[0] );
count(1);

View File

@ -72,7 +72,7 @@ m#<img class="renewcaptchaclick" src="/static/common/icons/arrow_refresh.png" al
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="5">/,
'dalek rejected with PE_BADCREDENTIALS' )
or print STDERR Dumper( $res->[2]->[0] );

View File

@ -52,7 +52,7 @@ ok(
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'token' );
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="5">/,
'dalek rejected with PE_BADCREDENTIALS' )
or print STDERR Dumper( $res->[2]->[0] );

View File

@ -0,0 +1,254 @@
use Test::More;
use strict;
use IO::String;
BEGIN {
require 't/test-lib.pm';
}
SKIP: {
eval { require Convert::Base32 };
if ($@) {
skip 'Convert::Base32 is missing';
}
my $res;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
requireToken => 0,
loginHistoryEnabled => 1,
bruteForceProtection => 1,
bruteForceProtectionIncrementalTempo => 1,
bruteForceProtectionMaxLockTime => 300,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
failedLoginNumber => 4,
}
}
);
## First successful connection
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'1st Auth query'
);
count(1);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# 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);
count(4);
# 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";
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' );
count(5);
$client->logout($id);
## First failed connection
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
),
'1st Bad Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%5 <span trspan="seconds">seconds</span>%,
'LockTime = 5' )
or print STDERR Dumper( $res->[2]->[0] );
count(3);
# Waiting
Time::Fake->offset("+3s");
## Try to connect
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
ok( $res->[2]->[0] =~ /<span trspan="enterTotpCode">/,
'Enter TOTP code' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
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),
accept => 'text/html',
),
'Post code'
);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%5 <span trspan="seconds">seconds</span>%,
'LockTime = 5' )
or print STDERR Dumper( $res->[2]->[0] );
count(4);
## Second failed connection
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
),
'2nd Bad Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%15 <span trspan="seconds">seconds</span>%,
'LockTime = 15' )
or print STDERR Dumper( $res->[2]->[0] );
count(3);
# Waiting
Time::Fake->offset("+10s");
## Try to connect
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
ok( $res->[2]->[0] =~ /<span trspan="enterTotpCode">/,
'Enter TOTP code' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
( $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),
accept => 'text/html',
),
'Post code'
);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%15 <span trspan="seconds">seconds</span>%,
'LockTime = 15' )
or print STDERR Dumper( $res->[2]->[0] );
count(4);
## Third failed connection
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
),
'3rd Bad Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/,
'Rejected -> Protection enabled' )
or print STDERR Dumper( $res->[2]->[0] );
ok( $res->[2]->[0] =~ m%60 <span trspan="seconds">seconds</span>%,
'LockTime = 60' )
or print STDERR Dumper( $res->[2]->[0] );
count(3);
# Waiting
Time::Fake->offset("+80s");
## Try to connect
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
ok( $res->[2]->[0] =~ /<span trspan="enterTotpCode"><\/span>/,
'Enter TOTP code' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
( $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),
accept => 'text/html',
),
'Post code'
);
count(2);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
$client->logout($id);
}
clean_sessions();
done_testing( count() );

View File

@ -1,7 +1,6 @@
use Test::More;
use strict;
use IO::String;
use Data::Dumper;
BEGIN {
require 't/test-lib.pm';

View File

@ -50,7 +50,7 @@ ok(
'Auth query'
);
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
ok( $res->[2]->[0] =~ /<span trmsg="5">/,
'dwho rejected with PE_BADCREDENTIALS' )
or print STDERR Dumper( $res->[2]->[0] );
count(1);
@ -105,7 +105,7 @@ ok(
);
count(1);
ok(
$res->[2]->[0] =~ /<span trmsg="41"><\/span><\/div>/,
$res->[2]->[0] =~ /<span trmsg="41">/,
'rtyler rejected with PE_SESSIONNOTGRANTED'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
@ -121,7 +121,7 @@ ok(
);
count(1);
ok(
$res->[2]->[0] =~ /<span trmsg="5"><\/span><\/div>/,
$res->[2]->[0] =~ /<span trmsg="5">/,
'rtyler rejected with PE_BADCREDENTIALS'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);

View File

@ -62,7 +62,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-warning alert"><span trmsg="1"></span></div>%,
m%<div class="message message-warning alert"><span trmsg="1">%,
'Found PE_SESSIONEXPIRED code'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);

View File

@ -61,7 +61,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-warning alert"><span trmsg="1"></span></div>%,
m%<div class="message message-warning alert"><span trmsg="1">%,
'Found PE_SESSIONEXPIRED code'
) or print STDERR Dumper( $res->[2]->[0] );
count(2);

View File

@ -125,6 +125,12 @@ ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%Auth-User: %, 'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%testHeader1: %, 'Found testHeader1' )
or explain( $res->[2]->[0], 'Header Key: testHeader1' );
ok( $res->[2]->[0] =~ m%testHeader2: %, 'Found testHeader2' )
or explain( $res->[2]->[0], 'Header Key: testHeader2' );
ok( $res->[2]->[0] !~ m%emptyHeader: %, 'emptyHeader not found' )
or explain( $res->[2]->[0], 'Header Key: emptyHeader' );
ok( $res->[2]->[0] =~ m%: rtyler%, 'Found rtyler' )
or explain( $res->[2]->[0], 'Header Value: rtyler' );
ok( $res->[2]->[0] =~ m%<div class="col">su</div>%, 'Found su' )
@ -134,7 +140,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">uid</td>%, 'Found uid' )
ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
'Found _whatToTrace' )
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
count(11);
count(14);
$query =~ s/user=dwho/user=msmith/;
$query =~

View File

@ -21,6 +21,7 @@ my $client = LLNG::Manager::Test->new( {
checkUserIdRule => '$uid ne "msmith"',
checkUserSearchAttributes => 'employee_nbr test1 _user test2 mail',
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyHeaders => 1,
checkUserDisplayEmptyValues => 1,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
@ -222,16 +223,20 @@ ok(
),
'POST checkuser'
);
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(2);
ok( $res->[2]->[0] =~ m%Auth-User: %, 'Found Auth-User' )
or explain( $res->[2]->[0], 'Header Key: Auth-User' );
ok( $res->[2]->[0] =~ m%testHeader1: %, 'Found testHeader1' )
or explain( $res->[2]->[0], 'Header Key: testHeader1' );
ok( $res->[2]->[0] =~ m%testHeader2: %, 'Found testHeader2' )
or explain( $res->[2]->[0], 'Header Key: testHeader2' );
ok( $res->[2]->[0] =~ m%emptyHeader: %, 'Found emptyHeader' )
or explain( $res->[2]->[0], 'Header Key: emptyHeader' );
ok( $res->[2]->[0] =~ m%: dwho<br/>%, 'Found dwho' )
or explain( $res->[2]->[0], 'Header Value: dwho' );
ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
@ -239,7 +244,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
or explain( $res->[2]->[0], 'Macro Key _whatToTrace' );
ok( $res->[2]->[0] =~ m%<td scope="row">dwho</td>%, 'Found dwho' )
or explain( $res->[2]->[0], 'Macro Value dwho' );
count(3);
count(7);
# Request with mail
$query =~ s/user=dwho/user=dwho%40badwolf.org/;

View File

@ -0,0 +1,316 @@
use Test::More;
use strict;
use IO::String;
require 't/test-lib.pm';
my $res;
my $file = "$main::tmpDir/20160530_msmith_dGVzdHJlZg==.json";
open F, "> $file" or die($!);
print F '[
{
"uid": "msmith",
"date": "2016-05-30",
"reference": "testref",
"title": "Test title",
"subtitle": "Test subtitle",
"text": "This is a test text",
"check": ["Accept test","Accept test2"]
}
]';
close F;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
loginHistoryEnabled => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
contextSwitchingRule => 1,
contextSwitchingIdRule => 1,
totp2fSelfRegistration => 1,
totp2fActivation => 1,
contextSwitchingStopWithLogout => 0,
notification => 1,
notificationStorage => 'File',
notificationStorageOptions => { dirName => $main::tmpDir },
}
}
);
## Try to authenticate
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=dwho'),
length => 23,
accept => 'text/html',
),
'Auth query'
);
count(1);
my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# 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);
count(4);
# 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";
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' );
count(5);
$client->logout($id);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'user', 'password' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
count(2);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# Get Menu
# ------------------------
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectOK($res);
ok(
$res->[2]->[0] =~ m%<span trspan="connectedAs">Connected as</span> rtyler%,
'Connected as rtyler'
) or print STDERR Dumper( $res->[2]->[0] );
expectAuthenticatedAs( $res, 'rtyler' );
ok(
$res->[2]->[0] =~
m%<span trspan="contextSwitching_ON">contextSwitching_ON</span>%,
'Connected as rtyler'
) or print STDERR Dumper( $res->[2]->[0] );
count(3);
# 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);
count(4);
# 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";
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' );
count(5);
# ContextSwitching form
# ------------------------
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'ContextSwitching form',
);
( $host, $url, $query ) =
expectForm( $res, undef, '/switchcontext', 'spoofId' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
'Found trspan="contextSwitching_ON"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
## POST form
$query =~ s/spoofId=/spoofId=dwho/;
ok(
$res = $client->_post(
'/switchcontext',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST switchcontext'
);
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
$id = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectAuthenticatedAs( $res, 'dwho' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
count(6);
# Stop ContextSwitching
# ------------------------
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Stop context switching',
);
$id = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
count(2);
expectAuthenticatedAs( $res, 'rtyler' );
# ContextSwitching form
# ------------------------
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'ContextSwitching form',
);
( $host, $url, $query ) =
expectForm( $res, undef, '/switchcontext', 'spoofId' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_ON">%,
'Found trspan="contextSwitching_ON"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_ON"' );
## POST form
$query =~ s/spoofId=/spoofId=msmith/;
ok(
$res = $client->_post(
'/switchcontext',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST switchcontext'
);
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
$id = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
expectAuthenticatedAs( $res, 'msmith' );
ok( $res->[2]->[0] =~ m%<span trspan="contextSwitching_OFF">%,
'Found trspan="contextSwitching_OFF"' )
or explain( $res->[2]->[0], 'trspan="contextSwitching_OFF"' );
count(6);
# Stop ContextSwitching
# ------------------------
ok(
$res = $client->_get(
'/switchcontext',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Stop context switching',
);
$id = expectCookie($res);
ok(
$res = $client->_get(
'/',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'Get Menu',
);
count(2);
expectAuthenticatedAs( $res, 'rtyler' );
$client->logout($id);
clean_sessions();
done_testing( count() );

View File

@ -2,19 +2,15 @@ use Test::More;
use strict;
use IO::String;
BEGIN {
require 't/test-lib.pm';
}
require 't/test-lib.pm';
my $res;
my $client = LLNG::Manager::Test->new( {
ini => {
logLevel => 'error',
authentication => 'Demo',
userDB => 'Same',
loginHistoryEnabled => 0,
brutForceProtection => 0,
portalMainLogo => 'common/logos/logo_llng_old.png',
requireToken => 0,
checkUser => 0,
@ -375,8 +371,8 @@ ok(
);
count(6);
ok( $res->[2]->[0] =~ m%<span trmsg="1"></span>%, 'Found PE_SESSIONEXPIRED' )
or explain( $res->[2]->[0], 'Sessuion expired' );
ok( $res->[2]->[0] =~ m%<span trmsg="1">%, 'Found PE_SESSIONEXPIRED' )
or explain( $res->[2]->[0], 'Session expired' );
ok(
$res = $client->_get(
'/',

View File

@ -55,7 +55,7 @@ ok(
),
'Auth query'
);
ok( $res->[2]->[0] =~ m%<span trmsg="40"></span>%, ' PE40 found' )
ok( $res->[2]->[0] =~ m%<span trmsg="40">%, ' PE40 found' )
or explain( $res->[2]->[0], "PE40 - Bad formed user" );
count(2);
@ -83,7 +83,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="5"></span></div>%,
m%<div class="message message-negative alert"><span trmsg="5">%,
' PE5 found'
) or explain( $res->[2]->[0], "PE5 - Forbidden identity" );
count(2);
@ -112,7 +112,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="93"></span>%,
m%<div class="message message-negative alert"><span trmsg="93">%,
' PE93 found'
) or explain( $res->[2]->[0], "PE93 - Impersonation service not allowed" );
count(2);
@ -287,10 +287,6 @@ m%<div class="alert alert-success"><div class="text-center"><b><span trspan="all
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] !~ m%<span trspan="groups_sso">%,
'trspan="groups_sso" NOT found' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
@ -309,7 +305,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
ok( $res->[2]->[0] =~ m%<td scope="row">testPrefix_groups</td>%,
'Found testPrefix_groups' )
or explain( $res->[2]->[0], 'testPrefix_groups' );
ok( $res->[2]->[0] =~ m%<td scope="row">su; su_test; test_su</td>%,
ok( $res->[2]->[0] =~ m%<td scope="row">[^<]*su; su_test; test_su</td>%,
'Found "su; su_test; test_su"' )
or explain( $res->[2]->[0], 'su' );
ok( $res->[2]->[0] =~ m%<td scope="row">testPrefix_uid</td>%,
@ -322,7 +318,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">test_impersonation</td>%,
or explain( $res->[2]->[0], 'test_impersonation' );
ok( $res->[2]->[0] =~ m%<td scope="row">rtyler/dwho</td>%, 'Found rtyler/dwo' )
or explain( $res->[2]->[0], 'Found rtyler/dwo' );
count(16);
count(15);
my %attributes = map /<td scope="row">(.+)?<\/td>/g, $res->[2]->[0];
ok( scalar keys %attributes == 35, 'Found 35 attributes' )

View File

@ -54,7 +54,7 @@ ok(
),
'Auth query'
);
ok( $res->[2]->[0] =~ m%<span trmsg="40"></span>%, ' PE40 found' )
ok( $res->[2]->[0] =~ m%<span trmsg="40">%, ' PE40 found' )
or explain( $res->[2]->[0], "PE40 - Bad formed user" );
count(2);
@ -82,7 +82,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="5"></span></div>%,
m%<div class="message message-negative alert"><span trmsg="5">%,
' PE5 found'
) or explain( $res->[2]->[0], "PE5 - Forbidden identity" );
count(2);
@ -111,7 +111,7 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="93"></span>%,
m%<div class="message message-negative alert"><span trmsg="93">%,
' PE93 found'
) or explain( $res->[2]->[0], "PE93 - Impersonation service not allowed" );
count(2);
@ -284,10 +284,6 @@ m%<div class="alert alert-success"><div class="text-center"><b><span trspan="all
ok( $res->[2]->[0] =~ m%<span trspan="headers">%, 'Found trspan="headers"' )
or explain( $res->[2]->[0], 'trspan="headers"' );
ok( $res->[2]->[0] !~ m%<span trspan="groups_sso">%,
'trspan="groups_sso" NOT found' )
or explain( $res->[2]->[0], 'trspan="groups_sso"' );
ok( $res->[2]->[0] =~ m%<span trspan="macros">%, 'Found trspan="macros"' )
or explain( $res->[2]->[0], 'trspan="macros"' );
ok( $res->[2]->[0] =~ m%<span trspan="attributes">%,
@ -306,7 +302,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">_whatToTrace</td>%,
ok( $res->[2]->[0] =~ m%<td scope="row">testPrefix_groups</td>%,
'Found testPrefix_groups' )
or explain( $res->[2]->[0], 'testPrefix_groups' );
ok( $res->[2]->[0] =~ m%<td scope="row">su; su_test; test_su</td>%,
ok( $res->[2]->[0] =~ m%<td scope="row">[^<]*su; su_test; test_su</td>%,
'Found "su; su_test; test_su"' )
or explain( $res->[2]->[0], 'su' );
ok( $res->[2]->[0] =~ m%<td scope="row">testPrefix_uid</td>%,
@ -324,7 +320,7 @@ ok( $res->[2]->[0] =~ m%<td scope="row">_session_id</td>%, 'Found _session_id' )
ok( $res->[2]->[0] =~ m%<td scope="row">_session_kind</td>%,
'Found _session_id' )
or explain( $res->[2]->[0], 'Found _session_kind' );
count(18);
count(17);
my %attributes = map /<td scope="row">(.+)?<\/td>/g, $res->[2]->[0];
ok( keys %attributes == 35, 'Found 35 attributes' )

View File

@ -72,11 +72,10 @@ ok(
),
'4th Bad Auth query -> Rejected'
);
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86">/, 'Protection enabled' );
count(2);
# Cool down
# Count down
Time::Fake->offset("+2s");
# Try to authenticate
@ -113,10 +112,8 @@ ok(
),
'Post code'
);
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
count(1);
ok( $res->[2]->[0] =~ /<span trmsg="86">/, 'Protection enabled' );
count(2);
# Cool down
Time::Fake->offset("+6s");
@ -143,7 +140,6 @@ ok(
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
'Found EXTCODE input'
) or print STDERR Dumper( $res->[2]->[0] );
count(1);
$query =~ s/code=/code=123456/;
ok(
@ -155,17 +151,16 @@ ok(
),
'Post code'
);
count(1);
count(2);
my $id = expectCookie($res);
ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
or print STDERR Dumper( $res->[2]->[0] );
count(1);
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
ok( @c == 5, 'Five entries found' )
ok( @c == 6, 'Six entries found' )
or print STDERR Dumper( $res->[2]->[0] );
count(1);
count(2);
$client->logout($id);
clean_sessions();

View File

@ -54,7 +54,10 @@
"domain": "example.com",
"exportedHeaders": {
"test1.example.com": {
"Auth-User": "$uid"
"Auth-User": "$uid",
"testHeader1": "testHeader_value",
"testHeader2": "' '",
"emptyHeader": "''"
},
"test2.example.com": {
"Auth-User": "$uid"