Forbid browsers to store users password & Improve unit tests (#1913)
This commit is contained in:
parent
94877793d4
commit
132f42d44c
4
COPYING
4
COPYING
|
@ -121,6 +121,10 @@ License: CC-3
|
|||
Comment: This work, "CustomAuth.png", is a derivative of
|
||||
"Noun project 1162.svg" by Christopher T. Howlett, under CC-BY-3.0.
|
||||
|
||||
Files: lemonldap-ng-portal/site/htdocs/static/common/fonts/password.ttf
|
||||
Copyright: 2007, the Tap2Play Team, https://git.tap2play.org.au/tap2play/web/tree/dev/fonts
|
||||
License: Expat
|
||||
|
||||
Files: lemonldap-ng-portal/site/htdocs/static/common/backgrounds/*
|
||||
Copyright: Various artists
|
||||
License: CC-BY-NC-ND-3.0 or GFDL-1.3
|
||||
|
|
|
@ -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(?:RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node)|S(?:erviceMetaDataAuthnContext|torageOptions))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions)|A(?:ppMetaData(?:(?:ExportedVar|Option)s|Node)|ttributes))|(?:ustomAddParam|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|howLanguages|slByAjax)|o(?:idc(?:ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|RPMetaDataOptions(?:LogoutSessionRequired|BypassConsent|RequirePKCE|Public)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|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|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?)?|y(?:Deleted|Other))|AjaxHook)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|d(?:isablePersistentStorage|biDynamicHashEnabled|ontCompactConf)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|rest(?:(?:Session|Config)Server|ExportSecretKeys)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs)|bruteForceProtection)$/;
|
||||
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|howLanguages|slByAjax)|o(?:idc(?:ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|RPMetaDataOptions(?:LogoutSessionRequired|BypassConsent|RequirePKCE|Public)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|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|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?)?|y(?:Deleted|Other))|AjaxHook)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|d(?:isablePersistentStorage|biDynamicHashEnabled|ontCompactConf)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|rest(?:(?:Session|Config)Server|ExportSecretKeys)|br(?:owsersDontStorePassword|uteForceProtection)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
|
||||
|
||||
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );
|
||||
|
||||
|
|
|
@ -605,6 +605,10 @@ sub attributes {
|
|||
'default' => 'TOTP,U2F,Yubikey',
|
||||
'type' => 'text'
|
||||
},
|
||||
'browsersDontStorePassword' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'bruteForceProtection' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
|
|
|
@ -865,6 +865,11 @@ sub attributes {
|
|||
default => '^[\w\.\-@]+$',
|
||||
documentation => 'Regular expression to validate login',
|
||||
},
|
||||
browsersDontStorePassword => {
|
||||
default => 0,
|
||||
type => 'bool',
|
||||
documentation => 'Avoid browsers to store users password',
|
||||
},
|
||||
useRedirectOnError => {
|
||||
type => 'bool',
|
||||
default => 1,
|
||||
|
|
|
@ -862,6 +862,7 @@ sub tree {
|
|||
help => 'security.html#configure_security_settings',
|
||||
nodes => [
|
||||
'userControl',
|
||||
'browsersDontStorePassword',
|
||||
'portalForceAuthn',
|
||||
'portalForceAuthnInterval',
|
||||
'key',
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"اسم المتغيرة خاطئ",
|
||||
"blackList":"القائمة السوداء",
|
||||
"browse":"تصفح",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"مستوى إثبات الهوية",
|
||||
"browserIdAutoLogin":"تسجيل الدخول التلقائي",
|
||||
"browserIdBackgroundColor":"لون الخلفية",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"Bad variable name",
|
||||
"blackList":"Black list",
|
||||
"browse":"Browse",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"Authentication level",
|
||||
"browserIdAutoLogin":"Automatic login",
|
||||
"browserIdBackgroundColor":"Background color",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"Bad variable name",
|
||||
"blackList":"Black list",
|
||||
"browse":"Browse",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"Authentication level",
|
||||
"browserIdAutoLogin":"Automatic login",
|
||||
"browserIdBackgroundColor":"Background color",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"Mauvais nom de variable",
|
||||
"blackList":"Liste noire",
|
||||
"browse":"Naviguer",
|
||||
"browsersDontStorePassword":"Interdire aux navigateurs de sauvegarder le mot de passe",
|
||||
"browserIdAuthnLevel":"Niveau d'authentification",
|
||||
"browserIdAutoLogin":"Authentification automatique",
|
||||
"browserIdBackgroundColor":"Couleur d'arrière plan",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"Nome variabile errato",
|
||||
"blackList":"Black list",
|
||||
"browse":"Naviga",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"Livello di autenticazione",
|
||||
"browserIdAutoLogin":"Login automatico",
|
||||
"browserIdBackgroundColor":"Colore di sfondo",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"Tên biến không hợp lệ",
|
||||
"blackList":"Danh sách đen",
|
||||
"browse":"Duyệt",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"Mức xác thực",
|
||||
"browserIdAutoLogin":"Đăng nhập tự động",
|
||||
"browserIdBackgroundColor":"Màu nền",
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
"badVariableName":"无效的 variable 名称",
|
||||
"blackList":"黑名单",
|
||||
"browse":"浏览",
|
||||
"browsersDontStorePassword":"Avoid browsers to store users password",
|
||||
"browserIdAuthnLevel":"认证等级",
|
||||
"browserIdAutoLogin":"自动登录",
|
||||
"browserIdBackgroundColor":"背景颜色",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -296,6 +296,7 @@ sub display {
|
|||
AUTH_ERROR_TYPE => $req->error_type,
|
||||
AUTH_URL => $req->{data}->{_url},
|
||||
LOGIN => $login,
|
||||
DONT_STORE_PASSWORD => $self->conf->{browsersDontStorePassword},
|
||||
CHECK_LOGINS => $self->conf->{portalCheckLogins},
|
||||
ASK_LOGINS => $req->param('checkLogins') || 0,
|
||||
DISPLAY_RESETPASSWORD => $self->conf->{portalDisplayResetPassword},
|
||||
|
|
|
@ -163,3 +163,15 @@ div.oidc_consent_message > ul {
|
|||
.progress-bar-animated {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input.key {
|
||||
font-family: 'password';
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'password';
|
||||
/*font-style: normal;*/
|
||||
/*font-weight: 400;*/
|
||||
src: url(/static/common/fonts/password.ttf);
|
||||
}
|
|
@ -1 +1 @@
|
|||
html,body{height:100%;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}#wrap{min-height:100%;height:auto;margin:0 auto -80px;padding:20px 0 80px}#footer{height:80px;background-color:#fff;background-color:rgba(255,255,255,0.9);text-align:center;padding-top:10px;overflow:hidden}#header img{background-color:#fff;background-color:rgba(255,255,255,0.8);margin-bottom:20px}.card,.navbar-light{background-color:#fff;background-color:rgba(255,255,255,0.9);background-image:none}.login,.password{text-align:center;padding:20px}div.form{margin:0 auto;max-width:330px}div.actions{margin:10px 0 0 0}div.actions a{margin-top:10px}.buttons{text-align:center;margin:10px 0 0 0;cursor:pointer}.btn{white-space:normal}.btn span.fa{padding-right:8px}li.ui-state-active{background-color:#fafafa;background-color:rgba(250,250,250,0.9)}#appslist,#password,#loginHistory,#logout,#oidcConsents{margin-top:20px}div.category{margin:10px 0;cursor:grab}div.application{margin:5px 0;overflow:hidden}div.application a,div.application a:hover{text-decoration:none}p.notifCheck label{margin-left:5px;margin-top:3px;display:inline-block}img.langicon{cursor:pointer}button.idploop{max-width:300px}button.idploop img{max-height:30px}div.oidc_consent_message>ul{text-align:left;list-style:circle}@media(min-width:768px){div.application{height:80px}div.application h4.appname{margin:0}#wrap{margin:0 auto -60px}#footer{height:60px}}.hiddenFrame{border:0;display:hidden;margin:0}.noborder{border:0}.max{width:100%}.link{cursor:pointer}.nodecor:hover,.nodecor:active.nodecor:focus{text-decoration:none}.fa.icon-blue{color:blue}.progress-bar-animated{width:100%}
|
||||
html,body{height:100%;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}#wrap{min-height:100%;height:auto;margin:0 auto -80px;padding:20px 0 80px}#footer{height:80px;background-color:#fff;background-color:rgba(255,255,255,0.9);text-align:center;padding-top:10px;overflow:hidden}#header img{background-color:#fff;background-color:rgba(255,255,255,0.8);margin-bottom:20px}.card,.navbar-light{background-color:#fff;background-color:rgba(255,255,255,0.9);background-image:none}.login,.password{text-align:center;padding:20px}div.form{margin:0 auto;max-width:330px}div.actions{margin:10px 0 0 0}div.actions a{margin-top:10px}.buttons{text-align:center;margin:10px 0 0 0;cursor:pointer}.btn{white-space:normal}.btn span.fa{padding-right:8px}li.ui-state-active{background-color:#fafafa;background-color:rgba(250,250,250,0.9)}#appslist,#password,#loginHistory,#logout,#oidcConsents{margin-top:20px}div.category{margin:10px 0;cursor:grab}div.application{margin:5px 0;overflow:hidden}div.application a,div.application a:hover{text-decoration:none}p.notifCheck label{margin-left:5px;margin-top:3px;display:inline-block}img.langicon{cursor:pointer}button.idploop{max-width:300px}button.idploop img{max-height:30px}div.oidc_consent_message>ul{text-align:left;list-style:circle}@media(min-width:768px){div.application{height:80px}div.application h4.appname{margin:0}#wrap{margin:0 auto -60px}#footer{height:60px}}.hiddenFrame{border:0;display:hidden;margin:0}.noborder{border:0}.max{width:100%}.link{cursor:pointer}.nodecor:hover,.nodecor:active.nodecor:focus{text-decoration:none}.fa.icon-blue{color:blue}.progress-bar-animated{width:100%}input.key{font-family:'password';width:100px}@font-face{font-family:'password';src:url(/static/common/fonts/password.ttf)}
|
BIN
lemonldap-ng-portal/site/htdocs/static/common/fonts/password.ttf
Normal file
BIN
lemonldap-ng-portal/site/htdocs/static/common/fonts/password.ttf
Normal file
Binary file not shown.
|
@ -17,7 +17,11 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-lock"></i> </span>
|
||||
</div>
|
||||
<input name="password" type="password" class="form-control" trplaceholder="password" required aria-required="true"/>
|
||||
<TMPL_IF NAME="DONT_STORE_PASSWORD">
|
||||
<input name="password" type="text" class="form-control key" trplaceholder="password" autocomplete="off" required aria-required="true"/>
|
||||
<TMPL_ELSE>
|
||||
<input name="password" type="password" class="form-control" trplaceholder="password" required aria-required="true"/>
|
||||
</TMPL_IF>
|
||||
</div>
|
||||
|
||||
<TMPL_IF NAME=CAPTCHA_SRC>
|
||||
|
|
|
@ -6,7 +6,7 @@ require 't/test-lib.pm';
|
|||
|
||||
my $res;
|
||||
|
||||
my $maintests = 16;
|
||||
my $maintests = 17;
|
||||
SKIP: {
|
||||
eval 'use GD::SecurityImage;use Image::Magick;';
|
||||
if ($@) {
|
||||
|
@ -15,11 +15,12 @@ SKIP: {
|
|||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
useSafeJail => 1,
|
||||
loginHistoryEnabled => 1,
|
||||
captcha_login_enabled => 1,
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
logLevel => 'error',
|
||||
useSafeJail => 1,
|
||||
browsersDontStorePassword => 1,
|
||||
loginHistoryEnabled => 1,
|
||||
captcha_login_enabled => 1,
|
||||
portalMainLogo => 'common/logos/logo_llng_old.png',
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -31,6 +32,12 @@ SKIP: {
|
|||
|
||||
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Unauth request' );
|
||||
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' );
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
m%<input name="password" type="text" class="form-control key" trplaceholder="password" autocomplete="off" required aria-required="true"/>%,
|
||||
'Password: Found text input'
|
||||
);
|
||||
|
||||
$query =~ s/.*\btoken=([^&]+).*/token=$1/;
|
||||
my $token;
|
||||
ok( $token = $1, ' Token value is defined' );
|
||||
|
|
|
@ -21,6 +21,13 @@ ok( $res = $client->_get( '/', accept => 'text/html' ), 'Unauth request' );
|
|||
count(1);
|
||||
|
||||
my ( $host, $url, $query ) = expectForm( $res, '#', undef, 'token' );
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
m%<input name="password" type="password" class="form-control" trplaceholder="password" required aria-required="true"/>%,
|
||||
'Password: Found password input'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$query =~ s/.*\b(token=[^&]+).*/$1/;
|
||||
|
||||
# Try to auth without token
|
||||
|
|
Loading…
Reference in New Issue
Block a user