Fix unit test

This commit is contained in:
Christophe Maudoux 2020-12-10 23:29:08 +01:00
commit b1232739a0
137 changed files with 1899 additions and 656 deletions

View File

@ -27,6 +27,9 @@ theses :
* **OAUTH2_USERINFO_ENDPOINT**: ``oauth2/userinfo``
* **OAUTH2_TOKEN_ENDPOINT**: ``oauth2/token``
* **OAUTH2_ID_MAP**: ``sub``
* **OAUTH2_USERNAME_MAP**: ``sub``
* **OAUTH2_FULLNAME_MAP**: ``name``
* **OAUTH2_EMAIL_MAP**: ``email``
.. danger::

View File

@ -246,7 +246,8 @@ Here are some recommended configurations:
_whatToTrace varchar(64),
_session_kind varchar(15),
user text,
_utime bigint
_utime bigint,
ipAddr varchar(64)
);
CREATE INDEX uid1 ON sessions (_whatToTrace) USING BTREE;
CREATE INDEX _s1 ON sessions (_session_kind);

View File

@ -18,7 +18,7 @@ Just enable it in the manager (section “plugins”).
displayed (by example: ``!$anonymous``)
- **Unrestricted users rule**: Rule to define which users can check
ALL users. ``Identities use rule`` is bypassed.
- **Hidden attributes**: Attributes not displayed
- **Hidden attributes**: Session attributes not displayed
- **Attributes used for searching sessions**: User's attributes used
for searching sessions in backend if ``whatToTrace`` fails. Useful
to look for sessions by mail or givenName. Let it blank to search
@ -31,11 +31,27 @@ Just enable it in the manager (section “plugins”).
even empty ones
- **Display persistent session data**: Rule to define which users can display
persistent session data
- **Hidden headers**: Sent headers whose value is masked except for unrestricted users.
Key is a Virtualhost name and value represents a headers list.
A blank value obfuscates ALL relative Virtualhost sent headers.
Note that just valued hearders are masked.
.. note::
By examples :
By example:
\* test1.example.com => ``Auth-User mail``
Just 'Auth-User' and 'mail' headers are masked if valued.
\* test2.example.com => '' ALL valued headers are masked.
Unrestricted users can see the masked headers.
.. note::
By example:
\* Search attributes => ``mail uid givenName``

View File

@ -503,6 +503,9 @@ Some options are available:
- Maintenance mode: reject all requests with a maintenance message
- Aliases: list of aliases for this virtual host *(avoid to rewrite
rules,...)*
- Access to trace: can be used for overwriting REMOTE_CUSTOM with a custom function.
Provide a comma separated parameters list with custom function path and args.
By example: My::accessToTrace, Doctor, Who
- Type: handler type (normal,
:doc:`ServiceToken Handler<servertoserver>`,
:doc:`DevOps Handler<devopshandler>`,...)
@ -515,6 +518,30 @@ Some options are available:
seconds. This TTL can be customized for each virtual host.
.. attention::
A hash reference containing $req, $session, $vhost, $custom and an array reference
with provided parameters is passed to the custom function.
::
package My;
sub accessToTrace {
my $hash = shift;
my $custom = $hash->{custom};
my $req = $hash->{req};
my $vhost = $hash->{vhost};
my $custom = $hash->{custom};
my $params = $hash->{params};
my $session = $hash->{session};
return "$custom alias $params->[0]_$params->[1]:$session->{groups}";
}
1;
.. danger::
A same virtual host can serve many locations. Each

View File

@ -10,7 +10,7 @@ extracted from the users database by the
:ref:`users module<start-authentication-users-and-password-databases>`.
To create a variable, you've just to map a user attributes in LL::NG
using ``Variables`` » ``Exported variables``. For each variable, The
using ``Variables`` » ``Exported variables``. For each variable, the
first field is the name which will be used in rules, macros or headers
and the second field is the name of the user database field.

View File

@ -25,19 +25,20 @@ Inside this jail, you can access to:
* Information about current request
* Extended functions:
* date_
* checkLogonHours_
* checkDate_
* basic_
* unicode2iso_
* iso2unicode_
* groupMatch_
* listMatch_
* inGroup_
* checkDate_
* checkLogonHours_
* date_
* encrypt_
* token_
* groupMatch_
* has2f_ (|new| in version 2.0.10)
* inGroup_ (|new| in version 2.0.8)
* isInNet6_
* varIsInUri_
* iso2unicode_
* listMatch_ (|new| in version 2.0.7)
* token_
* unicode2iso_
* varIsInUri_ (|new| in version 2.0.7)
.. |new| image:: /documentation/new.png
@ -238,6 +239,40 @@ Simple usage example:
groupMatch($hGroups, 'description', 'Service 1')
.. _has2f:
has2f
~~~~~
.. versionadded:: 2.0.10
This function tests if the current user has registered a second factor. The following types are supported:
* :doc:`TOTP<totp2f>`
* :doc:`U2F<u2f>`
* :doc:`UBK<yubikey2f>`
Example::
has2f()
has2f('UBK')
has2f('UBK') or has2f('TOTP')
.. warning::
Do **NOT** use this test to check if the user has **used** their second factor for logging in!
This test only checks if the user has registered a second factor. Regardless of their **current**
authentication level. It can be used to simplify second factor activation rules.
.. note::
Before version 2.0.10, you need to use the following syntax ::
$_2fDevices =~ /"type":\s*"TOTP"/s
.. _listMatch:
listMatch

View File

@ -57,6 +57,12 @@ logs, go into Manager, ``General Parameters`` > ``Logging`` >
User log samples
----------------
.. note::
The user name set in user log messages is configured with `whatToTrace` parameter, except
for messages corresponding to failed authentification, whe the user name logged is the
login used by the user.
Authentication:
::
@ -84,6 +90,12 @@ Logout:
[notice] User dwho has been disconnected from LDAP (81.20.13.21)
Password change:
::
[notice] Password changed for dwho (81.20.13.21)
Access to a CAS application non registered in configuration (when CAS server is open):
::

View File

@ -27,6 +27,16 @@ attribute you see there can be used in a rule!
In Perl, ``eq`` means *Equal* and must be used on strings.
``==`` should be used only on numbers
.. danger::
In Perl, ``@`` character means an array and ``%`` a hash!
If you want to write a macro with these characters, you have to escape them like this:
::
$my_email = "$uid\@my-domain.com"
$percent = "$rate\%more"
- Restricting access to specific groups
::
@ -64,6 +74,17 @@ attribute you see there can be used in a rule!
$_auth ne 'Demo'
- Checking if the user has a an **available** second factor.
::
# Since 2.0.10
has2f()
has2f('TOTP')
has2f('TOTP') or has2f('U2F')
# Before 2.0.10
$_2fDevices =~ /"type":\s*"TOTP"/s
.. tip::
@ -71,6 +92,7 @@ attribute you see there can be used in a rule!
strings. ``\b`` means *word Boundary*. (?:) means *non capturing*
parenthesis.
Using environment variables
---------------------------

View File

@ -6,8 +6,8 @@ Presentation
LemonLDAP::NG uses Safe jail to evaluate all expressions:
- Access rule
- Header
- Access rules
- Headers
- Form replay parameters
- Macros
- Groups
@ -31,3 +31,19 @@ to disable it.
To do this, go into Manager > General Parameters > Advanced Parameters >
Security > Use Safe Jail and disable it.
Assignment test
===============
Presentation
------------
Perl comparaisons are done by using ``eq`` for strings or ``==`` for integers.
To avoid an unwanted assignment like ``$authLevel = 5`` (BAD EXPRESSION!),
you can enable ``Avoid assignment in expressions`` option.
To do this, go into Manager > General Parameters > Advanced Parameters >
Security > Avoid assignment in expressions and enable it.
DISABLE by default.

View File

@ -7,15 +7,12 @@ options.
Disk cache (sessions an configuration)
--------------------------------------
::
chcon -R -t httpd_sys_rw_content_t /var/cache/lemonldap-ng
To persist the rule:
You need to set the correct context on the cache directory
::
semanage fcontext -a -t http_sys_content_t /var/cache/lemonldap-ng
semanage fcontext --add -t httpd_cache_t -f a '/var/cache/lemonldap-ng(/.*)?'
restorecon -R /var/cache/lemonldap-ng/
LDAP
----

View File

@ -74,6 +74,12 @@ In the manager (advanced parameters), you just have to enable it:
required even if users are not registered. This is automatically done
when "activation" is simply set to "on".
.. danger::
Range is tested backward and forward to prevent
positive or negative clock drift.
Enrollment
----------

View File

@ -21,6 +21,8 @@ backups and a rollback plan ready!
------
- New dependency: IO::Socket::Timeout
- TOTP check tolerates forward AND backward clock drift (totp2fRange)
- Avoid assignment in expressions option is disabled by default
2.0.9
-----
@ -31,7 +33,7 @@ backups and a rollback plan ready!
(see also `#2244 <https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues/2244>`__)
- SAML SOAP calls are now using ``text/xml`` instead of ``application/xml`` as the MIME Content Type, as required by `the SOAP standard <https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383526>`__
- Incremental lock times values can now be set in BruteForceProtection plugin through Manager.
It must be a list of comma separated values. Default values are ``5, 15, 60, 300, 600``
It MUST be a list of comma separated values. Default values are ``5, 15, 60, 300, 600``
Cookie issues with Chrome
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -190,14 +190,14 @@ use macros, local macros,...
.. attention::
- Since many HTTP servers refuse non ascii headers, it is recommended
to use encode_base64() function to transmit those headers
- Don't forget to add an empty string as second argument to
encode_base64 function to avoid a "newline" characters insertion in
result
- Header names must contain only letters and "-" character
- Header names must contain only letters and "-" character.
With Nginx, you can bypass this restriction by using
``underscores_in_headers on;`` directive

View File

@ -8,4 +8,16 @@ sub get_additional_arg {
return $_[0];
}
sub accessToTrace {
my $hash = shift;
my $custom = $hash->{custom};
my $req = $hash->{req};
my $vhost = $hash->{vhost};
my $custom = $hash->{custom};
my $params = $hash->{params};
my $session = $hash->{session};
return "$custom alias $params->[0]_$params->[1]:$session->{groups} with $session->{$params->[2]}";
}
1;

View File

@ -22,7 +22,7 @@ dirName=__pwd__/e2e-tests/conf
checkXSS = 0
portalSkin = bootstrap
staticPrefix = /static
languages = fr, en, vi, it, ar, de, zh, nl, es, pt, ro, tr
languages = fr, en, vi, it, ar, de, zh, nl, es, pt, ro, tr, zh_TW
templateDir = __pwd__/lemonldap-ng-portal/site/templates
portalStatus = 1
totp2fActivation = 1
@ -49,7 +49,7 @@ viewerAllowDiff = 1
staticPrefix = /static
instanceName = Demo
languages = fr, en, vi, ar, de, it, zh, tr
languages = fr, en, vi, ar, it, zh, tr, zh_TW, es
templateDir = __pwd__/lemonldap-ng-manager/site/templates
[node-handler]

View File

@ -164,6 +164,16 @@
"default": "accept"
}
},
"vhostOptions":{
"manager.example.com": {
"vhostMaintenance": 0,
"vhostPort": -1,
"vhostHttps": -1,
"vhostAliases": "",
"vhostServiceTokenTTL": -1,
"vhostAccessToTrace": "My::accessToTrace, Doctor, Who","vhostType":"Main"
}
},
"loginHistoryEnabled": 1,
"macros": {
"UA" : "$ENV{HTTP_USER_AGENT}",

View File

@ -196,7 +196,7 @@ staticPrefix = __PORTALSTATICDIR__
templateDir = __PORTALTEMPLATESDIR__
; languages: available languages for portal interface
languages = en, fr, vi, it, ar, de, fi, tr, pl
languages = en, fr, vi, it, ar, de, fi, tr, pl, zh_TW, es
; II - Optional parameters (overwrite configuration)
@ -383,7 +383,7 @@ staticPrefix = __MANAGERSTATICDIR__
templateDir = __MANAGERTEMPLATESDIR__
; languages: available languages for manager interface
languages = fr, en, it, vi, ar, tr, pl
languages = en, fr, it, vi, ar, tr, pl, zh_TW, es
; Manager modules enabled
; Set here the list of modules you want to see in manager interface

View File

@ -29,8 +29,8 @@ use constant DEFAULTCONFBACKEND => "File";
use constant DEFAULTCONFBACKENDOPTIONS => (
dirName => '/usr/local/lemonldap-ng/data/conf',
);
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)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Allow(?:PasswordGrant|Offline)|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:State|User|XSS)|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)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
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)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)s)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|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)|f(?:RemovedUseNotif|OnlyUpgrade)|kip(?:Upgrade|Renew)Confirmation|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Allow(?:PasswordGrant|Offline)|Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:freshMyRights|setPassword|gister)|CertificateResetByMail|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:o(?:ntextSwitching(?:Allowed2fModifications|StopWithLogout)|mpactConf|rsEnabled)|a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:State|User|XSS)|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)|re(?:st(?:(?:Password|Session|Config|Auth)Server|ExportSecretKeys)|freshSessions)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|d(?:is(?:ablePersistentStorage|playSessionId)|biDynamicHashEnabled)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|g(?:roupsBeforeMacros|lobalLogoutTimer)|a(?:voidAssignment|ctiveTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs)|wsdlServer)$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -22,7 +22,7 @@ our $specialNodeHash = {
};
our $doubleHashKeys = 'issuerDBGetParameters';
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|c(?:as(?:StorageOption|Attribute)|ustom(?:Plugins|Add)Param|ombModule)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|macro)s|o(?:idcS(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|penIdExportedVars)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|S(?:MTPTLSOpts|SLVarIf))';
our $simpleHashKeys = '(?:(?:c(?:as(?:StorageOption|Attribute)|ustom(?:Plugins|Add)Param|heckUserHiddenHeader|ombModule)|l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|macro)s|o(?:idcS(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|penIdExportedVars)|a(?:(?:daptativeAuthenticationLevelR|ut(?:hChoiceMod|oSigninR))ules|pplicationList)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|S(?:MTPTLSOpts|SLVarIf))';
our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:(?:UserAttribut|Servic|Rul)e|AuthnLevel)|(?:ExportedVar|Macro)s)';
our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:ProxiedServices|DisplayName|SortNumber|Gateway|Renew|Icon|Url)|ExportedVars)';
@ -30,7 +30,7 @@ our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:uth(?:orizationCodeExpiration|nLevel)|llow(?:PasswordGrant|Offline)|ccessTokenExpiration|dditionalAudiences)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|(?:ExportedVar|Macro)s)';
our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ign(?:S[LS]OMessage|atureMethod)|toreSAMLToken|[LS]OBinding|ortNumber)|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Re(?:questedAuthnContext|solutionRule|layStateURL)|Force(?:Authn|UTF8)|I(?:sPassive|con)|NameIDFormat)|ExportedAttributes|XML)';
our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:S(?:ign(?:S[LS]OMessage|atureMethod)|essionNotOnOrAfterTimeout)|N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|AuthnLevel|ForceUTF8)|(?:ExportedAttribute|Macro)s|XML)';
our $virtualHostKeys = '(?:vhost(?:A(?:uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)';
our $virtualHostKeys = '(?:vhost(?:A(?:ccessToTrace|uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)';
our $authParameters = {
adParams => [qw(ADPwdMaxAge ADPwdExpireWarning)],

View File

@ -9,6 +9,7 @@ use strict;
use Encode;
use MIME::Base64;
use Lemonldap::NG::Common::IPv6;
use JSON::XS;
#use AutoLoader qw(AUTOLOAD);
@ -18,7 +19,7 @@ our $VERSION = '2.1.0';
# Not that only functions, not methods, can be written here
our $functions =
[
qw(&checkLogonHours &date &checkDate &basic &unicode2iso &iso2unicode &groupMatch &isInNet6 &varIsInUri)
qw(&checkLogonHours &date &checkDate &basic &unicode2iso &iso2unicode &groupMatch &isInNet6 &varIsInUri &has2f)
];
## @function boolean checkLogonHours(string logon_hours, string syntax, string time_correction, boolean default_access)
@ -64,8 +65,8 @@ sub checkLogonHours {
# Use time_correction
if ($time_correction) {
my ( $sign, $time ) = ( $time_correction =~ /([+|-]?)(\d+)/ );
if ( $sign =~ /-/ ) { $hourpos -= $time; }
else { $hourpos += $time; }
if ( $sign =~ /-/ ) { $hourpos -= $time; }
else { $hourpos += $time; }
}
# Get the corresponding byte
@ -224,4 +225,26 @@ sub varIsInUri {
: $uri =~ /$wanteduri$attribute/o;
}
my $json = JSON::XS->new;
sub has2f {
my ( $session, $type ) = @_;
return 0 unless ( $session->{_2fDevices} );
my $_2fDevices = eval { $json->decode( $session->{_2fDevices} ); };
return 0 if ( $@ or ref($_2fDevices) ne "ARRAY" );
my $length = scalar @{$_2fDevices};
# Empty array
return 0 unless $length;
# Array has one value and we did not specify a type, succeed
if ($type) {
my @found = grep { lc( $_->{type} ) eq lc($type) } @{$_2fDevices};
return ( @found ? 1 : 0 );
}
return 1;
}
1;

View File

@ -19,8 +19,9 @@ sub verifyCode {
$self->logger->error('Bad characters in TOTP secret');
return -1;
}
for ( 0 .. $range ) {
for ( -$range .. $range ) {
if ( $code eq $self->_code( $s, $_, $interval, $digits ) ) {
$self->userLogger->info("Codes match at range $_");
return 1;
}
}

View File

@ -63,6 +63,7 @@ t/69-Lemonldap-NG-Handler-PSGI-SecureToken.t
t/70-Lemonldap-NG-Handler-PSGI-AuthBasic.t
t/71-Lemonldap-NG-Handler-PSGI-OAuth2.t
t/99-pod.t
t/custom.pm
t/lmConf-1.json
t/sessions/lock/.exists
t/test-psgi-lib.pm

View File

@ -238,6 +238,8 @@ sub defaultValuesInit {
$conf->{vhostOptions}->{$vhost}->{vhostAuthnLevel};
$class->tsv->{serviceTokenTTL}->{$vhost} =
$conf->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL};
$class->tsv->{accessToTrace}->{$vhost} =
$conf->{vhostOptions}->{$vhost}->{vhostAccessToTrace};
}
}
return 1;
@ -626,6 +628,9 @@ sub substitute {
# handle inGroup
$expr =~ s/\binGroup\(([^)]*)\)/listMatch(\$s->{'hGroups'},$1,1)/g;
# handle has2f
$expr =~ s/\bhas2f\(([^),]*)\)/has2f(\$s,$1)/g;
return $expr;
}

View File

@ -105,6 +105,7 @@ sub checkType {
sub run {
my ( $class, $req, $rule, $protection ) = @_;
my ( $id, $session );
my $vhost = $class->resolveAlias($req);
return $class->DECLINED unless ( $class->is_initial_req($req) );
@ -149,9 +150,41 @@ sub run {
# ACCOUNTING (1. Inform web server)
$class->set_user( $req, $session->{ $class->tsv->{whatToTrace} } );
$class->set_custom( $req, $session->{ $class->tsv->{customToTrace} } )
if $class->tsv->{customToTrace}
and $session->{ $class->tsv->{customToTrace} };
my $custom;
$custom = $session->{ $class->tsv->{customToTrace} }
if ( $class->tsv->{customToTrace}
and $session->{ $class->tsv->{customToTrace} } );
if ( $class->tsv->{accessToTrace}->{$vhost} ) {
my ( $function, @params ) = split /\s*,\s*/,
$class->tsv->{accessToTrace}->{$vhost};
if ( $function =~ qr/^(?:\w+(?:::\w+)*(?:\s+\w+(?:::\w+)*)*)?$/ ) {
my $c = eval {
no strict 'refs';
&{$function}( {
req => $req,
vhost => $vhost,
session => $session,
custom => $custom,
params => \@params
}
);
};
if ($@) {
$class->logger->error(
"Failed to overwrite customToTrace: $@");
}
else {
$class->logger->debug("Overwrite customToTrace with: $c");
$custom = $c;
}
}
else {
$class->logger->error(
"accessToTrace: Bad custom function name");
}
}
$class->set_custom( $req, $custom ) if $custom;
# AUTHORIZATION
return ( $class->forbidden( $req, $session ), $session )
@ -794,10 +827,14 @@ sub localUnlog {
if ( $id //= $class->fetchId($req) ) {
# Delete local cache
if ( $class->tsv->{refLocalStorage}
and $class->tsv->{refLocalStorage}->get($id) )
{
$class->tsv->{refLocalStorage}->remove($id);
if ( $class->tsv->{sessionCacheModule} ) {
my $module = $class->tsv->{sessionCacheModule};
my $options = $class->tsv->{sessionCacheOptions};
eval "use $module;";
my $cache = $module->new($options);
if ( $cache->get($id) ) {
$cache->remove($id);
}
}
}
}

View File

@ -6,7 +6,7 @@
# change 'tests => 1' to 'tests => last_test_to_print';
use strict;
use Test::More tests => 13;
use Test::More tests => 17;
require 't/test.pm';
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
@ -60,3 +60,39 @@ ok(
ok( $res = &$code, "Function works" );
ok( $res == 1, 'Get good result' );
$sub = "sub { return(has2f(\$_[0],\$_[1])) }";
$code = $jail->jail_reval($sub);
ok(
( defined($code) and ref($code) eq 'CODE' ),
'checkDate extended function is defined'
);
is(
$code->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
"TOTP"
),
1,
"Function works"
);
is(
$code->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
"UBK"
),
0,
"Function works"
);
is(
$code->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
),
1,
"Function works"
);

View File

@ -5,7 +5,7 @@
# change 'tests => 1' to 'tests => last_test_to_print';
use Test::More tests => 9;
use Test::More tests => 13;
require 't/test.pm';
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
@ -54,3 +54,40 @@ $listMatch = $jail->jail_reval($sub5);
ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' );
ok( &$listMatch eq '0', 'Get good result' );
# Test has2f method
my $sub6 = "sub { return(has2f(\$_[0],\$_[1])) }";
my $has2f = $jail->jail_reval($sub6);
ok(
( defined($has2f) and ref($has2f) eq 'CODE' ),
'checkDate extended function is defined'
);
is(
$has2f->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
"TOTP"
),
1,
"Function works"
);
is(
$has2f->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
),
1,
"Function works"
);
is(
$has2f->( {
_2fDevices =>
"[{\"name\":\"MyTOTP\",\"_secret\":\"g5fsxwf4d34biemlojsbbvhgtskrssos\",\"epoch\":1602173208,\"type\":\"TOTP\"}]"
},
"UBK"
),
0,
"Function works"
);

View File

@ -4,7 +4,10 @@ use MIME::Base64;
use Data::Dumper;
use URI::Escape;
require 't/test-psgi-lib.pm';
BEGIN {
require 't/test-psgi-lib.pm';
require 't/custom.pm';
}
init('Lemonldap::NG::Handler::Server::Nginx');
@ -31,6 +34,28 @@ count(4);
# Authentified queries
# --------------------
# Authorized query
ok(
$res =
$client->_get( '/', undef, 'test4.example.com', "lemonldap=$sessionId" ),
'Authentified query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
# Check headers
%h = @{ $res->[1] };
ok(
$h{'Lm-Remote-Custom'} eq
'dwho@badwolf.org alias Doctor_Who:users; timelords by using Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1',
'Lm-Remote-Custom is overwriten'
)
or explain(
\%h,
'Lm-Remote-Custom => "dwho@badwolf.org alias Doctor_Who:users; timelords by using Mozilla/5.0 (X11; VAX4000; rv:43.0) Gecko/20100101 Firefox/143.0 Iceweasel/143.0.1"'
);
count(1);
# Authorized query
ok( $res = $client->_get( '/', undef, undef, "lemonldap=$sessionId" ),
'Authentified query' );
@ -43,7 +68,11 @@ ok( $h{'Headername1'} eq 'Auth-User', 'Headername1 is set to "Auth-User"' )
or explain( \%h, 'Headername1 => "Auth-User"' );
ok( $h{'Headervalue1'} eq 'dwho', 'Headervalue1 is set to "dwho"' )
or explain( \%h, 'Headervalue1 => "dwho"' );
count(2);
ok(
$h{'Lm-Remote-Custom'} eq 'dwho@badwolf.org',
'Lm-Remote-Custom is set "dwho@badwolf.org"'
) or explain( \%h, 'Lm-Remote-User => "dwho@badwolf.org"' );
count(3);
# Request an URI protected by custom function -> allowed
ok(

View File

@ -4,6 +4,7 @@ use MIME::Base64;
BEGIN {
require 't/test-psgi-lib.pm';
require 't/custom.pm';
}
init('Lemonldap::NG::Handler::PSGI');
@ -39,7 +40,6 @@ ok(
'Authentified query'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Denied query
@ -50,7 +50,6 @@ ok(
'Denied query'
);
ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 );
count(2);
# Bad cookie
@ -67,11 +66,9 @@ ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
unlink(
't/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'
);
count(2);
done_testing( count() );
clean();
sub Lemonldap::NG::Handler::PSGI::handler {

View File

@ -0,0 +1,16 @@
package My;
sub accessToTrace {
my $hash = shift;
my $custom = $hash->{custom};
my $req = $hash->{req};
my $vhost = $hash->{vhost};
my $custom = $hash->{custom};
my $params = $hash->{params};
my $session = $hash->{session};
return
"$custom alias $params->[0]_$params->[1]:$session->{groups} by using $session->{ $params->[2] }";
}
1;

View File

@ -59,6 +59,9 @@
"^/logout": "logout_sso",
"default": "accept"
},
"test4.example.com": {
"default": "accept"
},
"*.example.org": {
"^/orgdeny": "deny",
"default": "accept"
@ -74,15 +77,20 @@
}
},
"macros": {
"_whatToTrace": "$_auth eq 'SAML' ? \"$_user\\@$_idpConfKey\" : \"$_user\""
"_whatToTrace": "$_auth eq 'SAML' ? \"$_user\\@$_idpConfKey\" : \"$_user\"",
"UA": "$ENV{HTTP_USER_AGENT}"
},
"portal": "http://auth.example.com/",
"reloadUrls": {},
"userDB": "Demo",
"vhostOptions": {
"test2.example.com": {
"vhostAuthnLevel": 5
}
"vhostAuthnLevel": 5
},
"test4.example.com": {
"vhostAccessToTrace": "My::accessToTrace, Doctor, Who, UA"
}
},
"whatToTrace": "_whatToTrace"
"whatToTrace": "_whatToTrace",
"customToTrace": "mail"
}

View File

@ -196,17 +196,20 @@ site/htdocs/static/js/viewer.min.js.map
site/htdocs/static/languages/ar.json
site/htdocs/static/languages/de.json
site/htdocs/static/languages/en.json
site/htdocs/static/languages/es.json
site/htdocs/static/languages/fr.json
site/htdocs/static/languages/it.json
site/htdocs/static/languages/pl.json
site/htdocs/static/languages/tr.json
site/htdocs/static/languages/vi.json
site/htdocs/static/languages/zh.json
site/htdocs/static/languages/zh_TW.json
site/htdocs/static/logos/ar.png
site/htdocs/static/logos/bootstrap.png
site/htdocs/static/logos/custom.png
site/htdocs/static/logos/de.png
site/htdocs/static/logos/en.png
site/htdocs/static/logos/es.png
site/htdocs/static/logos/favicon.ico
site/htdocs/static/logos/fr.png
site/htdocs/static/logos/it.png
@ -216,6 +219,7 @@ site/htdocs/static/logos/pl.png
site/htdocs/static/logos/tr.png
site/htdocs/static/logos/vi.png
site/htdocs/static/logos/zh.png
site/htdocs/static/logos/zh_TW.png
site/htdocs/static/reverseTree.json
site/htdocs/static/struct.json
site/templates/2ndfa.tpl

View File

@ -10,7 +10,7 @@
# Sessions.pm to manage sessions
package Lemonldap::NG::Manager;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use JSON;

View File

@ -1,6 +1,5 @@
package Lemonldap::NG::Manager::2ndFA;
use 5.10.0;
use utf8;
use strict;
use Mouse;
@ -12,9 +11,11 @@ use Lemonldap::NG::Common::Conf::ReConstants;
use feature 'state';
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Common::Session::REST';
extends qw(
Lemonldap::NG::Manager::Plugin
Lemonldap::NG::Common::Session::REST
Lemonldap::NG::Common::Conf::AccessLib
);
our $VERSION = '2.1.0';

View File

@ -1,13 +1,15 @@
# This module implements all the methods that responds to '/api/*' requests
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::RESTServer',
'Lemonldap::NG::Common::Session::REST';
extends qw(
Lemonldap::NG::Manager::Plugin
Lemonldap::NG::Common::Session::REST
Lemonldap::NG::Common::Conf::RESTServer
);
use Lemonldap::NG::Manager::Api::2F;
use Lemonldap::NG::Manager::Api::Misc;

View File

@ -4,7 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use JSON;

View File

@ -4,6 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use strict;
use Lemonldap::NG::Manager::Build::Attributes;
use Lemonldap::NG::Manager::Build::CTrees;
@ -26,7 +27,7 @@ sub _getDefaultValues {
my $defaultAttrs = Lemonldap::NG::Manager::Build::Attributes::attributes();
my $attrs = {};
foreach $attr (@allAttrs) {
foreach my $attr (@allAttrs) {
$attrs->{$attr} = $defaultAttrs->{$attr}->{default}
if ( defined $defaultAttrs->{$attr}
&& defined $defaultAttrs->{$attr}->{default} );
@ -39,7 +40,7 @@ sub _hasAllowedAttributes {
my ( $self, $attributes, $rootNode ) = @_;
my @allowedAttributes = $self->_listAttributes($rootNode);
foreach $attribute ( keys %{$attributes} ) {
foreach my $attribute ( keys %{$attributes} ) {
if ( length( ref($attribute) ) ) {
return {
res => "ko",

View File

@ -4,11 +4,10 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Manager::Conf::Parser;
use Data::Dumper;
extends 'Lemonldap::NG::Manager::Api::Common';

View File

@ -4,7 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Manager::Conf::Parser;

View File

@ -5,6 +5,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use strict;
use Mouse;
extends 'Lemonldap::NG::Manager::Api::Common';

View File

@ -4,7 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Manager::Conf::Parser;

View File

@ -4,7 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Manager::Conf::Parser;

View File

@ -4,7 +4,7 @@ our $VERSION = '2.1.0';
package Lemonldap::NG::Manager::Api;
use 5.10.0;
use strict;
use utf8;
use Mouse;

View File

@ -22,10 +22,9 @@ sub perlExpr {
grep( { $_ =~ /(?:Undefined subroutine|Devel::StackTrace)/ ? () : $_; }
split( /\n/, $@, 0 ) )
);
return -1, "__badExpression__: $err" if $err;
return -1, "__badExpression__: $err" if $err and $conf->{'useSafeJail'};
return $val =~ qr/(?<=[^=<!>\|\?])=(?![=~])/
? ( -1, '__badExpressionAssignment__' )
: 1;
&& $conf->{'avoidAssignment'} ? ( 1, '__badExpressionAssignment__' ) : 1;
}
sub types {
@ -642,6 +641,10 @@ sub attributes {
'default' => 'TOTP,U2F,Yubikey',
'type' => 'text'
},
'avoidAssignment' => {
'default' => 0,
'type' => 'bool'
},
'browsersDontStorePassword' => {
'default' => 0,
'type' => 'bool'
@ -891,6 +894,18 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => '_loginHistory _session_id hGroups',
'type' => 'text'
},
'checkUserHiddenHeaders' => {
'keyMsgFail' => '__badHostname__',
'keyTest' => qr/^\S+$/,
'test' => {
'keyMsgFail' => '__badHeaderName__',
'keyTest' => qr/^(?=[^\-])[\w\-\s]+(?<=[^-])$/,
'test' => sub {
return perlExpr(@_);
}
},
'type' => 'keyTextContainer'
},
'checkUserIdRule' => {
'default' => 1,
'test' => sub {
@ -1906,6 +1921,9 @@ m[^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
'mail2fLogo' => {
'type' => 'text'
},
'mail2fSessionKey' => {
'type' => 'text'
},
'mail2fSubject' => {
'type' => 'text'
},
@ -4144,6 +4162,10 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'utotp2fLogo' => {
'type' => 'text'
},
'vhostAccessToTrace' => {
'default' => '',
'type' => 'text'
},
'vhostAliases' => {
'default' => '',
'type' => 'text'

View File

@ -27,9 +27,10 @@ sub perlExpr {
my $err = join( '',
grep { $_ =~ /(?:Undefined subroutine|Devel::StackTrace)/ ? () : $_ }
split( /\n/, $@ ) );
return ( -1, "__badExpression__: $err" ) if $err;
return $val =~ qr/(?<=[^=<!>\|\?])=(?![=~])/
? ( -1, "__badExpressionAssignment__" )
return ( -1, "__badExpression__: $err" )
if ( $err && $conf->{useSafeJail} );
return ( $val =~ qr/(?<=[^=<!>\|\?])=(?![=~])/ && $conf->{avoidAssignment} )
? ( 1, "__badExpressionAssignment__" )
: 1;
}
@ -516,6 +517,17 @@ sub attributes {
documentation => 'Display empty headers rule',
flags => 'p',
},
checkUserHiddenHeaders => {
type => 'keyTextContainer',
keyTest => qr/^\S+$/,
keyMsgFail => '__badHostname__',
test => {
keyTest => qr/^(?=[^\-])[\w\-\s]+(?<=[^-])$/,
keyMsgFail => '__badHeaderName__',
test => sub { return perlExpr(@_) },
},
documentation => 'Header values to hide if not empty',
},
globalLogoutRule => {
type => 'boolOrExpr',
default => 0,
@ -1062,6 +1074,13 @@ sub attributes {
documentation => 'Activate Safe jail',
flags => 'hp',
},
avoidAssignment => {
default => 0,
type => 'bool',
help => 'safejail.html',
documentation => 'Avoid assignment in expressions',
flags => 'hp',
},
whatToTrace => {
type => 'lmAttrOrMacro',
default => 'uid',
@ -1878,6 +1897,10 @@ sub attributes {
type => 'text',
documentation => 'Custom logo for Mail 2F',
},
mail2fSessionKey => {
type => 'text',
documentation => 'Session parameter where mail is stored',
},
# External second factor
ext2fActivation => {
@ -2239,8 +2262,9 @@ sub attributes {
type => 'int',
default => -1,
},
vhostAliases => { type => 'text', default => '' },
vhostType => {
vhostAccessToTrace => { type => 'text', default => '' },
vhostAliases => { type => 'text', default => '' },
vhostType => {
type => 'select',
select => [
{ k => 'AuthBasic', v => 'AuthBasic' },

View File

@ -27,10 +27,10 @@ sub cTrees {
help => 'configvhost.html#options',
form => 'simpleInputContainer',
nodes => [
'vhostPort', 'vhostHttps',
'vhostMaintenance', 'vhostAliases',
'vhostType', 'vhostAuthnLevel',
'vhostServiceTokenTTL'
'vhostPort', 'vhostHttps',
'vhostMaintenance', 'vhostAliases',
'vhostAccessToTrace', 'vhostType',
'vhostAuthnLevel', 'vhostServiceTokenTTL'
],
},
],

View File

@ -271,7 +271,7 @@ sub tree {
'managerDn', 'managerPassword',
'ldapTimeout', 'ldapIOTimeout',
'ldapVersion', 'ldapRaw',
'ldapCAFile', 'ldapCAPath',
'ldapCAFile', 'ldapCAPath',
]
},
{
@ -553,10 +553,8 @@ sub tree {
title => 'logParams',
help => 'logs.html',
form => 'simpleInputContainer',
nodes => [
'whatToTrace', 'customToTrace',
'hiddenAttributes'
]
nodes =>
[ 'whatToTrace', 'customToTrace', 'hiddenAttributes' ]
},
{
title => 'cookieParams',
@ -772,7 +770,6 @@ sub tree {
{
title => 'checkUsers',
help => 'checkuser.html',
form => 'simpleInputContainer',
nodes => [
'checkUser',
'checkUserIdRule',
@ -783,6 +780,7 @@ sub tree {
'checkUserDisplayEmptyHeaders',
'checkUserDisplayEmptyValues',
'checkUserDisplayPersistentInfo',
'checkUserHiddenHeaders'
]
},
{
@ -900,6 +898,7 @@ sub tree {
'mail2fTimeout', 'mail2fSubject',
'mail2fBody', 'mail2fAuthnLevel',
'mail2fLabel', 'mail2fLogo',
'mail2fSessionKey',
]
},
{
@ -991,6 +990,7 @@ sub tree {
'key',
'trustedDomains',
'useSafeJail',
'avoidAssignment',
'checkXSS',
'requireToken',
'formTimeout',

View File

@ -6,7 +6,7 @@
# Read methods are inherited from Lemonldap::NG::Common::Conf::RESTServer
package Lemonldap::NG::Manager::Conf;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Common::Conf::Constants;

View File

@ -1,5 +1,6 @@
package Lemonldap::NG::Manager::Conf::Tests;
use strict;
use utf8;
use Lemonldap::NG::Common::Regexp;
use Lemonldap::NG::Handler::Main;
@ -449,7 +450,7 @@ sub tests {
"RSA_SHA1" )
{
undef $allsha1;
break;
last;
}
}
}
@ -461,7 +462,7 @@ sub tests {
->{samlSPMetaDataOptionsSignatureMethod} ne "RSA_SHA1" )
{
undef $allsha1;
break;
last;
}
}
}
@ -865,8 +866,7 @@ sub tests {
$appUrl =~ m#^(https?://[^/]+)(/.*)?$#;
my $appHost = $1;
unless ($appHost) {
push @msg,
"$clientConfKey CAS Application has no Service URL";
push @msg, "$casConfKey CAS Application has no Service URL";
$res = 0;
next;
}

View File

@ -1,6 +1,6 @@
package Lemonldap::NG::Manager::Notifications;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use JSON qw(from_json to_json);

View File

@ -1,6 +1,5 @@
package Lemonldap::NG::Manager::Sessions;
use 5.10.0;
use utf8;
use strict;
use Mouse;

View File

@ -1,6 +1,6 @@
package Lemonldap::NG::Manager::Viewer;
use 5.10.0;
use strict;
use utf8;
use Mouse;
use Lemonldap::NG::Common::Conf::Constants;

View File

@ -577,7 +577,9 @@ llapp.controller 'TreeCtrl', [
d = $q.defer()
d.notify 'Trying to get datas'
$scope.waiting = true
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{node.cnodes}").then (response) ->
console.log "Trying to get key #{node.cnodes}"
uri = encodeURI node.cnodes
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{uri}").then (response) ->
data = response.data
# Manage datas errors
if not data
@ -759,7 +761,13 @@ llapp.controller 'TreeCtrl', [
d.reject response.statusLine
$scope.waiting = false
else
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then node.get else node.title}").then (response) ->
uri = ''
if node.get
console.log "Trying to get key #{node.get}"
uri = encodeURI node.get
else
console.log "Trying to get title #{node.title}"
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then uri else node.title}").then (response) ->
# Set default value if response is null or if asked by server
data = response.data
if (data.value == null or (data.error and data.error.match /setDefault$/ ) ) and node['default'] != null

View File

@ -258,7 +258,9 @@ llapp.controller 'TreeCtrl', [
d = $q.defer()
d.notify 'Trying to get datas'
$scope.waiting = true
$http.get("#{window.viewPrefix}#{$scope.currentCfg.cfgNum}/#{node.cnodes}").then (response) ->
console.log "Trying to get key #{node.cnodes}"
uri = encodeURI node.cnodes
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{uri}").then (response) ->
data = response.data
# Manage datas errors
if not data
@ -360,7 +362,13 @@ llapp.controller 'TreeCtrl', [
d.reject response.statusLine
$scope.waiting = false
else
$http.get("#{window.viewPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then node.get else node.title}").then (response) ->
uri = ''
if node.get
console.log "Trying to get key #{node.get}"
uri = encodeURI node.get
else
console.log "Trying to get title #{node.title}"
$http.get("#{window.confPrefix}#{$scope.currentCfg.cfgNum}/#{if node.get then uri else node.title}").then (response) ->
# Set default value if response is null or if asked by server
data = response.data
if (data.value == null or (data.error and data.error.match /setDefault$/ ) ) and node['default'] != null

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1327,6 +1327,12 @@ function templates(tpl,key) {
"id" : tpl+"s/"+key+"/"+"vhostAliases",
"title" : "vhostAliases"
},
{
"default" : "",
"get" : tpl+"s/"+key+"/"+"vhostAccessToTrace",
"id" : tpl+"s/"+key+"/"+"vhostAccessToTrace",
"title" : "vhostAccessToTrace"
},
{
"default" : "Main",
"get" : tpl+"s/"+key+"/"+"vhostType",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -718,11 +718,13 @@ This file contains:
return _download(node);
};
_download = function(node) {
var d;
var d, uri;
d = $q.defer();
d.notify('Trying to get datas');
$scope.waiting = true;
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + node.cnodes).then(function(response) {
console.log("Trying to get key " + node.cnodes);
uri = encodeURI(node.cnodes);
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + uri).then(function(response) {
var a, data, len, o;
data = response.data;
if (!data) {
@ -900,7 +902,7 @@ This file contains:
}, readError);
};
$scope.getKey = function(node) {
var d, i, len, n, o, ref, tmp;
var d, i, len, n, o, ref, tmp, uri;
d = $q.defer();
if (!node.data) {
$scope.waiting = true;
@ -923,7 +925,14 @@ This file contains:
return $scope.waiting = false;
});
} else {
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + (node.get ? node.get : node.title)).then(function(response) {
uri = '';
if (node.get) {
console.log("Trying to get key " + node.get);
uri = encodeURI(node.get);
} else {
console.log("Trying to get title " + node.title);
}
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + (node.get ? uri : node.title)).then(function(response) {
var data;
data = response.data;
if ((data.value === null || (data.error && data.error.match(/setDefault$/))) && node['default'] !== null) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.12.8
/*
LemonLDAP::NG Viewer client
@ -328,11 +328,13 @@ This file contains:
return _download(node);
};
_download = function(node) {
var d;
var d, uri;
d = $q.defer();
d.notify('Trying to get datas');
$scope.waiting = true;
$http.get("" + window.viewPrefix + $scope.currentCfg.cfgNum + "/" + node.cnodes).then(function(response) {
console.log("Trying to get key " + node.cnodes);
uri = encodeURI(node.cnodes);
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + uri).then(function(response) {
var a, data, l, len;
data = response.data;
if (!data) {
@ -420,7 +422,7 @@ This file contains:
}
};
$scope.getKey = function(node) {
var d, i, l, len, n, ref, tmp;
var d, i, l, len, n, ref, tmp, uri;
d = $q.defer();
if (!node.data) {
$scope.waiting = true;
@ -443,7 +445,14 @@ This file contains:
return $scope.waiting = false;
});
} else {
$http.get("" + window.viewPrefix + $scope.currentCfg.cfgNum + "/" + (node.get ? node.get : node.title)).then(function(response) {
uri = '';
if (node.get) {
console.log("Trying to get key " + node.get);
uri = encodeURI(node.get);
} else {
console.log("Trying to get title " + node.title);
}
$http.get("" + window.confPrefix + $scope.currentCfg.cfgNum + "/" + (node.get ? uri : node.title)).then(function(response) {
var data;
data = response.data;
if ((data.value === null || (data.error && data.error.match(/setDefault$/))) && node['default'] !== null) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -75,6 +75,7 @@
"auto":"تلقائي",
"autoSignin":"Auto Signin",
"autoSigninRules":"القواعد",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"العودة إلى البوابة",
"badCasProxyId":"معرف خدمة بروكسي كاس غير صالح",
"badChoiceKey":"اسم مفتاح سيئ في قائمة الاختيارات",
@ -83,7 +84,7 @@
"badDomainName":"اسم النطاق سيئ",
"badEncoding":"تشفير خاطئ",
"badExpression":"تعبير خاطئ",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":" حقل الهيدر خاطئ",
"badHostname":"اسم الخادم خاطئ",
"badLdapUri":"\n URI LDAP خاطئ",
@ -207,6 +208,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"اختيارالإعدادات",
"chooseLogo":"اختيار الشعار",
@ -494,6 +496,7 @@
"mail2fAuthnLevel":"مستوى إثبات الهوية",
"mail2fLabel":"Label",
"mail2fLogo":"شعار",
"mail2fSessionKey":"مفتاح الجلسة الذي يحتوي على عنوان البريد الإلكتروني",
"mailBody":"محتوى البريد الناجح",
"mailCharset":"charset",
"mailConfirmBody":"تأكيد محتوى البريد",
@ -586,7 +589,7 @@
"offlineSessions":"Offline sessions",
"oldValue":"قيمة قديمة",
"on":"تنشيط",
"oidcAttribute":"OIDC Attribute",
"oidcAttribute":"خاصيات OIDC",
"oidcAuthnLevel":"مستوى إثبات الهوية",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":" أوبين أيدي كونيكت بروفيدر",
@ -1004,6 +1007,7 @@
"verifyU2FKey":"Verify U2F key",
"verifyTOTPKey":"Verify TOTP key",
"version":"الإصدار",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"اسماء مستعارة",
"vhostAuthnLevel":"مستوى إثبات الهوية واجب",
"vhostHttps":"إتش تي تي بي س",

View File

@ -75,6 +75,7 @@
"auto":"Automatic",
"autoSignin":"Auto Signin",
"autoSigninRules":"Regeln",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Back to portal",
"badCasProxyId":"Bad CAS proxied service identifier",
"badChoiceKey":"Bad key name in Choice menu",
@ -83,7 +84,7 @@
"badDomainName":"Ungültiger Domainname",
"badEncoding":"Ungültige Codierung",
"badExpression":"Bad expression",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Bad header name",
"badHostname":"Ungültiger Hostname",
"badLdapUri":"Bad LDAP URI",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",
"chooseLogo":"Choose logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Authentication level",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Session key containing mail address",
"mailBody":"Success mail content",
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Verify U2F key",
"verifyTOTPKey":"Verify TOTP key",
"version":"Version",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"Automatic",
"autoSignin":"Auto Signin",
"autoSigninRules":"Rules",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Back to portal",
"badCasProxyId":"Bad CAS proxied service identifier",
"badChoiceKey":"Bad key name in Choice menu",
@ -83,7 +84,7 @@
"badDomainName":"Bad domain name",
"badEncoding":"Bad encoding",
"badExpression":"Bad expression",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Bad header name",
"badHostname":"Bad hostname",
"badLdapUri":"Bad LDAP URI",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",
"chooseLogo":"Choose logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Authentication level",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Session key containing mail address",
"mailBody":"Success mail content",
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Verify U2F key",
"verifyTOTPKey":"Verify TOTP key",
"version":"Version",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"Automatique",
"autoSignin":"Connexion automatique",
"autoSigninRules":"Règles",
"avoidAssignment":"Eviter une affectation dans les expressions",
"backtoportal":"Retour au portail",
"badCasProxyId":"Mauvais identifiant de service proxy CAS",
"badChoiceKey":"Mauvais nom de clef dans le menu Choice",
@ -83,7 +84,7 @@
"badDomainName":"Mauvais nom de domaine",
"badEncoding":"Mauvais encodage",
"badExpression":"Mauvaise expression",
"badExpressionAssignment":"Expression contenant une affectation",
"badExpressionAssignment":"Expression contenant une affectation. Vous pouvez utliser \\x3D pour éviter cet avertissement.",
"badHeaderName":"Mauvais nom d'en-tête",
"badHostname":"Mauvais nom d'hôte",
"badLdapUri":"Mauvaise URI LDAP",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Afficher les données de session persistante",
"checkUserDisplayEmptyHeaders":"Afficher les entêtes nuls",
"checkUserDisplayEmptyValues":"Afficher les valeurs nulles",
"checkUserHiddenHeaders":"Entêtes masqués",
"checkUserSearchAttributes":"Attributs utilisés pour rechercher les sessions",
"choiceParams":"Paramètres des choix",
"chooseLogo":"Choisir le logo",
@ -373,7 +375,7 @@
"impersonationMergeSSOgroups":"Fusionner les groupes SSO réels et usurpés",
"impersonationSkipEmptyValues":"Ignorer les valeurs nulles",
"impersonationUnrestrictedUsersRule":"Règle des utilisateurs non restreints",
"incompleteForm":"Des champs requis manquent",
"incompleteForm":"Des champs requis sont manquants",
"index":"Index",
"infoFormMethod":"Méthode du formulaire d'information",
"invalidSessionData":"Donnée de session invalide",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Niveau de l'authentification",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Clef de session contenant l'adresse email",
"mailBody":"Contenu du message de succès",
"mailCharset":"Charset",
"mailConfirmBody":"Contenu du message de confirmation",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Vérifier la clef U2F",
"verifyTOTPKey":"Vérifier la clef TOTP",
"version":"Version",
"vhostAccessToTrace":"Accès à tracer",
"vhostAliases":"Alias",
"vhostAuthnLevel":"Niveau d'authentification requis",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"Automatico",
"autoSignin":"Accesso automatico",
"autoSigninRules":"Regole",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Torna al portale",
"badCasProxyId":"Identificatore di servizio difettoso CAS proxy",
"badChoiceKey":"Nome chiave errato nel menu Scelta",
@ -83,7 +84,7 @@
"badDomainName":"Nome di dominio errato",
"badEncoding":"Codifica errata",
"badExpression":"Espressione errata",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Nome intestazione errato",
"badHostname":"Hostname errato",
"badLdapUri":"LDAP URI errata",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Mostra valori vuoti",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Scelta parametri",
"chooseLogo":"Scegli logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Livello di autenticazione",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Chiave di sessione contenente l'indirizzo di posta",
"mailBody":"Successo contenuto di posta",
"mailCharset":"Charset",
"mailConfirmBody":"Contenuto della mail di conferma",
@ -585,7 +588,7 @@
"offlineSessions":"Offline sessions",
"oldValue":"Vecchio valore",
"on":"On",
"oidcAttribute":"OIDC Attribute",
"oidcAttribute":"Attributo OIDC",
"oidcAuthnLevel":"Livello di autenticazione",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":"Provider di OpenID Connect",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Verifica la chiave U2F",
"verifyTOTPKey":"Verifica la chiave TOTP",
"version":"Versioni",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Alias",
"vhostAuthnLevel":"Livello di autenticazione richiesto",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"Automatycznie",
"autoSignin":"Zaloguj się automatycznie",
"autoSigninRules":"Zasady",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Powrót do portalu",
"badCasProxyId":"Nieprawidłowy identyfikator usługi proxy CAS",
"badChoiceKey":"Błędna nazwa klucza w menu Wybór",
@ -83,7 +84,7 @@
"badDomainName":"Błędna nazwa domeny",
"badEncoding":"Błędne kodowanie",
"badExpression":"Błędne wyrażenie",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Błędna nazwa nagłówka",
"badHostname":"Błędna nazwa hosta",
"badLdapUri":"Błędny identyfikator URI LDAP",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Wyświetl puste nagłówki",
"checkUserDisplayEmptyValues":"Wyświetl puste wartości",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Atrybuty używane do wyszukiwania sesji",
"choiceParams":"Parametry wyboru",
"chooseLogo":"Wybierz logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Poziom uwierzytelnienia",
"mail2fLabel":"Etykieta",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Klucz sesji zawierający adres e-mail",
"mailBody":"Treść wiadomości o powodzeniu",
"mailCharset":"Zestaw znaków",
"mailConfirmBody":"Treść wiadomości potwierdzającej",
@ -585,7 +588,7 @@
"offlineSessions":"Sesje offline",
"oldValue":"Stara wartość",
"on":"Włączone",
"oidcAttribute":"OIDC Attribute",
"oidcAttribute":"Atrybut OIDC",
"oidcAuthnLevel":"Poziom uwierzytelnienia",
"oidcConsents":"Zgoda na OpenID Connect",
"oidcOP":"Dostawca OpenID Connect",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Sprawdź klucz U2F",
"verifyTOTPKey":"Sprawdź klucz TOTP",
"version":"Wersja",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliasy",
"vhostAuthnLevel":"Wymagany poziom uwierzytelnienia",
"vhostHttps":"HTTPS",

View File

@ -45,7 +45,7 @@
"ADPwdMaxAge":"Parola maksimum sınırı",
"advancedParams":"Gelişmiş parametreler",
"allowedMarkups":"İzin verilen biçimlendirmeler:",
"always":"Always",
"always":"Her zaman",
"apacheParams":"Apache parametreleri",
"apacheAuthnLevel":"Doğrulama seviyesi",
"application":"Uygulama",
@ -55,7 +55,7 @@
"appsInThisCat":"Bu kategorideki uygulamalar",
"array":"Array",
"attributesAndMacros":"Nitelikler ve Makrolar",
"attributeName":"Attribute name",
"attributeName":"Nitelik adı",
"authAndUserdb":"Yetkilendirme ve kullanıcı veri tabanı",
"authChain":"Doğrulama zinciri",
"authChoice":"Kimlik doğrulama tercihi",
@ -75,6 +75,7 @@
"auto":"Otomatik",
"autoSignin":"Otomatik Oturum Açma",
"autoSigninRules":"Kurallar",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Portala geri dön",
"badCasProxyId":"Hatalı CAS vekil servis tanımlayıcısı",
"badChoiceKey":"Tercih menüsünde hatalı anahtar adı",
@ -83,7 +84,7 @@
"badDomainName":"Hatalı etki alanı adı",
"badEncoding":"Hatalı kodlama",
"badExpression":"Hatalı ifade",
"badExpressionAssignment":"İfade bir atama içeriyor",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Hatalı başlık adı",
"badHostname":"Hatalı konak adı",
"badLdapUri":"Hatalı LDAP URI",
@ -100,7 +101,7 @@
"badValue":"Hatalı değer",
"badVariableName":"Hatalı değişken adı",
"blackList":"Kara liste",
"bool":"Boolean",
"bool":"Mantıksal",
"browse":"Göz at",
"browsersDontStorePassword":"Kullanıcı parolasını tarayıcılarda saklamaktan kaçının",
"browserIdAuthnLevel":"Doğrulama seviyesi",
@ -193,7 +194,7 @@
"cfgVersion":"Yapılandırma sürümü",
"checkXSS":"XSS saldırılarını kontrol et",
"clickHereToForce":"Zorlamak için buraya tıklayın",
"claimName":"Claim name",
"claimName":"İstek adı",
"checkboxes":"Onay kutuları",
"checkState":"Aktivasyon",
"checkStateSecret":"Paylaşılan sır",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Kalıcı oturum verisini görüntüle",
"checkUserDisplayEmptyHeaders":"Boş başlıkları görüntüle",
"checkUserDisplayEmptyValues":"Boş değerleri görüntüle",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Arama oturumlarında kullanılan nitelikler",
"choiceParams":"Tercih parametreleri",
"chooseLogo":"Logo seçin",
@ -377,7 +379,7 @@
"index":"Dizin",
"infoFormMethod":"Bilgi formu için metot",
"invalidSessionData":"Geçersiz oturum verisi",
"int":"Integer",
"int":"Sayı",
"internalReference":"Dahili referans",
"ipAddr":"IP adresi",
"ipAddresses":"IP adresleri",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Doğrulama seviyesi",
"mail2fLabel":"Etiket",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Oturum anahtarı e-posta adresini içeriyor",
"mailBody":"Başarılı e-posta içeriği",
"mailCharset":"Karakter seti",
"mailConfirmBody":"Doğrulama e-postası içeriği",
@ -526,7 +529,7 @@
"name":"Ad",
"needConfirmation":"Yeni bir yapılandırma mevcut. Bunu kaydetmeye zorlamak için 'kaydet' butonunun yanındaki onay kutusuna tıklayın.",
"networkProblem":"Ağ problemi",
"never":"Never",
"never":"Asla",
"newApp":"Yeni uygulama",
"newChain":"Yeni zincir",
"newCat":"Yeni kategori",
@ -585,7 +588,7 @@
"offlineSessions":"Çevrimdışı oturumlar",
"oldValue":"Eski değer",
"on":"Açık",
"oidcAttribute":"OIDC Attribute",
"oidcAttribute":"OIDC niteliği",
"oidcAuthnLevel":"Doğrulama seviyesi",
"oidcConsents":"OpenID Connect İzinleri",
"oidcOP":"OpenID Connect Sağlayıcısı",
@ -999,10 +1002,11 @@
"value":"Değer",
"values":"Değerler",
"variables":"Değişkenler",
"variableName":"Variable name",
"variableName":"Değişken adı",
"verifyU2FKey":"U2F anahtarını doğrula",
"verifyTOTPKey":"TOTP anahtarını doğrula",
"version":"Sürüm",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Takma adlar",
"vhostAuthnLevel":"Gereken doğrulama seviyesi",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"Tự động",
"autoSignin":"Auto Signin",
"autoSigninRules":"Quy tắc",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"Quay lại cổng thông tin",
"badCasProxyId":"Dịch vụ định danh đệm bởi CAS không hợp lệ",
"badChoiceKey":"Tên khoá không hợp lệ trong trình đơn Chọn",
@ -83,7 +84,7 @@
"badDomainName":"Tên miền không hợp lệ",
"badEncoding":"Mã hoá không hợp lệ",
"badExpression":"Biểu thức không hợp lệ",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"Tên tiêu đề không hợp lệ",
"badHostname":"Tên máy chủ không hợp lệ",
"badLdapUri":"URI LDAP không đúng",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Các tham số lựa chọn",
"chooseLogo":"Chọn logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"Mức xác thực",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Khóa phiên chứa địa chỉ thư",
"mailBody":"Nội dung thư thành công",
"mailCharset":"Charset",
"mailConfirmBody":"Xác nhận nội dung thư",
@ -585,7 +588,7 @@
"offlineSessions":"Offline sessions",
"oldValue":"Giá trị cũ",
"on":"Vào",
"oidcAttribute":"OIDC Attribute",
"oidcAttribute":"thuộc tính OIDC",
"oidcAuthnLevel":"Mức xác thực",
"oidcConsents":"OpenID Connect Consents",
"oidcOP":"Bộ cung cấp Kết nối OpenID",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Verify U2F key",
"verifyTOTPKey":"Verify TOTP key",
"version":"Phiên bản",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Bí danh",
"vhostAuthnLevel":"Mức xác thực bắt buộc",
"vhostHttps":"HTTPS",

View File

@ -75,6 +75,7 @@
"auto":"自动",
"autoSignin":"Auto Signin",
"autoSigninRules":"Rules",
"avoidAssignment":"Avoid assignment in expressions",
"backtoportal":"返回 portal",
"badCasProxyId":"无效的 CAS proxied service identifier",
"badChoiceKey":"选择菜单的无效 key ",
@ -83,7 +84,7 @@
"badDomainName":"无效的域名",
"badEncoding":"无效的编码",
"badExpression":"无效的表达式",
"badExpressionAssignment":"Expression containing an assignment",
"badExpressionAssignment":"Expression containing an assignment. You can use \\x3D to avoid this warning.",
"badHeaderName":"无效的头部名称",
"badHostname":"无效的主机名",
"badLdapUri":"无效的 LDAP URI ",
@ -206,6 +207,7 @@
"checkUserDisplayPersistentInfo":"Display persistent session data",
"checkUserDisplayEmptyHeaders":"Display empty headers",
"checkUserDisplayEmptyValues":"Display empty values",
"checkUserHiddenHeaders":"Hidden headers",
"checkUserSearchAttributes":"Attributes used for searching sessions",
"choiceParams":"Choice parameters",
"chooseLogo":"Choose logo",
@ -493,6 +495,7 @@
"mail2fAuthnLevel":"认证等级",
"mail2fLabel":"Label",
"mail2fLogo":"Logo",
"mail2fSessionKey":"Session key containing mail address",
"mailBody":"Success mail content",
"mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content",
@ -1003,6 +1006,7 @@
"verifyU2FKey":"Verify U2F key",
"verifyTOTPKey":"Verify TOTP key",
"version":"Version",
"vhostAccessToTrace":"Access to trace",
"vhostAliases":"Aliases",
"vhostAuthnLevel":"Required authentication level",
"vhostHttps":"HTTPS",

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -25,30 +25,29 @@ ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
or print STDERR Dumper($resBody);
ok( $resBody->{needConfirm} == 1, "JSON response contains \"needConfirm:1\"" )
or print STDERR Dumper($resBody);
ok(
@{ $resBody->{details}->{__warnings__} } == 3,
'JSON response contains 3 warnings'
@{ $resBody->{details}->{__warnings__} } == 4,
'JSON response contains 4 warnings'
) or print STDERR Dumper($resBody);
count(6);
foreach my $i ( 0 .. 2 ) {
foreach my $i ( 0 .. 3 ) {
ok(
$resBody->{details}->{__warnings__}->[$i]->{message} =~
/\b(unprotected|cross-domain-authentication|retries)\b/,
"Warning with 'unprotect', 'CDA' or 'retries' found"
/\b(unprotected|cross-domain-authentication|retries|__badExpressionAssignment__)\b/,
"Warning with 'unprotect', 'CDA', 'assignment' or 'retries' found"
) or print STDERR Dumper($resBody);
count(1);
}
count(4);
ok(
@{ $resBody->{details}->{__needConfirmation__} } == 2,
'JSON response contains 2 needConfirmation'
@{ $resBody->{details}->{__needConfirmation__} } == 1,
'JSON response contains 1 needConfirmation'
) or print STDERR Dumper($resBody);
ok(
@{ $resBody->{details}->{__changes__} } == 23,
'JSON response contains 23 changes'
@{ $resBody->{details}->{__changes__} } == 24,
'JSON response contains 24 changes'
) or print STDERR Dumper($resBody);
#print STDERR Dumper($resBody);
@ -89,7 +88,7 @@ while ( my $c = shift @{ $resBody->{details}->{__changes__} } ) {
}
}
ok( !@changes, 'All changes detected' ) or $bug = 1;
count(1);
if ($bug) {
print STDERR 'Expected not found: '
. Dumper( \@changes )
@ -99,8 +98,6 @@ if ($bug) {
#print STDERR Dumper(\@changes,\@cmsg);
count(6);
# TODO: check result of this
ok( $res = &client->jsonResponse('/diff/1/2'), 'Diff called' );
my ( @c1, @c2 );
@ -108,8 +105,8 @@ ok( ( @c1 = sort keys %{ $res->[0] } ), 'diff() detects changes in conf 1' );
ok( ( @c2 = sort keys %{ $res->[1] } ), 'diff() detects changes in conf 2' );
ok( @c1 == 12, '12 keys changed in conf 1' )
or print STDERR "Expect: 12 keys, get: " . join( ', ', @c1 ) . "\n";
ok( @c2 == 16, '16 keys changed or created in conf 2' )
or print STDERR "Expect: 16 keys, get: " . join( ',', @c2 ) . "\n";
ok( @c2 == 17, '17 keys changed or created in conf 2' )
or print STDERR "Expect: 17 keys, get: " . join( ',', @c2 ) . "\n";
count(5);
unlink $confFiles->[1];
@ -242,6 +239,11 @@ sub changes {
{
'confCompacted' => '1',
'removedKeys' => 'some; keys'
},
{
'new' => 1,
'key' => 'avoidAssignment',
'old' => '0'
}
];
}

View File

@ -1597,6 +1597,12 @@
"title": "useSafeJail",
"type": "bool",
"data": 1
}, {
"default": 0,
"id": "avoidAssignment",
"title": "avoidAssignment",
"type": "bool",
"data": 1
}, {
"default": 1,
"id": "checkXSS",

View File

@ -373,6 +373,7 @@ site/htdocs/static/common/ro.png
site/htdocs/static/common/tr.png
site/htdocs/static/common/vi.png
site/htdocs/static/common/zh.png
site/htdocs/static/common/zh_TW.png
site/htdocs/static/languages/ar.json
site/htdocs/static/languages/de.json
site/htdocs/static/languages/en.json
@ -387,6 +388,7 @@ site/htdocs/static/languages/ro.json
site/htdocs/static/languages/tr.json
site/htdocs/static/languages/vi.json
site/htdocs/static/languages/zh.json
site/htdocs/static/languages/zh_TW.json
site/templates/bootstrap/2fchoice.tpl
site/templates/bootstrap/2fregisters.tpl
site/templates/bootstrap/captcha.tpl
@ -456,6 +458,7 @@ site/templates/common/bullet_go.png
site/templates/common/key.png
site/templates/common/mail/ar.json
site/templates/common/mail/en.json
site/templates/common/mail/es.json
site/templates/common/mail/fi.json
site/templates/common/mail/fr.json
site/templates/common/mail/it.json
@ -463,6 +466,7 @@ site/templates/common/mail/ms.json
site/templates/common/mail/tr.json
site/templates/common/mail/vi.json
site/templates/common/mail/zh_CN.json
site/templates/common/mail/zh_TW.json
site/templates/common/mail_2fcode.tpl
site/templates/common/mail_certificateConfirm.tpl
site/templates/common/mail_certificateReset.tpl
@ -481,6 +485,7 @@ t/01-AuthDemo.t
t/01-CSP-and-CORS-headers.t
t/01-Handler-redirection-and-URL-check-by-portal.t
t/01-pdata.t
t/01-Unauth-Logout.t
t/02-Password-Demo-Local-noPpolicy.t
t/02-Password-Demo-Local-Ppolicy.t
t/02-Password-Demo.t
@ -657,6 +662,7 @@ t/64-StayConnected-with-History.t
t/65-AutoSignin.t
t/66-CDA-already-auth.t
t/66-CDA-PSGI-Try.t
t/66-CDA-with-doubleCookies.t
t/66-CDA-with-REST.t
t/66-CDA-with-SOAP.t
t/66-CDA.t
@ -686,6 +692,7 @@ t/69-FavApps.t
t/70-2F-TOTP-8-with-global-storage.t
t/70-2F-TOTP-and-U2F-with-TTL-and-JSON.t
t/70-2F-TOTP-with-History-and-Refresh.t
t/70-2F-TOTP-with-Range.t
t/70-2F-TOTP-with-TTL-and-JSON.t
t/70-2F-TOTP-with-TTL-and-XML.t
t/70-2F-TOTP-with-TTL.t
@ -705,6 +712,7 @@ t/76-2F-Ext-with-CodeActivation.t
t/76-2F-Ext-with-GrantSession.t
t/76-2F-Ext-with-History.t
t/77-2F-Extra.t
t/77-2F-Mail-SessionKey.t
t/77-2F-Mail-with-global-storage.t
t/77-2F-Mail.t
t/78-2F-Upgrade-Many.t

View File

@ -40,11 +40,20 @@ has ott => (
}
);
has sessionKey => (
is => 'rw',
lazy => 1,
default => sub {
return $_[0]->{conf}->{mail2fSessionKey}
|| $_[0]->{conf}->{mailSessionKey};
}
);
sub init {
my ($self) = @_;
$self->{conf}->{mail2fCodeRegex} ||= '\d{6}';
unless ( $self->conf->{mailSessionKey} ) {
$self->error("Missing 'mailSessionKey' parameter, aborting");
unless ( $self->sessionKey ) {
$self->error("Missing session key parameter, aborting");
return 0;
}
$self->prefix( $self->conf->{sfPrefix} )
@ -65,7 +74,7 @@ sub run {
$self->logger->debug("Generated two-factor code: $code");
$self->ott->updateToken( $token, __mail2fcode => $code );
my $dest = $req->{sessionInfo}->{ $self->conf->{mailSessionKey} };
my $dest = $req->{sessionInfo}->{ $self->sessionKey };
unless ($dest) {
$self->logger->error( "Could not find mail attribute for login "
. $req->{sessionInfo}->{_user} );

View File

@ -4,13 +4,18 @@ use strict;
use Mouse;
# Add constants used by this module
use Lemonldap::NG::Portal::Main::Constants
qw(PE_OK PE_FORBIDDENIP PE_USERNOTFOUND);
use Lemonldap::NG::Portal::Lib::Slave;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_FORBIDDENIP
PE_USERNOTFOUND
);
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Portal::Main::Auth';
extends qw(
Lemonldap::NG::Portal::Main::Auth
Lemonldap::NG::Portal::Lib::Slave
);
# INITIALIZATION

View File

@ -455,7 +455,7 @@ sub userModifyPassword {
return PE_LDAPERROR;
}
$self->{portal}->userLogger->notice("Password changed for $dn");
$self->{portal}->logger->notice("Password changed for $dn");
# Rebind as manager for next LDAP operations if we were bound as user
$self->bind() if $asUser;
@ -588,8 +588,7 @@ sub userModifyPassword {
return PE_WRONGMANAGERACCOUNT
if ( $mesg->code == 50 || $mesg->code == 8 );
if ( $mesg->code == 0 ) {
$self->{portal}
->userLogger->notice("Password changed $self->{portal}->{user}");
$self->{portal}->logger->notice("Password changed for $dn");
# Rebind as manager for next LDAP operations if we were bound as user
$self->bind() if $asUser;

View File

@ -5,11 +5,9 @@
# Slave common functions
package Lemonldap::NG::Portal::Lib::Slave;
use Exporter;
use base qw(Exporter);
use strict;
use Mouse;
our @EXPORT = qw(checkIP checkHeader);
our $VERSION = '2.1.0';
# RUNNING METHODS
@ -49,4 +47,4 @@ sub checkHeader {
return 0;
}
1;
1;

View File

@ -161,28 +161,29 @@ sub setPortalRoutes {
# psgi.js
->addUnauthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
->addAuthRoute( 'psgi.js' => 'sendJs', ['GET'] )
# portal.css
->addUnauthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
->addAuthRoute( 'portal.css' => 'sendCss', ['GET'] )
# lmerror
->addUnauthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
->addAuthRoute( lmerror => { ':code' => 'lmError' }, ['GET'] )
# Core REST API
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addUnauthRoute( ping => 'pleaseAuth', ['GET'] )
->addAuthRoute( ping => 'authenticated', ['GET'] )
# Refresh session
->addAuthRoute( refresh => 'refresh', ['GET'] )
->addAuthRoute( '*' => 'corsPreflight', ['OPTIONS'] )
->addAuthRoute( '*' => 'corsPreflight', ['OPTIONS'] )
->addUnauthRoute( '*' => 'corsPreflight', ['OPTIONS'] )
# Logout
->addAuthRoute( logout => 'logout', ['GET'] );
->addAuthRoute( logout => 'logout', ['GET'] )
->addUnauthRoute( logout => 'unauthLogout', ['GET'] );
# Default routes must point to routines declared above
$self->defaultAuthRoute('');
@ -239,9 +240,11 @@ sub reloadConf {
# Initialize templateDir
$self->{templateDir} =
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin};
$self->conf->{templateDir} . '/' . $self->conf->{portalSkin}
if ( $self->conf->{templateDir} and $self->conf->{portalSkin} );
unless ( -d $self->{templateDir} ) {
$self->error("Template dir $self->{templateDir} doesn't exist");
$self->error("Template dir $self->{templateDir} doesn't exist")
if ref( $self->{templateDir} ) eq 'SCALAR';
return $self->fail;
}
$self->templateDir(
@ -526,7 +529,11 @@ sub loadModule {
$self->error("Unable to build $module object: $@");
return 0;
}
( $obj and $obj->init ) or return 0;
unless ( $obj and $obj->init ) {
$self->error("$module init failed");
return 0;
}
$self->loadedModules->{$module} = $obj;
return $obj;
}

View File

@ -179,6 +179,27 @@ sub checkLogout {
PE_OK;
}
sub checkUnauthLogout {
my ( $self, $req ) = @_;
if ( defined $req->param('logout') ) {
$self->userLogger->info('Unauthenticated logout request');
$self->logger->debug('Cleaning pdata');
$self->logger->debug("Removing $self->{conf}->{cookieName} cookie");
$req->pdata({});
$req->addCookie(
$self->cookie(
name => $self->conf->{cookieName},
domain => $self->conf->{domain},
secure => $self->conf->{securedCookie},
expires => 'Wed, 21 Oct 2015 00:00:00 GMT',
value => 0
)
);
$req->steps( [ sub { PE_LOGOUT_OK } ] );
}
PE_OK;
}
sub authLogout {
my ( $self, $req ) = @_;
my $res = $self->_authentication->authLogout($req);
@ -349,7 +370,7 @@ sub authenticate {
$req->steps( [
'setSessionInfo', 'setMacros',
'setPersistentSessionInfo', 'storeHistory',
@{ $self->afterData }, sub { PE_BADCREDENTIALS }
@{ $self->afterData }, sub { PE_BADCREDENTIALS }
]
);

View File

@ -133,10 +133,11 @@ sub login {
return $self->do(
$req,
[
'controlUrl', @{ $self->beforeAuth },
$self->authProcess, @{ $self->betweenAuthAndData },
$self->sessionData, @{ $self->afterData },
$self->validSession, @{ $self->endAuth },
'checkUnauthLogout', 'controlUrl', # Fix 2342
@{ $self->beforeAuth }, $self->authProcess,
@{ $self->betweenAuthAndData }, $self->sessionData,
@{ $self->afterData }, $self->validSession,
@{ $self->endAuth }
]
);
}
@ -146,11 +147,11 @@ sub postLogin {
return $self->do(
$req,
[
'restoreArgs', 'controlUrl',
@{ $self->beforeAuth }, $self->authProcess,
@{ $self->betweenAuthAndData }, $self->sessionData,
@{ $self->afterData }, $self->validSession,
@{ $self->endAuth },
'checkUnauthLogout', 'restoreArgs', # Fix 2342
'controlUrl', @{ $self->beforeAuth },
$self->authProcess, @{ $self->betweenAuthAndData },
$self->sessionData, @{ $self->afterData },
$self->validSession, @{ $self->endAuth }
]
);
}
@ -232,6 +233,24 @@ sub logout {
);
}
sub unauthLogout {
my ( $self, $req ) = @_;
$self->userLogger->info('Unauthenticated logout request');
$self->logger->debug('Cleaning pdata');
$self->logger->debug("Removing $self->{conf}->{cookieName} cookie");
$req->pdata( {} );
$req->addCookie(
$self->cookie(
name => $self->conf->{cookieName},
domain => $self->conf->{domain},
secure => $self->conf->{securedCookie},
expires => 'Wed, 21 Oct 2015 00:00:00 GMT',
value => 0
)
);
return $self->do( $req, [ sub { PE_LOGOUT_OK } ] );
}
# RUNNING METHODS
# ---------------
@ -1051,7 +1070,7 @@ sub registerLogin {
}
my $history = $req->sessionInfo->{_loginHistory} ||= {};
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
my $type = ( $req->authResult > 0 ? 'failed' : 'success' ) . 'Login';
$history->{$type} ||= [];
$self->logger->debug("Current login saved into $type");

View File

@ -84,6 +84,10 @@ sub _modifyPassword {
my $res = $self->modifyPassword( $req, $req->data->{newpassword} );
if ( $res == PE_PASSWORD_OK ) {
$self->logger->debug( 'Update password in session for ' . $req->user );
my $userlog = $req->sessionInfo->{ $self->conf->{whatToTrace} };
my $iplog = $req->sessionInfo->{ipAddr};
$self->userLogger->notice("Password changed for $userlog ($iplog)")
if ( defined $userlog and $iplog );
my $infos;
# Store new password if asked

View File

@ -18,9 +18,8 @@ our $VERSION = '2.1.0';
sub init {
my ($self) = @_;
$self->ldap
and $self->filter
and $self->Lemonldap::NG::Portal::Password::Base::init;
return ( $self->Lemonldap::NG::Portal::Password::Base::init
and $self->Lemonldap::NG::Portal::Lib::LDAP::init );
}
# Confirmation is done by Lib::Net::LDAP::userModifyPassword

View File

@ -4,6 +4,7 @@ use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_APACHESESSIONERROR
PE_ERROR
PE_OK
);
@ -37,9 +38,18 @@ sub changeUrldc {
$cdaInfos->{cookie_name} = $self->{conf}->{cookieName};
}
else {
$cdaInfos->{cookie_value} =
$req->{sessionInfo}->{_httpSession};
$cdaInfos->{cookie_name} = $self->{conf}->{cookieName} . "http";
if ( $req->{sessionInfo}->{_httpSession} ) {
$cdaInfos->{cookie_value} =
$req->{sessionInfo}->{_httpSession};
$cdaInfos->{cookie_name} = $self->{conf}->{cookieName} . "http";
}
else {
$self->logger->error(
"Session does not contain _httpSession field. "
. "Portal must be accessed over HTTPS when using CDA with double cookie"
);
return PE_ERROR;
}
}
my $cdaSession =

View File

@ -23,7 +23,7 @@ sub init {
}
$self->addUnauthRoute( checkstate => 'check', ['GET'] )
->addAuthRoute( checkstate => 'check', ['GET'] );
return 1;
}
@ -49,7 +49,7 @@ sub check {
# - "buildCookie" useless here
$req->steps( [
'getUser', 'authenticate',
@{ $self->p->betweenAuthAndData }, $self->sessionData,
@{ $self->p->betweenAuthAndData }, $self->p->sessionData,
@{ $self->p->afterData }, 'storeHistory',
@{ $self->p->endAuth }
]

View File

@ -115,7 +115,9 @@ sub display {
$self->userLogger->info("Using spoofed SSO groups if exist")
if ( $self->conf->{impersonationRule} );
$attrs = $self->_removePersistentAttributes($attrs)
$attrs =
$self->_removeKeys( $attrs, $self->persistentAttrs,
'Remove persistent session attributes...' )
unless $self->displayPersistentInfoRule->( $req, $req->userData );
# Create an array of hashes and dispatch attributes for template loop
@ -180,8 +182,7 @@ sub check {
LOGIN => '',
TOKEN => $token,
};
return $self->p->sendJSONresponse( $req, $params )
if ( $req->wantJSON );
return $self->p->sendJSONresponse( $req, $params ) if $req->wantJSON;
return $self->p->sendHtml( $req, 'checkuser', params => $params )
if $msg;
}
@ -195,7 +196,7 @@ sub check {
$user = '';
$attrs = {};
return $self->p->sendError( $req, 'Malformed user', 400 )
if ( $req->wantJSON );
if $req->wantJSON;
return $self->p->sendHtml(
$req,
'checkuser',
@ -219,7 +220,7 @@ sub check {
if ( !$user or $user eq $req->{user} ) {
$self->userLogger->info("checkUser requested for himself");
$self->userLogger->info("Using spoofed SSO groups if exist")
if ( $self->conf->{impersonationRule} );
if $self->conf->{impersonationRule};
$attrs = $req->userData;
$user = $req->{user};
}
@ -262,7 +263,10 @@ sub check {
}
# Check identities rule
$self->logger->info("\"$user\" is an unrestricted user!") if $unUser;
$self->logger->info( '"'
. $savedUserData->{ $self->conf->{whatToTrace} }
. '" is an unrestricted user!' )
if $unUser;
unless ( $unUser || $self->idRule->( $req, $attrs ) ) {
$self->userLogger->warn(
"checkUser requested for an invalid user ($user)");
@ -279,8 +283,10 @@ sub check {
$attrs = {};
}
else {
$msg = 'checkUser' . $self->merged;
$attrs = $self->_removePersistentAttributes($attrs)
$msg = 'checkUser' . $self->merged;
$attrs =
$self->_removeKeys( $attrs, $self->persistentAttrs,
'Remove persistent session attributes...' )
unless $self->displayPersistentInfoRule->( $req, $savedUserData );
if ($computed) {
@ -320,17 +326,23 @@ sub check {
$self->_createArray( $req, $attrs, $savedUserData ) );
}
if ( $self->p->checkXSSAttack( 'CheckUser URL', $url ) ) {
$url = '';
$auth = 'VHnotFound';
}
# Check if user is allowed to access submitted URL and compute headers
if ( $url and %$attrs ) {
# Check url format
$url = $self->_urlFormat($url);
my $originalUrl;
( $url, $originalUrl ) = $self->_resolveURL( $req, $url );
# User is allowed ?
$self->logger->debug(
"checkUser requested for user: $attrs->{ $self->{conf}->{whatToTrace} } and URL: $url"
"checkUser requested for user: $attrs->{ $self->{conf}->{whatToTrace} } and URL: $url | alias: $originalUrl"
);
$auth = $self->_authorization( $req, $url, $attrs );
$auth = $self->_authorization( $req, $originalUrl, $attrs );
if ( $auth >= 0 ) {
$auth = $auth ? "allowed" : "forbidden";
$self->logger->debug(
@ -338,7 +350,8 @@ sub check {
. "$auth to access to $url" );
# Return VirtualHost headers
$array_hdrs = $self->_headers( $req, $url, $attrs, $savedUserData );
$array_hdrs =
$self->_headers( $req, $originalUrl, $attrs, $savedUserData );
}
else {
$auth = 'VHnotFound';
@ -352,17 +365,14 @@ sub check {
# TODO:
my $params = {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
LANGS => $self->conf->{showLanguages},
MSG => $msg,
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
LOGIN => $user,
URL => (
$self->p->checkXSSAttack( 'URL', $url ) ? ""
: $url
),
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
LANGS => $self->conf->{showLanguages},
MSG => $msg,
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
LOGIN => $user,
URL => $url,
ALLOWED => $auth,
ALERTE_AUTH => $alert_auth,
HEADERS => $array_hdrs,
@ -370,7 +380,8 @@ sub check {
MACROS => $array_attrs->[1],
GROUPS => $array_attrs->[0],
TOKEN => (
$self->ottRule->( $req, {} ) ? $self->ott->createToken()
$self->ottRule->( $req, {} )
? $self->ott->createToken()
: ''
)
};
@ -380,17 +391,24 @@ sub check {
return $self->p->sendHtml( $req, 'checkuser', params => $params );
}
sub _urlFormat {
my ( $self, $url ) = @_;
$url = 'http://' . $url unless ( $url =~ m#^https?://[^/]*.*#i );
my ( $proto, $vhost, $appuri ) = $url =~ m#^(https?://)([^/]*)(.*)#i;
sub _resolveURL {
my ( $self, $req, $url ) = @_;
my ($proto) = $url =~ m#^(https?://).*#i;
my ( $vhost, $appuri ) = $url =~ m#^(?:https?://)?([^/]*)(.*)#i;
my ($port) = $vhost =~ m#^.+(:\d+)$#;
$port ||= '';
$vhost =~ s/:\d+$//;
$vhost .= $self->conf->{domain} unless ( $vhost =~ /\./ );
$proto =
$self->p->HANDLER->_isHttps( $req, $vhost ) ? 'https://' : 'http://'
unless $proto;
$self->logger->debug( 'VHost is ' . uc( ( split( /:/, $proto ) )[0] ) );
my $originalVhost = $self->p->HANDLER->resolveAlias($vhost);
return lc("$proto$vhost$port") . "$appuri";
return (
lc("$proto$vhost$port") . $appuri,
lc("$proto$originalVhost$port") . $appuri
);
}
sub _userData {
@ -407,12 +425,9 @@ sub _userData {
: push @$steps, 'setLocalGroups';
$req->steps($steps);
if ( my $error = $self->p->process($req) ) {
if ( $error == PE_BADCREDENTIALS ) {
$self->userLogger->warn(
'checkUser requested for an invalid user ('
. $req->{user}
. ")" );
}
$self->userLogger->warn(
'checkUser requested for an invalid user (' . $req->{user} . ")" )
if ( $error == PE_BADCREDENTIALS );
$self->logger->debug("Process returned error: $error");
return $req->error(PE_BADCREDENTIALS);
}
@ -468,16 +483,48 @@ sub _headers {
$vhost =~ s/:\d+$//;
$req->{env}->{HTTP_HOST} = $vhost;
$self->p->HANDLER->headersInit( $self->{conf} );
my $headers = $self->p->HANDLER->checkHeaders( $req, $attrs );
# Remove hidden headers relative to VHost if required
unless ( $self->unrestrictedUsersRule->( $req, $savedUserData ) ) {
my $keysToRemove = '';
$keysToRemove = '__all__'
if exists $self->conf->{checkUserHiddenHeaders}->{$vhost};
$keysToRemove = $self->conf->{checkUserHiddenHeaders}->{$vhost}
if ( $keysToRemove
&& $self->conf->{checkUserHiddenHeaders}->{$vhost} =~ /\w+/ );
if ( $keysToRemove eq '__all__' ) {
$self->logger->debug(
"Overwrite for VirtualHost: $vhost ALL valued header(s)...");
@$headers = map {
$_->{value} =~ /\w+/
? { key => $_->{key}, value => '******' }
: $_
} @$headers;
}
else {
$self->logger->debug(
"Mask hidden header(s) for VirtualHost: $vhost");
my $hash = { map { $_->{key} => $_->{value} } @$headers };
$hash = $self->_removeKeys( $hash, $keysToRemove,
"Overwrite valued \"$keysToRemove\" header(s)...", 1 );
@$headers = (
map { { key => $_, value => $hash->{$_} } }
sort keys %$hash
);
}
}
# Remove empty headers if required
unless ( $self->displayEmptyHeadersRule->( $req, $savedUserData ) ) {
$self->logger->debug("Remove empty headers...");
@$headers = grep $_->{value} =~ /.+/, @$headers;
}
$self->logger->debug(
"Return \"$attrs->{ $self->{conf}->{whatToTrace} }\" headers");
return $self->p->HANDLER->checkHeaders( $req, $attrs )
if ( $self->displayEmptyHeadersRule->( $req, $savedUserData ) );
$self->logger->debug("Remove empty headers");
my @headers = grep $_->{value} =~ /.+/,
@{ $self->p->HANDLER->checkHeaders( $req, $attrs ) };
return \@headers;
return $headers;
}
sub _createArray {
@ -511,7 +558,7 @@ sub _dispatchAttributes {
my ( $grps, $mcrs, $others ) = ( [], [], [] );
my $macros = $self->{conf}->{macros};
$self->logger->debug("Dispatching attributes...");
$self->logger->debug("Dispatch attributes...");
while (@$attrs) {
my $element = shift @$attrs;
$self->logger->debug( "Processing element: $element->{key} => "
@ -539,9 +586,9 @@ sub _dispatchAttributes {
# Sort real and spoofed attributes if required
if ( $self->sorted ) {
$self->logger->debug('Dispatching real and spoofed attributes...');
$self->logger->debug('Sort real and spoofed attributes...');
my ( $realAttrs, $spoofedAttrs ) = ( [], [] );
my $prefix = "$self->{conf}->{impersonationPrefix}";
my $prefix = $self->{conf}->{impersonationPrefix};
while (@$others) {
my $element = shift @$others;
$self->logger->debug( "Processing attribute $element->{key} => "
@ -560,12 +607,22 @@ sub _dispatchAttributes {
return [ $grps, $mcrs, $others ];
}
sub _removePersistentAttributes {
my ( $self, $attrs ) = @_;
my $regex = join '|', split /\s+/, $self->persistentAttrs;
sub _removeKeys {
my ( $self, $attrs, $hidden, $msg, $mask ) = @_;
my $regex = join '|', split /\s+/, $hidden;
my @keys = grep /$regex/, keys %$attrs;
$self->logger->debug("Remove persistent session attributes");
delete @$attrs{@keys};
$self->logger->debug($msg);
if ($mask) {
$self->userLogger->info('Hide some headers');
foreach (@keys) {
$attrs->{$_} = '******' if $attrs->{$_} =~ /\w+/;
}
}
else {
$self->userLogger->info('Remove some headers');
delete @$attrs{@keys};
}
return $attrs;
}

View File

@ -140,7 +140,7 @@ sub display {
sub run {
my ( $self, $req ) = @_;
my $statut = PE_OK;
my $realId = $req->{user};
my $realId = $req->userData->{ $self->conf->{whatToTrace} };
my $spoofId = $req->param('spoofId') || ''; # ContextSwitching required ?
my $unUser = $self->unrestrictedUsersRule->( $req, $req->userData ) || 0;
@ -202,7 +202,7 @@ sub _switchContext {
my ( $self, $req, $spoofId, $unUser ) = @_;
my $realSessionId = $req->userData->{_session_id};
my $realAuthLevel = $req->userData->{authenticationLevel};
my $realId = $req->{user};
my $realId = $req->userData->{ $self->conf->{whatToTrace} };
my $raz = 0;
$req->{user} = $spoofId;

View File

@ -54,9 +54,9 @@ sub run {
my ( $self, $req ) = @_;
my $user = $req->{userData}->{ $self->conf->{whatToTrace} };
# Check activation rules
# Check activation rule
unless ( $self->rule->( $req, $req->userData ) ) {
$self->userLogger->info("Global logout not required for $user");
$self->userLogger->info("GlobaLogout not allowed for $user");
return PE_OK;
}
@ -116,20 +116,22 @@ sub globalLogout {
# Read active sessions from token
my $sessions = eval { from_json( $token->{sessions} ) };
if ($@) {
$self->logger->error("Bad encoding in OTT: $@");
$self->logger->error(
"GlobalLogout: bad encoding in OTT ($@)");
$res = PE_ERROR;
}
my $as;
foreach (@$sessions) {
unless ( $as = $self->p->getApacheSession( $_->{id} ) ) {
$self->userLogger->info(
"GlobalLogout: session $_->{id} expired");
next;
}
my $user = $token->{user};
if ( $req->{userData}->{ $self->{conf}->{whatToTrace} } eq
$user )
{
my $user = $token->{user};
my $req_user =
$req->{userData}->{ $self->{conf}->{whatToTrace} };
if ( $req_user eq $user ) {
foreach (@$sessions) {
unless ( $as = $self->p->getApacheSession( $_->{id} ) )
{
$self->userLogger->info(
"GlobalLogout: session $_->{id} expired");
next;
}
unless ( $req->{userData}->{_session_id} eq $_->{id} ) {
$self->userLogger->info(
"Remove \"$user\" session: $_->{id}");
@ -137,11 +139,12 @@ sub globalLogout {
$count++;
}
}
else {
$self->userLogger->warn(
"GlobalLogout called with an invalid token");
$res = PE_TOKENEXPIRED;
}
}
else {
$self->userLogger->warn(
"GlobalLogout called with an invalid token: $req_user is NOT $user"
);
$res = PE_TOKENEXPIRED;
}
}
else {
@ -157,7 +160,7 @@ sub globalLogout {
}
return $self->p->do( $req, [ sub { $res } ] ) if $res;
$self->userLogger->info("$count remaining session(s) have been removed");
$self->userLogger->info("$count remaining session(s) removed");
return $self->p->do( $req, [ 'authLogout', 'deleteSession' ] );
}
@ -177,16 +180,16 @@ sub activeSessions {
$self->module->searchOn( $moduleOptions, $self->conf->{whatToTrace},
$user );
$self->logger->debug('Remove corrupted sessions...');
my $corrupted = 0;
$self->logger->debug('Remove non-SSO session(s)...');
my $other = 0;
foreach ( keys %$sessions ) {
unless ( $sessions->{$_}->{_session_kind} eq 'SSO' ) {
delete $sessions->{$_};
$corrupted++;
$other++;
}
}
$self->logger->info("$corrupted corrupted session(s) removed")
if $corrupted;
$self->logger->info("$other non-SSO session(s) removed")
if $other;
$self->logger->debug('Build an array ref with sessions info...');
@$activeSessions =

View File

@ -72,16 +72,17 @@ sub run {
$req, 'simpleInfo', params => { trspan => $msg }
)
);
$self->userLogger->error( 'User '
. $req->sessionInfo->{uid}
. " was not granted to open session (rule -> $rule)" );
$self->userLogger->error( 'User "'
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
. '" was not granted to open session (rule ->'
. "$rule)" );
$req->urldc( $self->conf->{portal} );
return $req->authResult(PE_SESSIONNOTGRANTED);
}
else {
$self->userLogger->error( 'User '
. $req->sessionInfo->{uid}
. " was not granted to open session (rule -> "
$self->userLogger->error( 'User "'
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
. '" was not granted to open session (rule -> '
. $self->conf->{grantSessionRules}->{$_}
. ")" );
$req->urldc( $self->conf->{portal} );

View File

@ -490,6 +490,11 @@ sub changePwd {
return $result;
}
my $userlog = $req->sessionInfo->{ $self->conf->{whatToTrace} };
my $iplog = $req->sessionInfo->{ipAddr};
$self->userLogger->notice("Password changed for $userlog ($iplog)")
if ( defined $userlog and $iplog );
# Send mail containing the new password
$req->data->{mailAddress} ||=
$self->p->getFirstValue(

View File

@ -182,7 +182,7 @@ sub removeOther {
}
return $self->p->do( $req, [ sub { $res } ] ) if $res;
$self->userLogger->info("$count remaining session(s) have been removed");
$self->userLogger->info("$count remaining session(s) removed");
$req->mustRedirect(1);
return $self->p->autoRedirect($req);
}

View File

@ -40,12 +40,16 @@ sub setSessionInfo {
%{ $self->conf->{ldapExportedVars} } );
while ( my ( $k, $v ) = each %vars ) {
my $value = $self->ldap->getLdapValue( $req->data->{ldapentry}, $v );
# getLdapValue returns an empty string for missing attribute
# but we really want to return undef so they don't get stored in session
$req->sessionInfo->{$k} =
$self->ldap->getLdapValue( $req->data->{ldapentry}, $v ) || undef;
}
# This has to be a string comparison because "0" is a valid attribute
# value. See #2403
$value = undef if ( $value eq "" );
$req->sessionInfo->{$k} = $value;
}
PE_OK;
}

View File

@ -7,12 +7,18 @@ package Lemonldap::NG::Portal::UserDB::Slave;
use strict;
use Mouse;
use Lemonldap::NG::Portal::Lib::Slave;
use Lemonldap::NG::Portal::Main::Constants qw(PE_FORBIDDENIP PE_OK);
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_FORBIDDENIP
);
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Common::Module';
extends qw(
Lemonldap::NG::Common::Module
Lemonldap::NG::Portal::Lib::Slave
);
# INITIALIZATION

View File

@ -482,7 +482,7 @@ $(window).on 'load', () ->
$('#newpassword')[0].setCustomValidity(translate('PE28'))
return
if window.datas.ppolicy?
if window.datas.ppolicy? and $('#newpassword').length
# Initialize display
checkpassword ''

Some files were not shown because too many files have changed in this diff Show More