Merge branch 'feature-delayed-2fa-2124' into 'v2.0'
Delay 2FA until required by an application See merge request lemonldap-ng/lemonldap-ng!147
This commit is contained in:
commit
66c68f6056
|
@ -868,6 +868,9 @@
|
|||
"allowOffline" : {
|
||||
"type" : "boolean"
|
||||
},
|
||||
"authnLevel" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"rule" : {
|
||||
"type" : "string"
|
||||
},
|
||||
|
@ -1057,6 +1060,9 @@
|
|||
"type" : "integer",
|
||||
"default" : 72000
|
||||
},
|
||||
"authnLevel" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
"rule" : {
|
||||
"type" : "string"
|
||||
},
|
||||
|
|
|
@ -109,6 +109,8 @@ Options
|
|||
application.
|
||||
- **User attribute** : session field that will be used as main
|
||||
identifier.
|
||||
- **Authentication Level** : required authentication level to access this
|
||||
application
|
||||
- **Rule** : The access control rule to enforce on this application. If
|
||||
left blank, access will be allowed for everyone.
|
||||
|
||||
|
|
|
@ -268,7 +268,8 @@ Options
|
|||
https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
for details. These offline sessions can be administered through
|
||||
the Session Browser.
|
||||
- **Allow OAuth2.0 Password Grant** (since version ``2.0.8``) Allow the use of the Resource Owner Password Credentials Grant on by this client. This feature only works if you have configured a form-based authentication module.
|
||||
- **Allow OAuth2.0 Password Grant** (since version ``2.0.8``): Allow the use of the Resource Owner Password Credentials Grant on by this client. This feature only works if you have configured a form-based authentication module.
|
||||
- **Authentication Level**: required authentication level to access this application
|
||||
- **Access Rule**: lets you specify a :doc:`Perl rule<rules_examples>` to restrict access to this client
|
||||
|
||||
- **Logout**
|
||||
|
|
|
@ -162,10 +162,12 @@ These options override service signature options (see
|
|||
Security
|
||||
''''''''
|
||||
|
||||
- **Encryption mode**: set the encryption mode for this IDP (None,
|
||||
- **Encryption mode**: set the encryption mode for this SP (None,
|
||||
NameID or Assertion).
|
||||
- **Enable use of IDP initiated URL**: set to ``On`` to enable IDP
|
||||
Initiated URL on this SP.
|
||||
- **Authentication Level**: required authentication level to access this SP
|
||||
- **Access Rule**: lets you specify a :doc:`Perl rule<rules_examples>` to restrict access to this SP
|
||||
|
||||
|
||||
.. tip::
|
||||
|
|
|
@ -29,22 +29,44 @@ The E-Mail, External and REST 2F modules
|
|||
parameters.
|
||||
|
||||
|
||||
.. tip::
|
||||
Registration on first use
|
||||
-------------------------
|
||||
|
||||
If you want to force a 2F registration on first login, you can
|
||||
use 'Require 2FA'. You can also use a rule to force 2FA registration
|
||||
only for some users.
|
||||
.. tip::
|
||||
If you want to force a 2F registration on first login, you can use the *Force
|
||||
2FA registration at login* option.
|
||||
|
||||
You can display a message if an
|
||||
expired second factor has been removed by enabling 'Display a message if
|
||||
an expired SF is removed' option or setting a rule.
|
||||
You can use a `rule<writingrulesand_headers>` to enable this behavior only for
|
||||
some users.
|
||||
|
||||
.. tip::
|
||||
Second factor expiration
|
||||
------------------------
|
||||
|
||||
Link to second factor Manager is automatically display if at least a
|
||||
SFA module is enabled. You can set a rule to display or not the
|
||||
link.
|
||||
You can display a message if an expired second factor has been removed by
|
||||
enabling *Display a message if an expired SF is removed* option or setting a
|
||||
rule.
|
||||
|
||||
Self-care on Portal
|
||||
-------------------
|
||||
|
||||
User may register second facrots themselves on the Portal by using the 2FA Manager.
|
||||
|
||||
The link will be displayed if at least a SFA module is enabled. You can set a
|
||||
rule to display or not the link.
|
||||
|
||||
Session upgrade through 2FA
|
||||
---------------------------
|
||||
|
||||
|beta|
|
||||
|
||||
If you enable the *Use 2FA for session upgrade* option, second factor will only
|
||||
be asked on login if the target application requires an authentication level
|
||||
that is strictly higher than the one obtained by the Authentication backend
|
||||
(first factor).
|
||||
|
||||
The session upgrade mechanism will only require the second factor step, instead
|
||||
of doing a complete reauthentication.
|
||||
|
||||
.. |beta| image:: /documentation/beta.png
|
||||
|
||||
Providing tokens from an external source
|
||||
----------------------------------------
|
||||
|
@ -100,4 +122,3 @@ To enable manager Second Factor Administration Module, set
|
|||
|
||||
[portal]
|
||||
enabledModules = conf, sessions, notifications, 2ndFA
|
||||
|
||||
|
|
|
@ -1098,6 +1098,8 @@ components:
|
|||
notOnOrAfterTimeout:
|
||||
type: integer
|
||||
default: 72000
|
||||
authnLevel:
|
||||
type: integer
|
||||
rule:
|
||||
type: string
|
||||
forceUTF8:
|
||||
|
@ -1181,6 +1183,8 @@ components:
|
|||
type: string
|
||||
allowOffline:
|
||||
type: boolean
|
||||
authnLevel:
|
||||
type: integer
|
||||
rule:
|
||||
type: string
|
||||
IDTokenSignAlg:
|
||||
|
|
|
@ -30,7 +30,7 @@ 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)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
|
||||
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|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)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|heck(?:State|User|XSS)|da)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
|
||||
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|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)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|heck(?:State|User|XSS)|da)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
|
||||
|
||||
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );
|
||||
|
||||
|
|
|
@ -24,12 +24,12 @@ 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)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|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|(?:ExportedVar|Macro)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)';
|
||||
our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)|heckJWTSignature|onfigurationURI)|S(?:toreIDToken|ortNumber|cope)|TokenEndpointAuthMethod|(?:JWKSTimeou|Promp)t|I(?:DTokenMaxAge|con)|U(?:iLocales|seNonce)|Display(?:Name)?|AcrValues|MaxAge)|ExportedVars|J(?:SON|WKS))';
|
||||
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:(?:uthorizationCode|ccessToken)Expiration|llow(?:PasswordGrant|Offline)|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 $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(?:ignS[LS]OMessage|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(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|ForceUTF8)|(?:ExportedAttribute|Macro)s|XML)';
|
||||
our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?: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 $authParameters = {
|
||||
|
|
|
@ -123,12 +123,6 @@ sub BUILD {
|
|||
$data = $self->_tie_session;
|
||||
}
|
||||
|
||||
# If session is created
|
||||
# Then set session kind in session
|
||||
if ( $creation and $self->kind ) {
|
||||
$data->{_session_kind} = $self->kind;
|
||||
}
|
||||
|
||||
if ( $self->{info} ) {
|
||||
foreach ( keys %{ $self->{info} } ) {
|
||||
next if ( $_ eq "_session_id" and $data->{_session_id} );
|
||||
|
@ -143,6 +137,12 @@ sub BUILD {
|
|||
delete $self->{info};
|
||||
}
|
||||
|
||||
# If session is created
|
||||
# Then set session kind in session
|
||||
if ( $creation and $self->kind ) {
|
||||
$data->{_session_kind} = $self->kind;
|
||||
}
|
||||
|
||||
# Load session data into object
|
||||
if ($data) {
|
||||
if ( $self->kind and $data->{_session_kind} ) {
|
||||
|
|
|
@ -18,6 +18,8 @@ sub portalConsts {
|
|||
'10' => 'PE_BADCERTIFICATE',
|
||||
'100' => 'PE_PP_NOT_ALLOWED_CHARACTER',
|
||||
'101' => 'PE_PP_NOT_ALLOWED_CHARACTERS',
|
||||
'102' => 'PE_UPGRADESESSION',
|
||||
'103' => 'PE_NO_SECOND_FACTORS',
|
||||
'2' => 'PE_FORMEMPTY',
|
||||
'21' => 'PE_PP_ACCOUNT_LOCKED',
|
||||
'22' => 'PE_PP_PASSWORD_EXPIRED',
|
||||
|
|
|
@ -265,17 +265,14 @@ sub checkMaintenanceMode {
|
|||
return 0;
|
||||
}
|
||||
|
||||
## @rmethod boolean grant(string uri, string cond)
|
||||
# Grant or refuse client using compiled regexp and functions
|
||||
## @rmethod int getLevel(string uri, string $vhost)
|
||||
# Return required authentication level for this URI
|
||||
# default to vhost authentication level
|
||||
# @param $uri URI
|
||||
# @param $cond optional Function granting access
|
||||
# @return True if the user is granted to access to the current URL
|
||||
sub grant {
|
||||
my ( $class, $req, $session, $uri, $cond, $vhost ) = @_;
|
||||
# @param $vhost vhost name, default to current request
|
||||
sub getLevel {
|
||||
my ( $class, $req, $uri, $vhost ) = @_;
|
||||
my $level;
|
||||
|
||||
return $cond->( $req, $session ) if ($cond);
|
||||
|
||||
$vhost ||= $class->resolveAlias($req);
|
||||
|
||||
# Using URL authentification level if exists
|
||||
|
@ -290,13 +287,33 @@ sub grant {
|
|||
last;
|
||||
}
|
||||
}
|
||||
$level
|
||||
? $class->logger->debug(
|
||||
'Found AuthnLevel=' . $level . ' for "' . "$vhost$uri" . '"' )
|
||||
: $class->logger->debug("No URL authentication level found...");
|
||||
if ($level) {
|
||||
$class->logger->debug(
|
||||
'Found AuthnLevel=' . $level . ' for "' . "$vhost$uri" . '"' );
|
||||
return $level;
|
||||
}
|
||||
else {
|
||||
$class->logger->debug("No URL authentication level found...");
|
||||
return $class->tsv->{authnLevel}->{$vhost};
|
||||
}
|
||||
}
|
||||
|
||||
## @rmethod boolean grant(string uri, string cond)
|
||||
# Grant or refuse client using compiled regexp and functions
|
||||
# @param $uri URI
|
||||
# @param $cond optional Function granting access
|
||||
# @return True if the user is granted to access to the current URL
|
||||
sub grant {
|
||||
my ( $class, $req, $session, $uri, $cond, $vhost ) = @_;
|
||||
|
||||
return $cond->( $req, $session ) if ($cond);
|
||||
|
||||
$vhost ||= $class->resolveAlias($req);
|
||||
|
||||
my $level = $class->getLevel( $req, $uri );
|
||||
|
||||
# Using VH authentification level if exists
|
||||
if ( $level ||= $class->tsv->{authnLevel}->{$vhost} ) {
|
||||
if ($level) {
|
||||
if ( $session->{authenticationLevel} < $level ) {
|
||||
$class->logger->debug(
|
||||
"User authentication level = $session->{authenticationLevel}");
|
||||
|
|
|
@ -713,6 +713,9 @@ sub attributes {
|
|||
'casAppMetaDataOptions' => {
|
||||
'type' => 'subContainer'
|
||||
},
|
||||
'casAppMetaDataOptionsAuthnLevel' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
'casAppMetaDataOptionsRule' => {
|
||||
'test' => sub {
|
||||
return perlExpr(@_);
|
||||
|
@ -2152,6 +2155,9 @@ m[^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
|
|||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'oidcRPMetaDataOptionsAuthnLevel' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcRPMetaDataOptionsAuthorizationCodeExpiration' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
|
@ -3392,6 +3398,9 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'keyTest' => qr/^[a-zA-Z](?:[a-zA-Z0-9_\-\.]*\w)?$/,
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
'samlSPMetaDataOptionsAuthnLevel' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
'samlSPMetaDataOptionsCheckSLOMessageSignature' => {
|
||||
'default' => 1,
|
||||
'type' => 'bool'
|
||||
|
@ -3629,6 +3638,9 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'default' => 1,
|
||||
'type' => 'boolOrExpr'
|
||||
},
|
||||
'sfOnlyUpgrade' => {
|
||||
'type' => 'bool'
|
||||
},
|
||||
'sfRemovedMsgRule' => {
|
||||
'default' => 0,
|
||||
'type' => 'boolOrExpr'
|
||||
|
@ -3674,6 +3686,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'skipUpgradeConfirmation' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'slaveAuthnLevel' => {
|
||||
'default' => 2,
|
||||
'type' => 'int'
|
||||
|
|
|
@ -601,6 +601,12 @@ sub attributes {
|
|||
documentation =>
|
||||
'Avoid asking confirmation when an Issuer asks to renew auth',
|
||||
},
|
||||
skipUpgradeConfirmation => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation =>
|
||||
'Avoid asking confirmation during a session upgrade',
|
||||
},
|
||||
refreshSessions => {
|
||||
type => 'bool',
|
||||
documentation => 'Refresh sessions plugin',
|
||||
|
@ -2304,6 +2310,10 @@ sub attributes {
|
|||
type => 'text',
|
||||
documentation => 'CAS User attribute',
|
||||
},
|
||||
casAppMetaDataOptionsAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation => 'Authentication level requires to access to this CAS application',
|
||||
},
|
||||
casAppMetaDataOptionsRule => {
|
||||
type => 'text',
|
||||
test => sub { return perlExpr(@_) },
|
||||
|
@ -2915,6 +2925,10 @@ sub attributes {
|
|||
type => 'bool',
|
||||
default => 1,
|
||||
},
|
||||
samlSPMetaDataOptionsAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation => 'Authentication level requires to access to this SP',
|
||||
},
|
||||
samlSPMetaDataOptionsRule => {
|
||||
type => 'text',
|
||||
test => sub { return perlExpr(@_) },
|
||||
|
@ -3010,6 +3024,11 @@ sub attributes {
|
|||
help => 'secondfactor.html',
|
||||
documentation => 'Second factor required',
|
||||
},
|
||||
sfOnlyUpgrade => {
|
||||
type => 'bool',
|
||||
help => 'secondfactor.html',
|
||||
documentation => 'Only trigger second factor on session upgrade',
|
||||
},
|
||||
sfManagerRule => {
|
||||
type => 'boolOrExpr',
|
||||
default => 1,
|
||||
|
@ -4065,6 +4084,10 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
|
|||
default => 0,
|
||||
documentation => 'Issue refresh tokens',
|
||||
},
|
||||
oidcRPMetaDataOptionsAuthnLevel => {
|
||||
type => 'int',
|
||||
documentation => 'Authentication level requires to access to this RP',
|
||||
},
|
||||
oidcRPMetaDataOptionsRule => {
|
||||
type => 'text',
|
||||
test => sub { return perlExpr(@_) },
|
||||
|
|
|
@ -134,6 +134,7 @@ sub cTrees {
|
|||
nodes => [
|
||||
"samlSPMetaDataOptionsEncryptionMode",
|
||||
"samlSPMetaDataOptionsEnableIDPInitiatedURL",
|
||||
"samlSPMetaDataOptionsAuthnLevel",
|
||||
"samlSPMetaDataOptionsRule",
|
||||
]
|
||||
}
|
||||
|
@ -221,6 +222,7 @@ sub cTrees {
|
|||
'oidcRPMetaDataOptionsRequirePKCE',
|
||||
'oidcRPMetaDataOptionsAllowOffline',
|
||||
'oidcRPMetaDataOptionsAllowPasswordGrant',
|
||||
'oidcRPMetaDataOptionsAuthnLevel',
|
||||
'oidcRPMetaDataOptionsRule',
|
||||
]
|
||||
},
|
||||
|
@ -286,6 +288,7 @@ sub cTrees {
|
|||
nodes => [
|
||||
'casAppMetaDataOptionsService',
|
||||
'casAppMetaDataOptionsUserAttribute',
|
||||
'casAppMetaDataOptionsAuthnLevel',
|
||||
'casAppMetaDataOptionsRule'
|
||||
]
|
||||
},
|
||||
|
|
|
@ -108,7 +108,9 @@ sub portalConstants {
|
|||
PE_RESETCERTIFICATE_FORMEMPTY => 98,
|
||||
PE_RESETCERTIFICATE_FIRSTACCESS => 99,
|
||||
PE_PP_NOT_ALLOWED_CHARACTER => 100,
|
||||
PE_PP_NOT_ALLOWED_CHARACTERS => 101
|
||||
PE_PP_NOT_ALLOWED_CHARACTERS => 101,
|
||||
PE_UPGRADESESSION => 102,
|
||||
PE_NO_SECOND_FACTORS => 103
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -907,6 +907,7 @@ sub tree {
|
|||
'sfRemovedNotifMsg',
|
||||
],
|
||||
},
|
||||
'sfOnlyUpgrade',
|
||||
'sfManagerRule',
|
||||
'sfRequired',
|
||||
]
|
||||
|
@ -1015,6 +1016,7 @@ sub tree {
|
|||
nodes => [
|
||||
'jsRedirect', 'noAjaxHook',
|
||||
'skipRenewConfirmation',
|
||||
'skipUpgradeConfirmation',
|
||||
]
|
||||
},
|
||||
'nginxCustomHandlers',
|
||||
|
|
|
@ -48,6 +48,12 @@ function templates(tpl,key) {
|
|||
"id" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsUserAttribute",
|
||||
"title" : "casAppMetaDataOptionsUserAttribute"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsAuthnLevel",
|
||||
"id" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsAuthnLevel",
|
||||
"title" : "casAppMetaDataOptionsAuthnLevel",
|
||||
"type" : "int"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsRule",
|
||||
"id" : tpl+"s/"+key+"/"+"casAppMetaDataOptionsRule",
|
||||
|
@ -535,6 +541,12 @@ function templates(tpl,key) {
|
|||
"title" : "oidcRPMetaDataOptionsAllowPasswordGrant",
|
||||
"type" : "bool"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAuthnLevel",
|
||||
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAuthnLevel",
|
||||
"title" : "oidcRPMetaDataOptionsAuthnLevel",
|
||||
"type" : "int"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRule",
|
||||
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRule",
|
||||
|
@ -1153,6 +1165,12 @@ function templates(tpl,key) {
|
|||
"title" : "samlSPMetaDataOptionsEnableIDPInitiatedURL",
|
||||
"type" : "bool"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"samlSPMetaDataOptionsAuthnLevel",
|
||||
"id" : tpl+"s/"+key+"/"+"samlSPMetaDataOptionsAuthnLevel",
|
||||
"title" : "samlSPMetaDataOptionsAuthnLevel",
|
||||
"type" : "int"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"samlSPMetaDataOptionsRule",
|
||||
"id" : tpl+"s/"+key+"/"+"samlSPMetaDataOptionsRule",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"تطبيق كاس",
|
||||
"casAppMetaDataOptions":"خيارات",
|
||||
"casAppMetaDataOptionsService":"خدمة أل يو أر ل",
|
||||
"casAppMetaDataOptionsAuthnLevel":"مستوى إثبات الهوية",
|
||||
"casAppMetaDataOptionsRule":"القاعدة",
|
||||
"casAppMetaDataMacros":"ماكرو",
|
||||
"casAppMetaDataOptionsUserAttribute":"خاصّيّة المستخدم",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"بروتوكول",
|
||||
"oidcRPMetaDataOptionsPublic":"Public client",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"مستوى إثبات الهوية",
|
||||
"oidcRPMetaDataOptionsRule":"قاعدة الدخول",
|
||||
"oidcRPMetaDataMacros":"ماكرو",
|
||||
"oidcOPMetaDataOptionsScope":"نطاق",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Second factors authentication",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"تفعيل",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"مستخدم واحد لكل عنوان آي بي",
|
||||
"skipRenewConfirmation":"Skip re-auth confirmation",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"مستوى إثبات الهوية",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"المتغيرات المصدرة",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"جلسة ليست مع أو بعد المدة",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"ليس على أو بعد المدة",
|
||||
"samlSPMetaDataOptionsForceUTF8":"فرضUTF-8 ",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"مستوى إثبات الهوية",
|
||||
"samlSPMetaDataOptionsRule":"قاعدة الدخول",
|
||||
"samlSPMetaDataMacros":"ماكرو",
|
||||
"samlIDPName":"اسم SAML IDP",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
|
||||
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"CAS Applikationen",
|
||||
"casAppMetaDataOptions":"Optionen",
|
||||
"casAppMetaDataOptionsService":"Service URL",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"casAppMetaDataOptionsRule":"Regel",
|
||||
"casAppMetaDataMacros":"Macros",
|
||||
"casAppMetaDataOptionsUserAttribute":"User attribute",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protocol",
|
||||
"oidcRPMetaDataOptionsPublic":"Public client",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"oidcRPMetaDataOptionsRule":"Access rule",
|
||||
"oidcRPMetaDataMacros":"Macros",
|
||||
"oidcOPMetaDataOptionsScope":"Scope",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Second factors authentication",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"Activation",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"One user per IP address",
|
||||
"skipRenewConfirmation":"Skip re-auth confirmation",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Authentication level",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"Exported variables",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"sessionNotOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"samlSPMetaDataOptionsRule":"Access rule",
|
||||
"samlSPMetaDataMacros":"Macros",
|
||||
"samlIDPName":"SAML IDP Name",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"CAS Applications",
|
||||
"casAppMetaDataOptions":"Options",
|
||||
"casAppMetaDataOptionsService":"Service URL",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"casAppMetaDataOptionsRule":"Rule",
|
||||
"casAppMetaDataMacros":"Macros",
|
||||
"casAppMetaDataOptionsUserAttribute":"User attribute",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protocol",
|
||||
"oidcRPMetaDataOptionsPublic":"Public client",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"oidcRPMetaDataOptionsRule":"Access rule",
|
||||
"oidcRPMetaDataMacros":"Macros",
|
||||
"oidcOPMetaDataOptionsScope":"Scope",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Second factors authentication",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"Activation",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"One user per IP address",
|
||||
"skipRenewConfirmation":"Skip re-auth confirmation",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Authentication level",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"Exported variables",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"sessionNotOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Authentication level",
|
||||
"samlSPMetaDataOptionsRule":"Access rule",
|
||||
"samlSPMetaDataMacros":"Macros",
|
||||
"samlIDPName":"SAML IDP Name",
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"Applications CAS",
|
||||
"casAppMetaDataOptions":"Options",
|
||||
"casAppMetaDataOptionsService":"URL du service",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Niveau d'authentification",
|
||||
"casAppMetaDataOptionsRule":"Règle",
|
||||
"casAppMetaDataMacros":"Macros",
|
||||
"casAppMetaDataOptionsUserAttribute":"Attribut de l'utilisateur",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protocole",
|
||||
"oidcRPMetaDataOptionsPublic":"Client public",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"PKCE requis",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Niveau d'authentification",
|
||||
"oidcRPMetaDataOptionsRule":"Règle d'accès",
|
||||
"oidcRPMetaDataMacros":"Macros",
|
||||
"oidcOPMetaDataOptionsScope":"Étendue",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Seconds facteurs d'authentification",
|
||||
"sfExtra":"Seconds facteurs additionnels",
|
||||
"sfManagerRule":"Afficher le lien du Gestionnaire",
|
||||
"sfOnlyUpgrade": "Utiliser le second facteur pour augmenter le niveau d'authentification",
|
||||
"sfRequired":"Exiger l'enrôlement d'un SF à l'authentification",
|
||||
"sfRemovedNotification":"Avertir si un SF expiré est supprimé",
|
||||
"sfRemovedMsgRule":"Activation",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"Une seule session par utilisateur",
|
||||
"singleUserByIP":"Un seul utilisateur par adresse IP",
|
||||
"skipRenewConfirmation":"Éviter la confirmation de ré-authentification",
|
||||
"skipUpgradeConfirmation":"Éviter la confirmation d'élévation du niveau d'authentification",
|
||||
"slaveAuthnLevel":"Niveau d'authentification",
|
||||
"slaveDisplayLogo":"Afficher le logo d'authentification",
|
||||
"slaveExportedVars":"Variables exportées",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"Durée sessionNotOnOrAfter",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Durée notOnOrAfter",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Forcer l'UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Niveau d'authentification",
|
||||
"samlSPMetaDataOptionsRule":"Règle d'accès",
|
||||
"samlSPMetaDataMacros":"Macros",
|
||||
"samlIDPName":"Nom du fournisseur d'identité SAML",
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"Applicazioni CAS",
|
||||
"casAppMetaDataOptions":"Opzioni",
|
||||
"casAppMetaDataOptionsService":"URL del servizio",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Livello di autenticazione",
|
||||
"casAppMetaDataOptionsRule":"Regola",
|
||||
"casAppMetaDataMacros":"Macro",
|
||||
"casAppMetaDataOptionsUserAttribute":"Attributo utente",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protocollo",
|
||||
"oidcRPMetaDataOptionsPublic":"Cliente pubblico",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Richiedi PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Livello di autenticazione",
|
||||
"oidcRPMetaDataOptionsRule":"Regola di accesso",
|
||||
"oidcRPMetaDataMacros":"Macro",
|
||||
"oidcOPMetaDataOptionsScope":"Scopo",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Autenticazione a due fattori",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"Attivazione",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"One user per IP address",
|
||||
"skipRenewConfirmation":"Salta la conferma di re-auth",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Livello di autenticazione",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"Variabili esportate",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"Durata sessionNotOnOrAfter ",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Durata di notOnOrAfter ",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Forza UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Livello di autenticazione",
|
||||
"samlSPMetaDataOptionsRule":"Regola di accesso",
|
||||
"samlSPMetaDataMacros":"Macro",
|
||||
"samlIDPName":"Nome di SAML IDP ",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
|
||||
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
|
||||
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"Aplikacje CAS",
|
||||
"casAppMetaDataOptions":"Opcje",
|
||||
"casAppMetaDataOptionsService":"URL usługi",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Poziom uwierzytelnienia",
|
||||
"casAppMetaDataOptionsRule":"Reguła",
|
||||
"casAppMetaDataMacros":"Makra",
|
||||
"casAppMetaDataOptionsUserAttribute":"Atrybut użytkownika",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protokół",
|
||||
"oidcRPMetaDataOptionsPublic":"Klient publiczny",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Wymagaj PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Poziom uwierzytelnienia",
|
||||
"oidcRPMetaDataOptionsRule":"Reguła dostępu",
|
||||
"oidcRPMetaDataMacros":"Makra",
|
||||
"oidcOPMetaDataOptionsScope":"Zakres",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Drugi czynnik uwierzytelniania",
|
||||
"sfExtra":"Dodatkowe drugie czynniki",
|
||||
"sfManagerRule":"Link do Menedżera wyświetlania",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Wymuś rejestrację 2FA przy logowaniu",
|
||||
"sfRemovedNotification":"Ostrzeż, gdy przeterminowany 2FA został usunięty",
|
||||
"sfRemovedMsgRule":"Aktywacja",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"Jedna sesja na użytkownika",
|
||||
"singleUserByIP":"Jeden użytkownik na adres IP",
|
||||
"skipRenewConfirmation":"Pomiń potwierdzenie ponownego uwierzytelnienia",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Poziom uwierzytelnienia",
|
||||
"slaveDisplayLogo":"Wyświetl logo uwierzytelniające",
|
||||
"slaveExportedVars":"Wyeksportowane zmienne",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"czas trwania sessionNotOnOrAfter",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"czas trwania notOnOrAfter",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Wymuś UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Poziom uwierzytelnienia",
|
||||
"samlSPMetaDataOptionsRule":"Reguła dostępu",
|
||||
"samlSPMetaDataMacros":"Makra",
|
||||
"samlIDPName":"Nazwa IDP SAML",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"Limit czasu sesji RelayState",
|
||||
"samlUseQueryStringSpecific":"Użyj określonej metody query_string",
|
||||
"samlOverrideIDPEntityID":"Zastąp identyfikator jednostki podczas działania jako IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"CAS Uygulamaları",
|
||||
"casAppMetaDataOptions":"Seçenekler",
|
||||
"casAppMetaDataOptionsService":"Servis URL'si",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Doğrulama seviyesi",
|
||||
"casAppMetaDataOptionsRule":"Kural",
|
||||
"casAppMetaDataMacros":"Makrolar",
|
||||
"casAppMetaDataOptionsUserAttribute":"Kullanıcı niteliği",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protokol",
|
||||
"oidcRPMetaDataOptionsPublic":"Açık istemci",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"PKCE gerektir",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Doğrulama seviyesi",
|
||||
"oidcRPMetaDataOptionsRule":"Erişim kuralı",
|
||||
"oidcRPMetaDataMacros":"Makrolar",
|
||||
"oidcOPMetaDataOptionsScope":"Kapsam",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"İki faktörlü kimlik doğrulaması",
|
||||
"sfExtra":"Ek ikinci faktörler",
|
||||
"sfManagerRule":"Yönetici bağlantısını görüntüle",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Girişte 2FA kayıtlanmaya zorla",
|
||||
"sfRemovedNotification":"Süresi dolan 2FA kaldırıldığında uyar",
|
||||
"sfRemovedMsgRule":"Aktivasyon",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"Her kullanıcı için bir oturum",
|
||||
"singleUserByIP":"Her IP adresi için bir kullanıcı",
|
||||
"skipRenewConfirmation":"Yeniden yetkilendirme doğrulamasını geç",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Doğrulama seviyesi",
|
||||
"slaveDisplayLogo":"Doğrulama logosunu görüntüle",
|
||||
"slaveExportedVars":"Dışa aktarılan değişkenler",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"sessionNotOnOrAfter süresi",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter süresi",
|
||||
"samlSPMetaDataOptionsForceUTF8":"UTF-8'e zorla",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Doğrulama seviyesi",
|
||||
"samlSPMetaDataOptionsRule":"Erişim kuralı",
|
||||
"samlSPMetaDataMacros":"Makrolar",
|
||||
"samlIDPName":"SAML IDP Adı",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"RelayState oturum zaman aşımı",
|
||||
"samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
|
||||
"samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"Ứng dụng CAS",
|
||||
"casAppMetaDataOptions":"Tùy chọn",
|
||||
"casAppMetaDataOptionsService":"Dịch vụ URL",
|
||||
"casAppMetaDataOptionsAuthnLevel":"Mức xác thực",
|
||||
"casAppMetaDataOptionsRule":"Quy tắc",
|
||||
"casAppMetaDataMacros":"Macros",
|
||||
"casAppMetaDataOptionsUserAttribute":"thuộc tính người dùng",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Giao thức",
|
||||
"oidcRPMetaDataOptionsPublic":"Public client",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"Mức xác thực",
|
||||
"oidcRPMetaDataOptionsRule":"Quy tắc truy cập",
|
||||
"oidcRPMetaDataMacros":"Macros",
|
||||
"oidcOPMetaDataOptionsScope":"Phạm vi",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Second factors authentication",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"Kích hoạt",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"Một người dùng theo địa chỉ IP",
|
||||
"skipRenewConfirmation":"Skip re-auth confirmation",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"Mức xác thực",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"Biến đã được xuất",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"thời gian sessionNotOnOrAfter ",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"Thời gian notOnOrAfter ",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Bắt buộc UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"Mức xác thực",
|
||||
"samlSPMetaDataOptionsRule":"Quy tắc truy cập",
|
||||
"samlSPMetaDataMacros":"Macros",
|
||||
"samlIDPName":"Tên SAML IDP ",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
|
||||
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
"casAppMetaDataNodes":"CAS 系列应用",
|
||||
"casAppMetaDataOptions":"选项",
|
||||
"casAppMetaDataOptionsService":"服务 URL",
|
||||
"casAppMetaDataOptionsAuthnLevel":"认证等级",
|
||||
"casAppMetaDataOptionsRule":"规则",
|
||||
"casAppMetaDataMacros":"Macros",
|
||||
"casAppMetaDataOptionsUserAttribute":"User attribute",
|
||||
|
@ -593,6 +594,7 @@
|
|||
"oidcOPMetaDataOptionsProtocol":"Protocol",
|
||||
"oidcRPMetaDataOptionsPublic":"Public client",
|
||||
"oidcRPMetaDataOptionsRequirePKCE":"Require PKCE",
|
||||
"oidcRPMetaDataOptionsAuthnLevel":"认证等级",
|
||||
"oidcRPMetaDataOptionsRule":"Access rule",
|
||||
"oidcRPMetaDataMacros":"Macros",
|
||||
"oidcOPMetaDataOptionsScope":"Scope",
|
||||
|
@ -847,6 +849,7 @@
|
|||
"sfaTitle":"Second factors authentication",
|
||||
"sfExtra":"Additional second factors",
|
||||
"sfManagerRule":"Display Manager link",
|
||||
"sfOnlyUpgrade": "Use 2FA for session upgrade",
|
||||
"sfRequired":"Force 2FA registration at login",
|
||||
"sfRemovedNotification":"Warn if an expired 2FA is removed",
|
||||
"sfRemovedMsgRule":"激活",
|
||||
|
@ -862,6 +865,7 @@
|
|||
"singleSession":"One session per user",
|
||||
"singleUserByIP":"One user per IP address",
|
||||
"skipRenewConfirmation":"Skip re-auth confirmation",
|
||||
"skipUpgradeConfirmation":"Skip upgrade confirmation",
|
||||
"slaveAuthnLevel":"认证等级",
|
||||
"slaveDisplayLogo":"Display authentication logo",
|
||||
"slaveExportedVars":"Exported variables",
|
||||
|
@ -1074,6 +1078,7 @@
|
|||
"samlSPMetaDataOptionsSessionNotOnOrAfterTimeout":"sessionNotOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsNotOnOrAfterTimeout":"notOnOrAfter duration",
|
||||
"samlSPMetaDataOptionsForceUTF8":"Force UTF-8",
|
||||
"samlSPMetaDataOptionsAuthnLevel":"认证等级",
|
||||
"samlSPMetaDataOptionsRule":"Access rule",
|
||||
"samlSPMetaDataMacros":"Macros",
|
||||
"samlIDPName":"SAML IDP Name",
|
||||
|
@ -1141,4 +1146,4 @@
|
|||
"samlRelayStateTimeout":"RelayState session timeout",
|
||||
"samlUseQueryStringSpecific":"Use specific query_string method",
|
||||
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -19,6 +19,7 @@ use Lemonldap::NG::Portal::Main::Constants qw(
|
|||
PE_OK
|
||||
PE_SENDRESPONSE
|
||||
PE_TOKENEXPIRED
|
||||
PE_NO_SECOND_FACTORS
|
||||
);
|
||||
|
||||
our $VERSION = '2.0.8';
|
||||
|
@ -198,7 +199,30 @@ sub run {
|
|||
$self->logger->debug("2F checkLogins set") if ($checkLogins);
|
||||
|
||||
# Skip 2F unless a module has been registered
|
||||
return PE_OK unless ( @{ $self->sfModules } );
|
||||
unless ( @{ $self->sfModules } ) {
|
||||
if ( $self->conf->{sfOnlyUpgrade} and $req->data->{doingSfUpgrade} ) {
|
||||
$self->logger->error(
|
||||
"Trying to perform 2FA session upgrade but no "
|
||||
. "second factor modules are configured" );
|
||||
return PE_ERROR;
|
||||
}
|
||||
else {
|
||||
return PE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
# Skip 2F if authnLevel is already high enough
|
||||
if (
|
||||
$self->conf->{sfOnlyUpgrade}
|
||||
and ( ( $req->pdata->{targetAuthnLevel} || 0 ) <=
|
||||
( $req->sessionInfo->{authenticationLevel} || 0 ) )
|
||||
)
|
||||
{
|
||||
$self->logger->debug(
|
||||
"Current authentication level satisfied target service,"
|
||||
. " skipping 2FA" );
|
||||
return PE_OK;
|
||||
}
|
||||
|
||||
# Remove expired 2F devices
|
||||
my $session = $req->sessionInfo;
|
||||
|
@ -296,7 +320,16 @@ sub run {
|
|||
return PE_SENDRESPONSE;
|
||||
}
|
||||
else {
|
||||
return PE_OK;
|
||||
if ( $self->conf->{sfOnlyUpgrade} and $req->data->{doingSfUpgrade} )
|
||||
{
|
||||
|
||||
# cancel redirection to issuer/vhost
|
||||
delete $req->pdata->{_url};
|
||||
return PE_NO_SECOND_FACTORS;
|
||||
}
|
||||
else {
|
||||
return PE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,13 @@ sub storeEnvAndCheckGateway {
|
|||
|
||||
if ($app) {
|
||||
$req->env->{llng_cas_app} = $app;
|
||||
|
||||
# Store target authentication level in pdata
|
||||
my $targetAuthnLevel = $self->conf->{casAppMetaDataOptions}->{$app}
|
||||
->{casAppMetaDataOptionsAuthnLevel};
|
||||
$req->pdata->{targetAuthnLevel} = $targetAuthnLevel
|
||||
if $targetAuthnLevel;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,20 +157,6 @@ sub run {
|
|||
|| $req->param('gateway');
|
||||
my $casServiceTicket;
|
||||
|
||||
# Renew
|
||||
if ( $renew
|
||||
and $renew eq 'true'
|
||||
and time - $req->sessionInfo->{_utime} >
|
||||
$self->conf->{portalForceAuthnInterval} )
|
||||
{
|
||||
|
||||
# Authentication must be replayed
|
||||
$self->logger->debug("Authentication renew requested");
|
||||
$self->{updateSession} = 1;
|
||||
$req->env->{QUERY_STRING} =~ s/renew=true/renew=false/;
|
||||
return $self->reAuth($req);
|
||||
}
|
||||
|
||||
# If no service defined, exit
|
||||
unless ( defined $service ) {
|
||||
$self->logger->debug("No service defined in CAS URL");
|
||||
|
@ -177,6 +170,26 @@ sub run {
|
|||
my ( $host, $uri ) = ( $1, $2 );
|
||||
my $app = $self->casAppList->{$host};
|
||||
|
||||
my $spAuthnLevel =
|
||||
$self->conf->{casAppMetaDataOptions}->{$app}
|
||||
->{casAppMetaDataOptionsAuthnLevel} || 0;
|
||||
|
||||
# Renew
|
||||
if ( $renew
|
||||
and $renew eq 'true'
|
||||
and time - $req->sessionInfo->{_utime} >
|
||||
$self->conf->{portalForceAuthnInterval} )
|
||||
{
|
||||
|
||||
# Authentication must be replayed
|
||||
$self->logger->debug("Authentication renew requested");
|
||||
$self->{updateSession} = 1;
|
||||
$req->env->{QUERY_STRING} =~ s/renew=true/renew=false/;
|
||||
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->reAuth($req);
|
||||
}
|
||||
|
||||
# Check access on the service
|
||||
my $casAccessControlPolicy = $self->conf->{casAccessControlPolicy};
|
||||
|
||||
|
@ -188,6 +201,21 @@ sub run {
|
|||
$self->userLogger->error('CAS service not configured');
|
||||
return PE_CAS_SERVICE_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
# Check if we have sufficient auth level
|
||||
my $authenticationLevel =
|
||||
$req->{sessionInfo}->{authenticationLevel} || 0;
|
||||
if ( $authenticationLevel < $spAuthnLevel ) {
|
||||
$self->logger->debug(
|
||||
"Insufficient authentication level for service $app"
|
||||
. " (has: $authenticationLevel, want: $spAuthnLevel)" );
|
||||
|
||||
# Reauth with sp auth level as target
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->upgradeAuth($req);
|
||||
}
|
||||
|
||||
# Check access rule
|
||||
if ( my $rule = $self->spRules->{$app} ) {
|
||||
if ( $rule->( $req, $req->sessionInfo ) ) {
|
||||
$self->logger->debug("CAS service $service access allowed");
|
||||
|
|
|
@ -321,6 +321,9 @@ sub run {
|
|||
);
|
||||
}
|
||||
|
||||
my $spAuthnLevel = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAuthnLevel} || 0;
|
||||
|
||||
# Check if user needs to be reauthenticated
|
||||
my $prompt = $oidc_request->{'prompt'};
|
||||
if (
|
||||
|
@ -334,6 +337,7 @@ sub run {
|
|||
$self->logger->debug(
|
||||
"Reauthentication required by Relying Party in prompt parameter"
|
||||
);
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->reAuth($req);
|
||||
}
|
||||
|
||||
|
@ -343,9 +347,23 @@ sub run {
|
|||
$self->logger->debug(
|
||||
"Reauthentication forced because authentication time ($_lastAuthnUTime) is too old (>$max_age s)"
|
||||
);
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->reAuth($req);
|
||||
}
|
||||
|
||||
# Check if we have sufficient auth level
|
||||
my $authenticationLevel =
|
||||
$req->{sessionInfo}->{authenticationLevel} || 0;
|
||||
if ( $authenticationLevel < $spAuthnLevel ) {
|
||||
$self->logger->debug(
|
||||
"Insufficient authentication level for service $rp"
|
||||
. " (has: $authenticationLevel, want: $spAuthnLevel)" );
|
||||
|
||||
# Reauth with sp auth level as target
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->upgradeAuth($req);
|
||||
}
|
||||
|
||||
# Check scope validity
|
||||
# We use a slightly more relaxed version of
|
||||
# https://tools.ietf.org/html/rfc6749#appendix-A.4
|
||||
|
@ -2162,6 +2180,12 @@ sub exportRequestParameters {
|
|||
if ( $req->param('client_id') ) {
|
||||
my $rp = $self->getRP( $req->param('client_id') );
|
||||
$req->env->{"llng_oidc_rp"} = $rp if $rp;
|
||||
|
||||
# Store target authentication level in pdata
|
||||
my $targetAuthnLevel = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAuthnLevel};
|
||||
$req->pdata->{targetAuthnLevel} = $targetAuthnLevel
|
||||
if $targetAuthnLevel;
|
||||
}
|
||||
|
||||
return PE_OK;
|
||||
|
|
|
@ -185,6 +185,13 @@ sub storeEnv {
|
|||
$req->env->{llng_saml_sp} = $sp;
|
||||
if ( my $spConfKey = $self->spList->{$sp}->{confKey} ) {
|
||||
$req->env->{llng_saml_spconfkey} = $spConfKey;
|
||||
|
||||
# Store target authentication level in pdata
|
||||
my $targetAuthnLevel =
|
||||
$self->conf->{samlSPMetaDataOptions}->{$spConfKey}
|
||||
->{samlSPMetaDataOptionsAuthnLevel};
|
||||
$req->pdata->{targetAuthnLevel} = $targetAuthnLevel
|
||||
if $targetAuthnLevel;
|
||||
}
|
||||
}
|
||||
return PE_OK;
|
||||
|
@ -389,6 +396,7 @@ sub run {
|
|||
$self->logger->debug("$sp match $spConfKey SP in configuration");
|
||||
$req->env->{llng_saml_spconfkey} = $spConfKey;
|
||||
|
||||
# Check access rule
|
||||
if ( my $rule = $self->spRules->{$spConfKey} ) {
|
||||
unless ( $rule->( $req, $req->sessionInfo ) ) {
|
||||
$self->userLogger->warn( 'User '
|
||||
|
@ -450,6 +458,10 @@ sub run {
|
|||
|
||||
$self->logger->debug("SSO: authentication request is valid");
|
||||
|
||||
my $spAuthnLevel =
|
||||
$self->conf->{samlSPMetaDataOptions}->{$spConfKey}
|
||||
->{samlSPMetaDataOptionsAuthnLevel} || 0;
|
||||
|
||||
# Get ForceAuthn flag
|
||||
my $force_authn;
|
||||
|
||||
|
@ -477,6 +489,7 @@ sub run {
|
|||
. $req->sessionInfo->{ $self->conf->{whatToTrace} } );
|
||||
|
||||
# Replay authentication process
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->reAuth($req);
|
||||
}
|
||||
|
||||
|
@ -486,9 +499,18 @@ sub run {
|
|||
unless ( $self->checkDestination( $login->request, $url ) );
|
||||
}
|
||||
|
||||
# Map authenticationLevel with SAML2 authentication context
|
||||
# Check if we have sufficient auth level
|
||||
my $authenticationLevel =
|
||||
$req->{sessionInfo}->{authenticationLevel};
|
||||
$req->{sessionInfo}->{authenticationLevel} || 0;
|
||||
if ( $authenticationLevel < $spAuthnLevel ) {
|
||||
$self->logger->debug(
|
||||
"Insufficient authentication level for service $spConfKey"
|
||||
. " (has: $authenticationLevel, want: $spAuthnLevel)" );
|
||||
|
||||
# Reauth with sp auth level as target
|
||||
$req->pdata->{targetAuthnLevel} = $spAuthnLevel;
|
||||
return $self->upgradeAuth($req);
|
||||
}
|
||||
|
||||
$authn_context =
|
||||
$self->authnLevel2authnContext($authenticationLevel);
|
||||
|
|
|
@ -105,6 +105,8 @@ use constant {
|
|||
PE_RESETCERTIFICATE_FIRSTACCESS => 99,
|
||||
PE_PP_NOT_ALLOWED_CHARACTER => 100,
|
||||
PE_PP_NOT_ALLOWED_CHARACTERS => 101,
|
||||
PE_UPGRADESESSION => 102,
|
||||
PE_NO_SECOND_FACTORS => 103,
|
||||
};
|
||||
|
||||
sub portalConsts {
|
||||
|
@ -119,6 +121,8 @@ sub portalConsts {
|
|||
'10' => 'PE_BADCERTIFICATE',
|
||||
'100' => 'PE_PP_NOT_ALLOWED_CHARACTER',
|
||||
'101' => 'PE_PP_NOT_ALLOWED_CHARACTERS',
|
||||
'102' => 'PE_UPGRADESESSION',
|
||||
'103' => 'PE_NO_SECOND_FACTORS',
|
||||
'2' => 'PE_FORMEMPTY',
|
||||
'21' => 'PE_PP_ACCOUNT_LOCKED',
|
||||
'22' => 'PE_PP_PASSWORD_EXPIRED',
|
||||
|
@ -310,7 +314,9 @@ our @EXPORT_OK = (
|
|||
'PE_RESETCERTIFICATE_FORMEMPTY',
|
||||
'PE_RESETCERTIFICATE_FIRSTACCESS',
|
||||
'PE_PP_NOT_ALLOWED_CHARACTER',
|
||||
'PE_PP_NOT_ALLOWED_CHARACTERS'
|
||||
'PE_PP_NOT_ALLOWED_CHARACTERS',
|
||||
'PE_UPGRADESESSION',
|
||||
'PE_NO_SECOND_FACTORS'
|
||||
);
|
||||
our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK, 'import' ], );
|
||||
|
||||
|
|
|
@ -261,15 +261,20 @@ sub display {
|
|||
);
|
||||
}
|
||||
|
||||
elsif ( $req->error == PE_RENEWSESSION ) {
|
||||
# when upgrading session, the administrator can configure LLNG
|
||||
# to ask only for 2FA
|
||||
elsif ( $req->error == PE_UPGRADESESSION ) {
|
||||
$skinfile = 'upgradesession';
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => 'askToRenew',
|
||||
CONFIRMKEY => $self->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->data->{_url},
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
FORMACTION => '/upgradesession',
|
||||
MSG => 'askToUpgrade',
|
||||
PORTALBUTTON => 1,
|
||||
BUTTON => 'upgradeSession',
|
||||
CONFIRMKEY => $self->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->data->{_url},
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
|
@ -278,13 +283,37 @@ sub display {
|
|||
);
|
||||
}
|
||||
|
||||
# renew uses the same plugin as upgrade, but first factor is mandatory
|
||||
elsif ( $req->error == PE_RENEWSESSION ) {
|
||||
$skinfile = 'upgradesession';
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
FORMACTION => '/renewsession',
|
||||
MSG => 'askToRenew',
|
||||
CONFIRMKEY => $self->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
PORTALBUTTON => 1,
|
||||
BUTTON => 'renewSession',
|
||||
URL => $req->data->{_url},
|
||||
(
|
||||
$req->data->{customScript}
|
||||
? ( CUSTOM_SCRIPT => $req->data->{customScript} )
|
||||
: ()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
# Looks a lot like upgradesession, but no portal logo
|
||||
elsif ( $req->error == PE_MUSTAUTHN ) {
|
||||
$skinfile = 'updatesession';
|
||||
$skinfile = 'upgradesession';
|
||||
%templateParams = (
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
FORMACTION => '/renewsession',
|
||||
MSG => 'PE87',
|
||||
CONFIRMKEY => $self->stamp,
|
||||
BUTTON => 'renewSession',
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->data->{_url},
|
||||
(
|
||||
|
|
|
@ -17,6 +17,7 @@ use Lemonldap::NG::Common::FormEncode;
|
|||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_OK
|
||||
PE_RENEWSESSION
|
||||
PE_UPGRADESESSION
|
||||
);
|
||||
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
@ -250,6 +251,19 @@ qq'<script type="text/javascript" src="$self->{p}->{staticPrefix}/common/js/auto
|
|||
return PE_RENEWSESSION;
|
||||
}
|
||||
|
||||
sub upgradeAuth {
|
||||
my ( $self, $req ) = @_;
|
||||
$req->data->{customScript} =
|
||||
qq'<script type="text/javascript" src="$self->{p}->{staticPrefix}/common/js/autoRenew.min.js"></script>'
|
||||
if ( $self->conf->{skipUpgradeConfirmation} );
|
||||
$req->data->{_url} =
|
||||
encode_base64( $self->conf->{portal} . $req->path_info, '' );
|
||||
$req->pdata->{ $self->ipath } = $self->storeRequest($req);
|
||||
push @{ $req->pdata->{keepPdata} }, $self->ipath, $self->ipath . 'Path';
|
||||
$req->pdata->{issuerTs} = time;
|
||||
return PE_UPGRADESESSION;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
|
|
|
@ -138,11 +138,18 @@ sub controlUrl {
|
|||
# Unprotected hosts
|
||||
my ( $vhost, $appuri ) = $tmp =~ m#^https?://([^/]*)(.*)#;
|
||||
$vhost =~ s/:\d+$//;
|
||||
$vhost = 'http://' . $self->HANDLER->resolveAlias($vhost);
|
||||
|
||||
# try to resolve alias
|
||||
my $originalVhost = $self->HANDLER->resolveAlias($vhost);
|
||||
$vhost = 'http://' . $originalVhost;
|
||||
$self->logger->debug( "Required URL (param: "
|
||||
. ( $req->param('logout') ? 'HTTP Referer' : 'urldc' )
|
||||
. " | value: $tmp | alias: $vhost)" );
|
||||
|
||||
# If the target URL has an authLevel set in config, remember it.
|
||||
my $level = $self->HANDLER->getLevel( $req, $appuri, $originalVhost );
|
||||
$req->pdata->{targetAuthnLevel} = $level if $level;
|
||||
|
||||
if ( $tmp
|
||||
and !$self->isTrustedUrl($tmp)
|
||||
and !$self->isTrustedUrl($vhost) )
|
||||
|
|
|
@ -381,7 +381,7 @@ sub autoRedirect {
|
|||
)
|
||||
{
|
||||
$self->logger->info("Force cleaning pdata");
|
||||
$req->pdata( {} );
|
||||
delete $req->{pdata}->{_url};
|
||||
}
|
||||
return [ 302, [ Location => $req->{urldc}, $req->spliceHdrs ], [] ];
|
||||
}
|
||||
|
|
|
@ -32,14 +32,38 @@ sub init {
|
|||
"-> Upgrade tokens will be stored into global storage");
|
||||
$self->ott->cache(undef);
|
||||
}
|
||||
$self->addAuthRoute( upgradesession => 'ask', ['GET'] );
|
||||
$self->addAuthRoute( upgradesession => 'confirm', ['POST'] );
|
||||
$self->addAuthRoute( upgradesession => 'askUpgrade', ['GET'] );
|
||||
$self->addAuthRoute( upgradesession => 'confirmUpgrade', ['POST'] );
|
||||
$self->addAuthRoute( renewsession => 'askRenew', ['GET'] );
|
||||
$self->addAuthRoute( renewsession => 'confirmRenew', ['POST'] );
|
||||
}
|
||||
|
||||
sub askUpgrade {
|
||||
my ( $self, $req ) = @_;
|
||||
$self->ask( $req, '/upgradesession', 'askToUpgrade', 'upgradeSession' );
|
||||
}
|
||||
|
||||
sub askRenew {
|
||||
my ( $self, $req ) = @_;
|
||||
$self->ask( $req, '/renewsession', 'askToRenew', 'renewSession' );
|
||||
}
|
||||
|
||||
sub confirmUpgrade {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
# sfOnlyUpgrade feature can only be used during session renew
|
||||
return $self->confirm( $req, $self->conf->{sfOnlyUpgrade} );
|
||||
}
|
||||
|
||||
sub confirmRenew {
|
||||
my ( $self, $req ) = @_;
|
||||
return $self->confirm($req);
|
||||
}
|
||||
|
||||
# RUNNING METHOD
|
||||
|
||||
sub ask {
|
||||
my ( $self, $req ) = @_;
|
||||
my ( $self, $req, $url, $message, $buttonlabel ) = @_;
|
||||
|
||||
# Check if auth is already running
|
||||
if ( $req->param('upgrading') or $req->param('kerberos') ) {
|
||||
|
@ -53,19 +77,21 @@ sub ask {
|
|||
$req,
|
||||
'upgradesession',
|
||||
params => {
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
MSG => 'askToUpgrade',
|
||||
CONFIRMKEY => $self->p->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->param('url'),
|
||||
MAIN_LOGO => $self->conf->{portalMainLogo},
|
||||
LANGS => $self->conf->{showLanguages},
|
||||
FORMACTION => $url,
|
||||
PORTALBUTTON => 1,
|
||||
MSG => $message,
|
||||
BUTTON => $buttonlabel,
|
||||
CONFIRMKEY => $self->p->stamp,
|
||||
PORTAL => $self->conf->{portal},
|
||||
URL => $req->param('url'),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub confirm {
|
||||
my ( $self, $req ) = @_;
|
||||
|
||||
my ( $self, $req, $sfOnly ) = @_;
|
||||
my $upg;
|
||||
|
||||
if ( $req->param('kerberos') ) {
|
||||
|
@ -86,12 +112,31 @@ sub confirm {
|
|||
return $self->p->do( $req, [ sub { $res } ] ) if ($res);
|
||||
if ( $upg or $req->param('confirm') == 1 ) {
|
||||
$req->data->{noerror} = 1;
|
||||
$self->p->setHiddenFormValue(
|
||||
$req,
|
||||
upgrading => $self->ott->createToken,
|
||||
'', 0
|
||||
); # Insert token
|
||||
return $self->p->login($req);
|
||||
|
||||
if ($sfOnly) {
|
||||
|
||||
$req->data->{doingSfUpgrade} = 1;
|
||||
|
||||
# Short circuit the first part of login, only do a 2FA step
|
||||
return $self->p->do(
|
||||
$req,
|
||||
[
|
||||
'importHandlerData', 'secondFactor',
|
||||
@{ $self->p->afterData }, $self->p->validSession,
|
||||
@{ $self->p->endAuth },
|
||||
]
|
||||
);
|
||||
}
|
||||
else {
|
||||
$self->p->setHiddenFormValue(
|
||||
$req,
|
||||
upgrading => $self->ott->createToken,
|
||||
'', 0
|
||||
); # Insert token
|
||||
# Do a regular login
|
||||
# Do a regular login
|
||||
return $self->p->login($req);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Go to portal
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"قبول",
|
||||
"accessDenied":"ليس لديك إذن بالدخول لهذا التطبيق",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"تم إصدار طلب تسجيل لهذا الحساب من قبل",
|
||||
"rememberChoice":"تذكر اختياري",
|
||||
"removeOtherSessions":"إزالة الجلسات الأخرى",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"هل تريد إعادة إرسال رسالة التأكيد؟",
|
||||
"resentConfirm":"هل تريد إعادة إرسال رسالة التأكيد؟",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"Dieser Dienst benötigt Zwei-Faktor-Authentifizierung. Bitte legen Sie ein Gerät an und gehen dann zum Portal zurück.",
|
||||
"accept":"Akzeptieren",
|
||||
"accessDenied":"Sie haben keine Zugriffsberechtigung für diese Anwendung",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Eine Registrierungsanforderung für dieses Konto wurde bereits gestellt am",
|
||||
"rememberChoice":"Meine Auswahl merken",
|
||||
"removeOtherSessions":"Andere Sitzungen löschen",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Bestätigungsmail erneuert senden ?",
|
||||
"resentConfirm":"Möchtest du, dass die Bestätigungsmail erneut gesendet wird ?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Accept",
|
||||
"accessDenied":"You have no access authorization for this application",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Remember my choice",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Resend confirmation mail?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Por favor, seleccione su nuevo certificado",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"Este servicio necesita la autenticación de dos factores. Registre un dispositivo ahora, luego reingrese al portal.",
|
||||
"accept":"Aceptar",
|
||||
"accessDenied":"No está autorizado a acceder a esta aplicación",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Ya fue expedida una solicitud de registro para esta cuenta",
|
||||
"rememberChoice":"Recordar mi elección",
|
||||
"removeOtherSessions":"Suprimir las otras sesiones",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"¿Reenviar e-mail de confirmación?",
|
||||
"resentConfirm":"¿Desea que el e-mail de confirmación sea reenviado?",
|
||||
"resetCertificateOK":"Su certificado ha sido reiniciado con éxito",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Conozca su perfil",
|
||||
"yourTotpKey":"Su llave TOTP",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Hyväksy",
|
||||
"accessDenied":"Sinulla ei ole käyttöoikeutta tähän sovellukseen",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Muista valintani",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Uudelleen lähetä vahvistus sähköposti?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Veuillez sélectionner votre nouveau certificat",
|
||||
"PE100":"Le mot de passe contient un caractère interdit",
|
||||
"PE101":"Le mot de passe contient des caractères interdits",
|
||||
"PE102":"Mise à niveau de la session",
|
||||
"PE103":"Aucun second facteur disponible pour votre compte",
|
||||
"2fRegRequired":"Ce service requiert une authentification à deux facteurs. Enregistrez un équipement ici et retournez au portail.",
|
||||
"accept":"Accepter",
|
||||
"accessDenied":"Vous n'avez pas les droits d'accès à cette application",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Une demande de création pour ce compte a déjà été faite le ",
|
||||
"rememberChoice":"Se souvenir de mon choix",
|
||||
"removeOtherSessions":"Fermer les autres sessions",
|
||||
"renewSession":"Renouveller la session",
|
||||
"resendConfirmMail":"Renvoyer le mail de confirmation ?",
|
||||
"resentConfirm":"Voulez-vous que le message de confirmation soit renvoyé ?",
|
||||
"resetCertificateOK":"Votre certificat a bien été réinitialisé!",
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"Questo servizio richiede un'autenticazione a doppio fattore. Registrare un dispositivo ora, quindi tornare al portale.",
|
||||
"accept":"Accetta",
|
||||
"accessDenied":"Non hai un'autorizzazione di accesso per questa applicazione",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Una richiesta di registrazione per questo conto é già stata rilasciata il",
|
||||
"rememberChoice":"Ricordarsi della mia scelta",
|
||||
"removeOtherSessions":"Rimuovere altre sessioni",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Inviare nuovamente mail di conferma?",
|
||||
"resentConfirm":"Vuoi inviare di nuovo la mail di conferma?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"La tua chiave TOTP",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Accept",
|
||||
"accessDenied":"You have no access authorization for this application",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Remember my choice",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Resend confirmation mail?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Wybierz nowy certyfikat",
|
||||
"PE100":"Hasło zawiera niedozwolony znak",
|
||||
"PE101":"Hasło zawiera niedozwolone znaki",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"Ta usługa wymaga podwójnego uwierzytelnienia. Zarejestruj urządzenie 2ndFA teraz, a następnie wróć do portalu.",
|
||||
"accept":"Akceptuj",
|
||||
"accessDenied":"Nie masz dostępu do tej aplikacji",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Wniosek o rejestrację tego konta został już złożony w dniu ",
|
||||
"rememberChoice":"Zapamiętaj mój wybór",
|
||||
"removeOtherSessions":"Usuń inne sesje",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Czy wysłać ponownie wiadomość z potwierdzeniem?",
|
||||
"resentConfirm":"Czy chcesz, aby wiadomość z potwierdzeniem została ponownie wysłana?",
|
||||
"resetCertificateOK":"Twój certyfikat został pomyślnie zresetowany!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Twój profil",
|
||||
"yourTotpKey":"Twój klucz TOTP",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Accept",
|
||||
"accessDenied":"You have no access authorization for this application",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Remember my choice",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Resend confirmation mail?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Accept",
|
||||
"accessDenied":"You have no access authorization for this application",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"A register request for this account was already issued on ",
|
||||
"rememberChoice":"Remember my choice",
|
||||
"removeOtherSessions":"Remove other sessions",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Resend confirmation mail?",
|
||||
"resentConfirm":"Do you want the confirmation mail to be resent?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Lütfen yeni sertifikanızı seçin",
|
||||
"PE100":"Parola izin verilmeyen karakter içeriyor",
|
||||
"PE101":"Parola izin verilmeyen karakterler içeriyor",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"Bu servis iki adımlı kimlik doğrulama gerektiriyor. Şimdi bir cihaz ekleyin ve ardından portala geri dönün",
|
||||
"accept":"Kabul Et",
|
||||
"accessDenied":"Bu uygulamaya erişim yetkiniz yok",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Bu hesap için kayıt olma isteği zaten şu tarihte alındı:",
|
||||
"rememberChoice":"Seçimimi hatırla",
|
||||
"removeOtherSessions":"Diğer oturumları sil",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Doğrulama e-postasını tekrar gönder?",
|
||||
"resentConfirm":"Onay e-postasının tekrar gönderilmesini ister misiniz?",
|
||||
"resetCertificateOK":"Sertifikanız başarıyla sıfırlandı!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Profilini bil",
|
||||
"yourTotpKey":"TOTP anahtarınız",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Chấp nhận",
|
||||
"accessDenied":"Bạn không có quyền truy cập vào ứng dụng này",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"Yêu cầu đăng ký cho tài khoản này đã được cấp phát",
|
||||
"rememberChoice":"Hãy nhớ sự lựa chọn của tôi",
|
||||
"removeOtherSessions":"Xóa các phiên khác",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"Gửi lại thư xác nhận?",
|
||||
"resentConfirm":"Bạn có muốn gửi lại thư xác nhận không?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
"PE99":"Please select your new certificate",
|
||||
"PE100":"Password contains not allowed character",
|
||||
"PE101":"Password contains not allowed characters",
|
||||
"PE102":"Session must be upgraded",
|
||||
"PE103":"No second factors available for your account",
|
||||
"2fRegRequired":"This service requires a double factor authentication. Register a device now, then go back to the portal.",
|
||||
"accept":"Accept 方法",
|
||||
"accessDenied":"您无权访问此应用",
|
||||
|
@ -249,6 +251,7 @@
|
|||
"registerRequestAlreadyIssued":"此账户已存在一个注册请求",
|
||||
"rememberChoice":"记住我的选择",
|
||||
"removeOtherSessions":"移除其他会话",
|
||||
"renewSession":"Renew session",
|
||||
"resendConfirmMail":"重新发送确认邮件?",
|
||||
"resentConfirm":"您想确认邮件被重新发送吗?",
|
||||
"resetCertificateOK":"Your certificate has been successfully reset!",
|
||||
|
@ -315,4 +318,4 @@
|
|||
"yourProfile":"Know your profile",
|
||||
"yourTotpKey":"Your TOTP key",
|
||||
"yubikey2f":"Yubikey"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<TMPL_INCLUDE NAME="header.tpl">
|
||||
|
||||
<div id="errorcontent" class="container">
|
||||
|
||||
<div class="message message-positive alert"><span trspan="<TMPL_VAR NAME="MSG">"></span></div>
|
||||
|
||||
<form id="upgrd" action="/upgradesession" method="post" class="password" role="form">
|
||||
<input type="hidden" name="confirm" value="<TMPL_VAR NAME="CONFIRMKEY">">
|
||||
<input type="hidden" name="url" value="<TMPL_VAR NAME="URL">">
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<span class="fa fa-sign-in"></span>
|
||||
<span trspan="upgradeSession">Upgrade session</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<TMPL_INCLUDE NAME="footer.tpl">
|
|
@ -4,18 +4,20 @@
|
|||
|
||||
<div class="message message-positive alert"><span trspan="<TMPL_VAR NAME="MSG">"></span></div>
|
||||
|
||||
<form id="upgrd" action="/upgradesession" method="post" class="password" role="form">
|
||||
<form id="upgrd" action="<TMPL_VAR NAME="FORMACTION">" method="post" class="password" role="form">
|
||||
<input type="hidden" name="confirm" value="<TMPL_VAR NAME="CONFIRMKEY">">
|
||||
<input type="hidden" name="url" value="<TMPL_VAR NAME="URL">">
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<span class="fa fa-sign-in"></span>
|
||||
<span trspan="upgradeSession">Upgrade session</span>
|
||||
<span trspan="<TMPL_VAR NAME="BUTTON">">Upgrade session</span>
|
||||
</button>
|
||||
<TMPL_IF NAME="PORTALBUTTON">
|
||||
<a href="<TMPL_VAR NAME="PORTAL_URL">" class="btn btn-primary" role="button">
|
||||
<span class="fa fa-home"></span>
|
||||
<span trspan="goToPortal">Go to portal</span>
|
||||
</a>
|
||||
</TMPL_IF>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
381
lemonldap-ng-portal/t/30-SAML-POST-with-2F-UpgradeOnly.t
Normal file
381
lemonldap-ng-portal/t/30-SAML-POST-with-2F-UpgradeOnly.t
Normal file
|
@ -0,0 +1,381 @@
|
|||
use lib 'inc';
|
||||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
use MIME::Base64;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
require 't/saml-lib.pm';
|
||||
require 't/smtp.pm';
|
||||
}
|
||||
|
||||
my $maintests = 20;
|
||||
my $debug = 'error';
|
||||
my ( $issuer, $sp, $res );
|
||||
|
||||
# Redefine LWP methods for tests
|
||||
LWP::Protocol::PSGI->register(
|
||||
sub {
|
||||
my $req = Plack::Request->new(@_);
|
||||
fail('POST should not launch SOAP requests');
|
||||
count(1);
|
||||
return [ 500, [], [] ];
|
||||
}
|
||||
);
|
||||
|
||||
SKIP: {
|
||||
eval "use Lasso";
|
||||
if ($@) {
|
||||
skip 'Lasso not found', $maintests;
|
||||
}
|
||||
|
||||
# Initialization
|
||||
$issuer = register( 'issuer', \&issuer );
|
||||
$sp = register( 'sp', \&sp );
|
||||
|
||||
## FIRST CASE##
|
||||
# * Login directly to SP, 2FA is asked
|
||||
|
||||
# Simple SP access
|
||||
ok(
|
||||
$res = $sp->_get(
|
||||
'/', accept => 'text/html',
|
||||
),
|
||||
'Unauth SP request'
|
||||
);
|
||||
expectOK($res);
|
||||
my ( $host, $url, $s ) =
|
||||
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
|
||||
'SAMLRequest' );
|
||||
|
||||
# Push SAML request to IdP
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
$url,
|
||||
IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
length => length($s)
|
||||
),
|
||||
'Post SAML request to IdP'
|
||||
);
|
||||
expectOK($res);
|
||||
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# Try to authenticate with an authorized user to IdP
|
||||
$s = "user=dwho&password=dwho&$s";
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
$url,
|
||||
IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
cookie => $pdata,
|
||||
length => length($s),
|
||||
),
|
||||
'Post authentication'
|
||||
);
|
||||
|
||||
( $host, $url, $s ) =
|
||||
expectForm( $res, undef, '/mail2fcheck?skin=bootstrap', 'token', 'code' );
|
||||
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
ok( mail() =~ m%<b>(\d{4})</b>%, 'Found 2F code in mail' )
|
||||
or print STDERR Dumper( mail() );
|
||||
|
||||
my $code = $1;
|
||||
|
||||
$s =~ s/code=/code=${code}/;
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/mail2fcheck',
|
||||
IO::String->new($s),
|
||||
length => length($s),
|
||||
cookie => $pdata,
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
|
||||
my $idpId = expectCookie($res);
|
||||
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
expectRedirection( $res, 'http://auth.idp.com/saml' );
|
||||
|
||||
ok(
|
||||
$res = $issuer->_get(
|
||||
'/saml',
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Follow redirection'
|
||||
);
|
||||
|
||||
# Expect pdata to be cleared
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' );
|
||||
|
||||
( $host, $url, $s ) =
|
||||
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
|
||||
'SAMLResponse' );
|
||||
|
||||
# Post SAML response to SP
|
||||
switch ('sp');
|
||||
ok(
|
||||
$res = $sp->_post(
|
||||
$url, IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
length => length($s),
|
||||
),
|
||||
'Post SAML response to SP'
|
||||
);
|
||||
|
||||
# Verify authentication on SP
|
||||
expectRedirection( $res, 'http://auth.sp.com' );
|
||||
my $spId = expectCookie($res);
|
||||
|
||||
ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' );
|
||||
expectOK($res);
|
||||
expectAuthenticatedAs( $res, 'dwho@badwolf.org@idp' );
|
||||
|
||||
## SECOND CASE##
|
||||
# * Login to IDP without 2FA
|
||||
# * Login to SP, 2FA is asked to upgrade session
|
||||
|
||||
# Login to IDP
|
||||
$s = "user=dwho&password=dwho";
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
"/",
|
||||
IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
length => length($s),
|
||||
),
|
||||
'Post authentication'
|
||||
);
|
||||
|
||||
# No 2FA asked
|
||||
$idpId = expectCookie($res);
|
||||
|
||||
# Simple SP access
|
||||
ok(
|
||||
$res = $sp->_get(
|
||||
'/', accept => 'text/html',
|
||||
),
|
||||
'Unauth SP request'
|
||||
);
|
||||
expectOK($res);
|
||||
my ( $host, $url, $s ) =
|
||||
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
|
||||
'SAMLRequest' );
|
||||
|
||||
# Push SAML request to IdP
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
$url,
|
||||
IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId",
|
||||
length => length($s)
|
||||
),
|
||||
'Post SAML request to IdP'
|
||||
);
|
||||
expectOK($res);
|
||||
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# IDP should offer to upgrade the session
|
||||
( $host, $url, $s ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm', 'url' );
|
||||
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($s),
|
||||
length => length($s),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
( $host, $url, $s ) =
|
||||
expectForm( $res, undef, '/mail2fcheck?skin=bootstrap', 'token', 'code' );
|
||||
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
|
||||
ok( mail() =~ m%<b>(\d{4})</b>%, 'Found 2F code in mail' )
|
||||
or print STDERR Dumper( mail() );
|
||||
|
||||
my $code = $1;
|
||||
|
||||
$s =~ s/code=/code=${code}/;
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/mail2fcheck',
|
||||
IO::String->new($s),
|
||||
length => length($s),
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
|
||||
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
expectRedirection( $res, 'http://auth.idp.com/saml/singleSignOn' );
|
||||
|
||||
ok(
|
||||
$res = $issuer->_get(
|
||||
'/saml',
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Follow redirection'
|
||||
);
|
||||
|
||||
# Expect pdata to be cleared
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' );
|
||||
|
||||
( $host, $url, $s ) =
|
||||
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
|
||||
'SAMLResponse' );
|
||||
|
||||
# Post SAML response to SP
|
||||
switch ('sp');
|
||||
ok(
|
||||
$res = $sp->_post(
|
||||
$url, IO::String->new($s),
|
||||
accept => 'text/html',
|
||||
length => length($s),
|
||||
),
|
||||
'Post SAML response to SP'
|
||||
);
|
||||
|
||||
# Verify authentication on SP
|
||||
expectRedirection( $res, 'http://auth.sp.com' );
|
||||
my $spId = expectCookie($res);
|
||||
|
||||
ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' );
|
||||
expectOK($res);
|
||||
expectAuthenticatedAs( $res, 'dwho@badwolf.org@idp' );
|
||||
|
||||
}
|
||||
|
||||
count($maintests);
|
||||
clean_sessions();
|
||||
done_testing( count() );
|
||||
|
||||
sub issuer {
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'idp.com',
|
||||
portal => 'http://auth.idp.com',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
sfOnlyUpgrade => 1,
|
||||
issuerDBSAMLActivation => 1,
|
||||
mail2fActivation => 1,
|
||||
mail2fCodeRegex => '\d{4}',
|
||||
mail2fAuthnLevel => 5,
|
||||
samlSPMetaDataOptions => {
|
||||
'sp.com' => {
|
||||
samlSPMetaDataOptionsEncryptionMode => 'none',
|
||||
samlSPMetaDataOptionsSignSSOMessage => 1,
|
||||
samlSPMetaDataOptionsSignSLOMessage => 1,
|
||||
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
|
||||
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
|
||||
samlSPMetaDataOptionsAuthnLevel => 4,
|
||||
}
|
||||
},
|
||||
samlSPMetaDataExportedAttributes => {
|
||||
'sp.com' => {
|
||||
cn =>
|
||||
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||
uid =>
|
||||
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
|
||||
}
|
||||
},
|
||||
samlOrganizationDisplayName => "IDP",
|
||||
samlOrganizationName => "IDP",
|
||||
samlOrganizationURL => "http://www.idp.com/",
|
||||
samlServicePrivateKeyEnc => saml_key_idp_private_enc,
|
||||
samlServicePrivateKeySig => saml_key_idp_private_sig,
|
||||
samlServicePublicKeyEnc => saml_key_idp_public_enc,
|
||||
samlServicePublicKeySig => saml_key_idp_public_sig,
|
||||
samlSPMetaDataXML => {
|
||||
"sp.com" => {
|
||||
samlSPMetaDataXML =>
|
||||
samlSPMetaDataXML( 'sp', 'HTTP-POST' )
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub sp {
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'sp.com',
|
||||
portal => 'http://auth.sp.com',
|
||||
authentication => 'SAML',
|
||||
userDB => 'Same',
|
||||
issuerDBSAMLActivation => 0,
|
||||
restSessionServer => 1,
|
||||
samlIDPMetaDataExportedAttributes => {
|
||||
idp => {
|
||||
mail => "0;mail;;",
|
||||
uid => "1;uid",
|
||||
cn => "0;cn"
|
||||
}
|
||||
},
|
||||
samlIDPMetaDataOptions => {
|
||||
idp => {
|
||||
samlIDPMetaDataOptionsEncryptionMode => 'none',
|
||||
samlIDPMetaDataOptionsSSOBinding => 'post',
|
||||
samlIDPMetaDataOptionsSLOBinding => 'post',
|
||||
samlIDPMetaDataOptionsSignSSOMessage => 1,
|
||||
samlIDPMetaDataOptionsSignSLOMessage => 1,
|
||||
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
|
||||
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
|
||||
samlIDPMetaDataOptionsForceUTF8 => 1,
|
||||
}
|
||||
},
|
||||
samlIDPMetaDataExportedAttributes => {
|
||||
idp => {
|
||||
"uid" => "0;uid;;",
|
||||
"cn" => "1;cn;;",
|
||||
},
|
||||
},
|
||||
samlIDPMetaDataXML => {
|
||||
idp => {
|
||||
samlIDPMetaDataXML =>
|
||||
samlIDPMetaDataXML( 'idp', 'HTTP-POST' )
|
||||
}
|
||||
},
|
||||
samlOrganizationDisplayName => "SP",
|
||||
samlOrganizationName => "SP",
|
||||
samlOrganizationURL => "http://www.sp.com",
|
||||
samlServicePublicKeySig => saml_key_sp_public_sig,
|
||||
samlServicePrivateKeyEnc => saml_key_sp_private_enc,
|
||||
samlServicePrivateKeySig => saml_key_sp_private_sig,
|
||||
samlServicePublicKeyEnc => saml_key_sp_public_enc,
|
||||
samlSPSSODescriptorAuthnRequestsSigned => 1,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
|
@ -88,11 +88,11 @@ SKIP: {
|
|||
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
my $tmp;
|
||||
( $host, $tmp, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm' );
|
||||
expectForm( $res, undef, '/renewsession', 'confirm' );
|
||||
ok( $res->[2]->[0] =~ /trspan="askToRenew"/, 'Propose to renew session' );
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession',
|
||||
'/renewsession',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
|
@ -110,7 +110,7 @@ SKIP: {
|
|||
$query .= '&user=dwho&password=dwho';
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession',
|
||||
'/renewsession',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
|
|
|
@ -77,11 +77,11 @@ SKIP: {
|
|||
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
|
||||
my $tmp;
|
||||
( $host, $tmp, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm' );
|
||||
expectForm( $res, undef, '/renewsession', 'confirm' );
|
||||
ok( $res->[2]->[0] =~ /trspan="askToRenew"/, 'Propose to renew session' );
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession',
|
||||
'/renewsession',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
|
@ -99,7 +99,7 @@ SKIP: {
|
|||
$query .= '&user=russian&password=russian';
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession',
|
||||
'/renewsession',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
|
|
|
@ -185,13 +185,13 @@ SKIP: {
|
|||
# Verify that confirmation is asked
|
||||
my ( $host, $url );
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm', 'url' );
|
||||
expectForm( $res, undef, '/renewsession', 'confirm', 'url' );
|
||||
|
||||
# Verify that autopost is required (skipRenewConfirmation is set to 1)
|
||||
ok( $res->[2]->[0] =~ /autoRenew\.(?:min\.)js/m, ' Get autorenew.js' );
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession', IO::String->new($query),
|
||||
'/renewsession', IO::String->new($query),
|
||||
length => length($query),
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
accept => 'text/html'
|
||||
|
@ -206,7 +206,7 @@ SKIP: {
|
|||
$query .= '&password=dwho';
|
||||
ok(
|
||||
$res = $issuer->_post(
|
||||
'/upgradesession', IO::String->new($query),
|
||||
'/renewsession', IO::String->new($query),
|
||||
length => length($query),
|
||||
cookie => "lemonldap=$idpId; $pdata",
|
||||
accept => 'text/html'
|
||||
|
|
427
lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F-UpgradeOnly.t
Normal file
427
lemonldap-ng-portal/t/32-OIDC-Code-Flow-with-2F-UpgradeOnly.t
Normal file
|
@ -0,0 +1,427 @@
|
|||
use lib 'inc';
|
||||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use LWP::UserAgent;
|
||||
use LWP::Protocol::PSGI;
|
||||
use MIME::Base64;
|
||||
|
||||
BEGIN {
|
||||
require 't/test-lib.pm';
|
||||
require 't/oidc-lib.pm';
|
||||
}
|
||||
|
||||
my $debug = 'error';
|
||||
my ( $op, $rp, $res );
|
||||
|
||||
my $access_token;
|
||||
|
||||
LWP::Protocol::PSGI->register(
|
||||
sub {
|
||||
my $req = Plack::Request->new(@_);
|
||||
ok( $req->uri =~ m#http://auth.((?:o|r)p).com(.*)#, ' REST request' );
|
||||
my $host = $1;
|
||||
my $url = $2;
|
||||
my ( $res, $client );
|
||||
count(1);
|
||||
if ( $host eq 'op' ) {
|
||||
pass(" Request from RP to OP, endpoint $url");
|
||||
$client = $op;
|
||||
}
|
||||
elsif ( $host eq 'rp' ) {
|
||||
pass(' Request from OP to RP');
|
||||
$client = $rp;
|
||||
}
|
||||
else {
|
||||
fail(' Aborting REST request (external)');
|
||||
return [ 500, [], [] ];
|
||||
}
|
||||
if ( $req->method =~ /^post$/i ) {
|
||||
my $s = $req->content;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
$url, IO::String->new($s),
|
||||
length => length($s),
|
||||
type => $req->header('Content-Type'),
|
||||
),
|
||||
' Execute request'
|
||||
);
|
||||
}
|
||||
else {
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
$url,
|
||||
custom => {
|
||||
HTTP_AUTHORIZATION => $req->header('Authorization'),
|
||||
}
|
||||
),
|
||||
' Execute request'
|
||||
);
|
||||
}
|
||||
ok( $res->[0] == 200, ' Response is 200' );
|
||||
ok( getHeader( $res, 'Content-Type' ) =~ m#^application/json#,
|
||||
' Content is JSON' )
|
||||
or explain( $res->[1], 'Content-Type => application/json' );
|
||||
count(4);
|
||||
if ( $res->[2]->[0] =~ /"access_token":"(.*?)"/ ) {
|
||||
$access_token = $1;
|
||||
pass "Found access_token $access_token";
|
||||
count(1);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
);
|
||||
|
||||
# Initialization
|
||||
ok( $op = op(), 'OP portal' );
|
||||
|
||||
ok( $res = $op->_get('/oauth2/jwks'), 'Get JWKS, endpoint /oauth2/jwks' );
|
||||
expectOK($res);
|
||||
my $jwks = $res->[2]->[0];
|
||||
|
||||
ok(
|
||||
$res = $op->_get('/.well-known/openid-configuration'),
|
||||
'Get metadata, endpoint /.well-known/openid-configuration'
|
||||
);
|
||||
expectOK($res);
|
||||
my $metadata = $res->[2]->[0];
|
||||
count(3);
|
||||
|
||||
###### FIRST TEST #####################
|
||||
# Authenticate to OP at low level, then
|
||||
# upgrade with 2FA
|
||||
|
||||
# Authenticate to OP, 2FA not required
|
||||
my $query = "user=french&password=french";
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
'/',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
),
|
||||
"Post authentication with no target auth level",
|
||||
);
|
||||
count(1);
|
||||
my $idpId = expectCookie($res);
|
||||
|
||||
switch ('rp');
|
||||
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
|
||||
ok( $rp = rp( $jwks, $metadata ), 'RP portal' );
|
||||
count(1);
|
||||
|
||||
# Query RP for auth
|
||||
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' );
|
||||
count(1);
|
||||
( my $url, $query ) =
|
||||
expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# );
|
||||
|
||||
# Push request to OP
|
||||
switch ('op');
|
||||
ok(
|
||||
$res = $op->_get(
|
||||
$url,
|
||||
query => $query,
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId",
|
||||
),
|
||||
"Push request to OP, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
expectOK($res);
|
||||
my $pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# OP should offer to upgrade the session
|
||||
( my $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm', 'url' );
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId;lemonldappdata=$pdata",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# Prompt for 2FA
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/ext2fcheck?skin=bootstrap', 'token', 'code',
|
||||
'checkLogins' );
|
||||
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$query =~ s/code=/code=123456/;
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
'/ext2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId;lemonldappdata=$pdata",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
($url) = expectRedirection( $res, qr#http://auth.op.com(/?/oauth2.*)# );
|
||||
|
||||
ok(
|
||||
$res = $op->_get(
|
||||
"$url",
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId; lemonldappdata=$pdata",
|
||||
),
|
||||
"Follow redirection to Oauth2 issuer"
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
is( $pdata, '', "Pdata was cleared" );
|
||||
count(1);
|
||||
|
||||
( $host, my $tmp );
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, qr#/oauth2/authorize.*#, 'confirm' );
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
'/oauth2/authorize',
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId",
|
||||
length => length($query),
|
||||
),
|
||||
"Post confirmation, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
|
||||
($query) = expectRedirection( $res, qr#^http://auth.rp.com/?\?(.*)$# );
|
||||
|
||||
# Push OP response to RP
|
||||
switch ('rp');
|
||||
|
||||
ok( $res = $rp->_get( '/', query => $query, accept => 'text/html' ),
|
||||
'Call openidconnectcallback on RP' );
|
||||
count(1);
|
||||
my $spId = expectCookie($res);
|
||||
|
||||
ok( $res = $rp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' );
|
||||
count(1);
|
||||
expectOK($res);
|
||||
expectAuthenticatedAs( $res, 'french' );
|
||||
|
||||
###### SECOND TEST #####################
|
||||
# Authenticate to secure RP immediately
|
||||
|
||||
# Query RP for auth
|
||||
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' );
|
||||
count(1);
|
||||
( $url, $query ) =
|
||||
expectRedirection( $res, qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# );
|
||||
|
||||
# Push request to OP
|
||||
switch ('op');
|
||||
ok( $res = $op->_get( $url, query => $query, accept => 'text/html' ),
|
||||
"Push request to OP, endpoint $url" );
|
||||
count(1);
|
||||
expectOK($res);
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# Try to authenticate to OP
|
||||
$query = "user=french&password=french&$query";
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
$url,
|
||||
IO::String->new($query),
|
||||
accept => 'text/html',
|
||||
length => length($query),
|
||||
cookie => "lemonldappdata=$pdata",
|
||||
),
|
||||
"Post authentication, endpoint $url"
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/ext2fcheck?skin=bootstrap', 'token', 'code',
|
||||
'checkLogins' );
|
||||
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
$query =~ s/code=/code=123456/;
|
||||
|
||||
ok(
|
||||
$res = $op->_post(
|
||||
'/ext2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldappdata=$pdata",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$idpId = expectCookie($res);
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
($url) = expectRedirection( $res, qr#http://auth.op.com(/?/oauth2)# );
|
||||
|
||||
ok(
|
||||
$res = $op->_get(
|
||||
"$url",
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$idpId; lemonldappdata=$pdata",
|
||||
),
|
||||
"Follow redirection to Oauth2 issuer"
|
||||
);
|
||||
count(1);
|
||||
|
||||
$pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
is( $pdata, '', "Pdata was cleared" );
|
||||
count(1);
|
||||
|
||||
($query) = expectRedirection( $res, qr#^http://auth.rp.com/?\?(.*)$# );
|
||||
|
||||
# Push OP response to RP
|
||||
switch ('rp');
|
||||
|
||||
ok( $res = $rp->_get( '/', query => $query, accept => 'text/html' ),
|
||||
'Call openidconnectcallback on RP' );
|
||||
count(1);
|
||||
$spId = expectCookie($res);
|
||||
|
||||
ok( $res = $rp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' );
|
||||
count(1);
|
||||
expectOK($res);
|
||||
expectAuthenticatedAs( $res, 'french' );
|
||||
|
||||
clean_sessions();
|
||||
done_testing( count() );
|
||||
|
||||
sub op {
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'idp.com',
|
||||
portal => 'http://auth.op.com/',
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
issuerDBOpenIDConnectActivation => 1,
|
||||
sfOnlyUpgrade => 1,
|
||||
ext2fActivation => 1,
|
||||
ext2fAuthnLevel => 5,
|
||||
ext2fCodeActivation => '123456',
|
||||
ext2FSendCommand => 't/sendOTP.pl -uid dwho',
|
||||
ext2FValidateCommand => 't/vrfyOTP.pl -uid dwho -code $code',
|
||||
restSessionServer => 1,
|
||||
oidcRPMetaDataExportedVars => {
|
||||
rp => {
|
||||
email => "mail",
|
||||
family_name => "cn",
|
||||
name => "cn"
|
||||
}
|
||||
},
|
||||
oidcServiceMetaDataAuthorizeURI => "authorize",
|
||||
oidcServiceMetaDataCheckSessionURI => "checksession.html",
|
||||
oidcServiceMetaDataJWKSURI => "jwks",
|
||||
oidcServiceMetaDataEndSessionURI => "logout",
|
||||
oidcServiceMetaDataRegistrationURI => "register",
|
||||
oidcServiceMetaDataTokenURI => "token",
|
||||
oidcServiceMetaDataUserInfoURI => "userinfo",
|
||||
oidcServiceAllowHybridFlow => 1,
|
||||
oidcServiceAllowImplicitFlow => 1,
|
||||
oidcServiceAllowDynamicRegistration => 1,
|
||||
oidcServiceAllowAuthorizationCodeFlow => 1,
|
||||
oidcRPMetaDataOptions => {
|
||||
rp => {
|
||||
oidcRPMetaDataOptionsDisplayName => "RP",
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsClientID => "rpid",
|
||||
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
|
||||
oidcRPMetaDataOptionsBypassConsent => 0,
|
||||
oidcRPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcRPMetaDataOptionsUserIDAttr => "",
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsAuthnLevel => 5,
|
||||
oidcRPMetaDataOptionsPostLogoutRedirectUris =>
|
||||
"http://auth.rp.com/?logout=1"
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataOptions => {},
|
||||
oidcOPMetaDataJSON => {},
|
||||
oidcOPMetaDataJWKS => {},
|
||||
oidcServiceMetaDataAuthnContext => {
|
||||
'loa-4' => 4,
|
||||
'loa-1' => 1,
|
||||
'loa-5' => 5,
|
||||
'loa-2' => 2,
|
||||
'loa-3' => 3
|
||||
},
|
||||
oidcServicePrivateKeySig => oidc_key_op_private_sig,
|
||||
oidcServicePublicKeySig => oidc_key_op_public_sig,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub rp {
|
||||
my ( $jwks, $metadata ) = @_;
|
||||
return LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => $debug,
|
||||
domain => 'rp.com',
|
||||
portal => 'http://auth.rp.com/',
|
||||
authentication => 'OpenIDConnect',
|
||||
userDB => 'Same',
|
||||
restSessionServer => 1,
|
||||
oidcOPMetaDataExportedVars => {
|
||||
op => {
|
||||
cn => "name",
|
||||
uid => "sub",
|
||||
sn => "family_name",
|
||||
mail => "email"
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataOptions => {
|
||||
op => {
|
||||
oidcOPMetaDataOptionsCheckJWTSignature => 1,
|
||||
oidcOPMetaDataOptionsJWKSTimeout => 0,
|
||||
oidcOPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcOPMetaDataOptionsScope => "openid profile",
|
||||
oidcOPMetaDataOptionsStoreIDToken => 0,
|
||||
oidcOPMetaDataOptionsMaxAge => 30,
|
||||
oidcOPMetaDataOptionsDisplay => "",
|
||||
oidcOPMetaDataOptionsClientID => "rpid",
|
||||
oidcOPMetaDataOptionsConfigurationURI =>
|
||||
"https://auth.op.com/.well-known/openid-configuration"
|
||||
}
|
||||
},
|
||||
oidcOPMetaDataJWKS => {
|
||||
op => $jwks,
|
||||
},
|
||||
oidcOPMetaDataJSON => {
|
||||
op => $metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
177
lemonldap-ng-portal/t/78-2F-UpgradeOnly.t
Normal file
177
lemonldap-ng-portal/t/78-2F-UpgradeOnly.t
Normal file
|
@ -0,0 +1,177 @@
|
|||
use Test::More;
|
||||
use strict;
|
||||
use IO::String;
|
||||
use Data::Dumper;
|
||||
|
||||
require 't/test-lib.pm';
|
||||
require 't/smtp.pm';
|
||||
|
||||
use_ok('Lemonldap::NG::Common::FormEncode');
|
||||
count(1);
|
||||
my $res;
|
||||
|
||||
my $client = LLNG::Manager::Test->new( {
|
||||
ini => {
|
||||
logLevel => 'error',
|
||||
sfOnlyUpgrade => 1,
|
||||
mail2fActivation => '$uid eq "dwho"',
|
||||
mail2fCodeRegex => '\d{4}',
|
||||
mail2fAuthnLevel => 5,
|
||||
authentication => 'Demo',
|
||||
userDB => 'Same',
|
||||
'vhostOptions' => {
|
||||
'test1.example.com' => {
|
||||
'vhostAuthnLevel' => 3
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# CASE 1: no 2F available
|
||||
# -----------------------
|
||||
my $query = 'user=rtyler&password=rtyler';
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
my $id = expectCookie($res);
|
||||
|
||||
# After attempting to access test1,
|
||||
# the handler sends up back to /upgradesession
|
||||
# --------------------------------------------
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/upgradesession',
|
||||
query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t',
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Upgrade session query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
( my $host, my $url, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm', 'url' );
|
||||
|
||||
# Accept session upgrade
|
||||
# ----------------------
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Accept session upgrade query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
my $pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
# A message warns the user that they do not have any 2FA available
|
||||
expectPortalError( $res, 103 );
|
||||
|
||||
# CASE 2: has 2F available
|
||||
# -------------------
|
||||
$query = 'user=dwho&password=dwho';
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
),
|
||||
'Auth query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
$id = expectCookie($res);
|
||||
|
||||
# After attempting to access test1,
|
||||
# the handler sends up back to /upgradesession
|
||||
# --------------------------------------------
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
'/upgradesession',
|
||||
query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29t',
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Upgrade session query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
( my $host, my $url, $query ) =
|
||||
expectForm( $res, undef, '/upgradesession', 'confirm', 'url' );
|
||||
|
||||
# Accept session upgrade
|
||||
# ----------------------
|
||||
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/upgradesession',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id",
|
||||
),
|
||||
'Accept session upgrade query'
|
||||
);
|
||||
count(1);
|
||||
|
||||
my $pdata = expectCookie( $res, 'lemonldappdata' );
|
||||
|
||||
( $host, $url, $query ) =
|
||||
expectForm( $res, undef, '/mail2fcheck?skin=bootstrap', 'token', 'code' );
|
||||
|
||||
ok(
|
||||
$res->[2]->[0] =~
|
||||
qr%<input name="code" value="" type="text" class="form-control" id="extcode" trplaceholder="code" autocomplete="off" />%,
|
||||
'Found EXTCODE input'
|
||||
) or print STDERR Dumper( $res->[2]->[0] );
|
||||
count(1);
|
||||
|
||||
ok( mail() =~ m%<b>(\d{4})</b>%, 'Found 2F code in mail' )
|
||||
or print STDERR Dumper( mail() );
|
||||
count(1);
|
||||
|
||||
my $code = $1;
|
||||
|
||||
# Post 2F code
|
||||
# ------------
|
||||
|
||||
$query =~ s/code=/code=${code}/;
|
||||
ok(
|
||||
$res = $client->_post(
|
||||
'/mail2fcheck',
|
||||
IO::String->new($query),
|
||||
length => length($query),
|
||||
accept => 'text/html',
|
||||
cookie => "lemonldap=$id;lemonldappdata=$pdata",
|
||||
),
|
||||
'Post code'
|
||||
);
|
||||
count(1);
|
||||
expectRedirection( $res, 'http://test1.example.com' );
|
||||
$id = expectCookie($res);
|
||||
|
||||
my $cookies = getCookies($res);
|
||||
ok( !$cookies->{lemonldappdata}, " Make sure no pdata is returned" );
|
||||
count(1);
|
||||
|
||||
clean_sessions();
|
||||
|
||||
done_testing( count() );
|
||||
|
Loading…
Reference in New Issue
Block a user