A bit lost on how tu trigger username-password validation against LDAP in IDPV4

Claude Libois clibois.work at gmail.com
Wed May 5 13:49:15 UTC 2021


The goal of the custom flow is to provide a choice between 2 authentication:
- external one
- authentication against LDAP with provide credentials.
The same webpage is used to trigger either external authentication either
ldap one.
After LDAP/External authentication, we use an http call to a Rest API to
get information about username and so populate saml response through
attribute-resolver mechanism.

To achieve this, we didn't tweak completely the current file but added some
custom flow that are very similar to the existing one:
We got a xxx-particulier-ldap-authn-config.xml and
a xxx-particulier-password-authn-config.xml nearly identical to
the ldap-authn-config.xml and password-authn-config.xml.
We have got a specific spring webflow for this auth flow. This is this flow
which is using the
 <evaluate expression="ValidateUsernamePassword" />  which is mapped to
the   ValidateUsernamePasswordAgainstLDAP in IDP3.
I'm not sure if it was really needed to redefine the whole flow or if we
could have just tweaked the existing one.
Regards,
Claude

Le mer. 5 mai 2021 à 15:45, Claude Libois <clibois.work at gmail.com> a écrit :

> The goal of the custom flow is to provide a choice between 2
> authentication:
> - external one
> - authentication against LDAP with provide credentials.
> The same webpage is used to trigger either external authentication either
> ldap one.
> After LDAP/External authentication, we use an http call to a Rest API to
> get information about username and so populate saml response through
> attribute-resolver mechanism.
>
> To achieve this, we didn't tweak completely the current file but added
> some custom flow that are very similar to the existing one:
> We got a xxx-particulier-ldap-authn-config.xml and
> a xxx-particulier-password-authn-config.xml nearly identical to
> the ldap-authn-config.xml and password-authn-config.xml.
> We have got a specific spring webflow for this auth flow. This is this
> flow which is using the
>  <evaluate expression="ValidateUsernamePassword" />  which is mapped to
> the   ValidateUsernamePasswordAgainstLDAP in IDP3.
> I'm not sure if it was really needed to redefine the whole flow or if we
> could have just tweaked the existing one.
> Regards,
> Claude
> PS: here is the custom webflow:
>
> particuliers-flow.xml
>
> <flow xmlns="http://www.springframework.org/schema/webflow"
>       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>       xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
>       parent="authn.abstract, authn/conditions">
>
>     <!--
>     This is a login flow for basic-auth or form-based password authentication. The back-end step is named
>     "ValidateUsernamePassword" and can be supplied by different back-end implementation files.
>     -->
>
>     <var name="thisFlow" class="net.shibboleth.idp.authn.AuthenticationFlowDescriptor" />
>
>     <action-state id="ExtractUsernamePasswordFromBasicAuth">
>         <evaluate expression="ExtractUsernamePasswordFromBasicAuth" />
>         <evaluate expression="'proceed'" />
>
>         <transition on="proceed" to="ValidateUsernamePassword" />
>
>
>
>         <!-- Fall through to a different flow if basic-auth extract fails on a passive or non-browser request. -->
>         <transition on="#{ opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isPassive() || !opensamlProfileRequestContext.isBrowserProfile() }" to="ReselectFlow" />
>
>         <transition on="NoCredentials" to="DisplayUsernamePasswordPage" />
>         <transition on="InvalidCredentials" to="DisplayUsernamePasswordPage" />
>         <on-exit>
>             <set name="thisFlow" value="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getAttemptedFlow()" />
>         </on-exit>
>     </action-state>
>
>     <view-state id="DisplayUsernamePasswordPage" view="xxxx/particulier">
>         <on-render>
>             <evaluate expression="environment" result="viewScope.environment" />
>             <evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
>             <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" />
>             <evaluate expression="authenticationContext.getPotentialFlows().values().?[id matches 'authn/(' + (flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.authn.Password.ExtendedFlows') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.Password.ExtendedFlows').trim() : '') + ')']" result="viewScope.extendedAuthenticationFlows" />
>             <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.authn.Password.PrincipalOverride') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.Password.PrincipalOverride') : null" result="viewScope.passwordPrincipals" />
>             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.ui.context.RelyingPartyUIContext))" result="viewScope.rpUIContext" />
>             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationErrorContext))" result="viewScope.authenticationErrorContext" />
>             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationWarningContext))" result="viewScope.authenticationWarningContext" />
>             <evaluate expression="authenticationContext.getSubcontext(T(net.shibboleth.idp.authn.context.LDAPResponseContext))" result="viewScope.ldapResponseContext" />
>             <evaluate expression="T(net.shibboleth.utilities.java.support.codec.HTMLEncoder)" result="viewScope.encoder" />
>             <evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
>             <evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
>             <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
>         </on-render>
>
>         <transition on="proceed" to="ExtractUsernamePasswordFromFormRequest">
>             <evaluate expression="authenticationContext.setAttemptedFlow(thisFlow)" />
>         </transition>
>
>       <transition on="proceed_fas" to="FASAuthSelected"/>
>       <transition on="proceed_gestionDeCompte" to="GestionDeCompteAuthSelected"/>
>
>         <transition on="#{currentEvent.id.startsWith('authn/')}" to="PreserveAuthenticationFlowState">
>             <evaluate expression="authenticationContext.setAttemptedFlow(authenticationContext.getPotentialFlows().get(currentEvent.id))" />
>         </transition>
>
>         <on-exit>
>             <evaluate expression="opensamlProfileRequestContext.addSubcontext(new net.shibboleth.idp.consent.context.ConsentManagementContext(), true).setRevokeConsent(requestParameters._shib_idp_revokeConsent == 'true')" />
>         </on-exit>
>     </view-state>
>
>     <action-state id="PreserveAuthenticationFlowState">
>         <evaluate expression="PreserveAuthenticationFlowState" />
>         <evaluate expression="'proceed'" />
>
>         <transition on="proceed" to="CallExtendedFlow" />
>     </action-state>
>
>     <subflow-state id="CallExtendedFlow" subflow="#{opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getAttemptedFlow().getId()}">
>         <input name="calledAsSubflow" value="true" />
>         <input name="calledAsExtendedFlow" value="true" />
>         <transition on="proceed" to="proceed" />
>         <transition to="DisplayUsernamePasswordPage">
>             <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getIntermediateFlows().put(opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getAttemptedFlow().getId(), opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).getAttemptedFlow())" />
>         </transition>
>     </subflow-state>
>
>     <action-state id="ExtractUsernamePasswordFromFormRequest">
>         <evaluate expression="ExtractUsernamePasswordFromFormRequest" />
>         <evaluate expression="'proceed'" />
>
>         <!-- Let the validate action handle any problems later. -->
>         <transition to="ValidateUsernamePassword" />
>     </action-state>
>
>     <action-state id="ValidateUsernamePassword" parent="authn/conditions#ValidateUsernamePassword">
>         <evaluate expression="ValidateUsernamePassword" />
>         <evaluate expression="'proceed'" />
>
>         <transition on="proceed" to="ContinueSuccessfulAuthentication" />
>
>         <!-- Fall through to a different flow if login fails on a passive or non-browser request. -->
>         <transition on="#{ opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).isPassive() || !opensamlProfileRequestContext.isBrowserProfile() }" to="ReselectFlow" />
>
>         <!-- Other event transitions are determined by deployer in /flows/authn/conditions/conditions-flow.xml -->
>     </action-state>
>
>     <action-state id="ContinueSuccessfulAuthentication">
>         <evaluate expression="PopulateSubjectCanonicalizationContext" />
>         <evaluate expression="'proceed'" />
>
>         <transition on="proceed" to="CallSubjectCanonicalization" />
>     </action-state>
>
>     <!-- This runs a c14n step on the result of the authentication. -->
>     <subflow-state id="CallSubjectCanonicalization" subflow="c14n">
>         <input name="calledAsSubflow" value="true" />
>         <transition on="proceed" to="proceed" />
>
>         <!-- This shouldn't generally happen, but if c14n fails, it's allowable to fall through. -->
>         <transition on="SubjectCanonicalizationError" to="ReselectFlow" />
>     </subflow-state>
>
>     <bean-import resource="particulier-beans.xml" />
>
> </flow>
>
> and the particuliers-bean.xml:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <beans xmlns="http://www.springframework.org/schema/beans"
>        xmlns:context="http://www.springframework.org/schema/context"
>        xmlns:util="http://www.springframework.org/schema/util"
>        xmlns:p="http://www.springframework.org/schema/p"
>        xmlns:c="http://www.springframework.org/schema/c"
>        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
>                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
>                            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
>
>        default-init-method="initialize"
>        default-destroy-method="destroy">
>
>     <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
>           p:placeholderPrefix="%{" p:placeholderSuffix="}" />
>
>     <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
>     <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
>
>     <bean id="shibboleth.StorageBackedAccountLockoutManager" abstract="true"
>           class="net.shibboleth.idp.authn.impl.StorageBackedAccountLockoutManager"
>           p:storageService-ref="shibboleth.StorageService">
>         <property name="lockoutKeyStrategy">
>             <bean class="net.shibboleth.idp.authn.impl.StorageBackedAccountLockoutManager.UsernameIPLockoutKeyStrategy"
>                   p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
>         </property>
>     </bean>
>
>     <import resource="../../../conf/authn/xxxx/xxxx-particulier-password-authn-config.xml" />
>
>     <bean id="ExtractUsernamePasswordFromBasicAuth"
>           class="net.shibboleth.idp.authn.impl.ExtractUsernamePasswordFromBasicAuth" scope="prototype"
>           p:httpServletRequest-ref="shibboleth.HttpServletRequest"
>           p:lowercase-ref="shibboleth.authn.Password.Lowercase"
>           p:uppercase-ref="shibboleth.authn.Password.Uppercase"
>           p:trim-ref="shibboleth.authn.Password.Trim"
>           p:transforms-ref="shibboleth.authn.Password.Transforms" />
>
>     <bean id="PreserveAuthenticationFlowState"
>           class="net.shibboleth.idp.authn.impl.PreserveAuthenticationFlowState" scope="prototype"
>           p:httpServletRequest-ref="shibboleth.HttpServletRequest"
>           p:parameterNames="#{getObject('shibboleth.authn.Password.ExtendedFlowParameters')}" />
>
>     <bean id="ExtractUsernamePasswordFromFormRequest"
>           class="net.shibboleth.idp.authn.impl.ExtractUsernamePasswordFromFormRequest" scope="prototype"
>           p:httpServletRequest-ref="shibboleth.HttpServletRequest"
>           p:usernameFieldName-ref="shibboleth.authn.Password.UsernameFieldName"
>           p:passwordFieldName-ref="shibboleth.authn.Password.PasswordFieldName"
>           p:SSOBypassFieldName-ref="shibboleth.authn.Password.SSOBypassFieldName"
>           p:lowercase-ref="shibboleth.authn.Password.Lowercase"
>           p:uppercase-ref="shibboleth.authn.Password.Uppercase"
>           p:trim-ref="shibboleth.authn.Password.Trim"
>           p:transforms-ref="shibboleth.authn.Password.Transforms" />
>
>     <bean id="ValidateUsernamePasswordAgainstJAAS"
>           class="net.shibboleth.idp.authn.impl.ValidateUsernamePasswordAgainstJAAS" scope="prototype"
>           p:savePasswordToCredentialSet="#{getObject('shibboleth.authn.Password.RetainAsPrivateCredential') ?: false}"
>           p:removeContextAfterValidation="#{getObject('shibboleth.authn.Password.RemoveAfterValidation') ?: true}"
>           p:loginConfigStrategy="#{getObject('shibboleth.authn.JAAS.LoginConfigStrategy')}"
>           p:loginConfigNames="#{getObject('shibboleth.authn.JAAS.LoginConfigNames')}"
>           p:loginConfigurations="#{getObject('shibboleth.authn.JAAS.LoginConfigurations')}"
>           p:loginConfigType="JavaLoginConfig"
>           p:addDefaultPrincipals="#{getObject('shibboleth.authn.Password.addDefaultPrincipals') ?:
>                 (getObject('shibboleth.authn.Password.PrincipalOverride') == null
>                     or getObject('shibboleth.authn.Password.PrincipalOverride').isEmpty())}"
>           p:supportedPrincipals="#{getObject('shibboleth.authn.Password.PrincipalOverride')}"
>           p:classifiedMessages-ref="shibboleth.authn.Password.ClassifiedMessageMap"
>           p:resultCachingPredicate="#{getObject('shibboleth.authn.Password.resultCachingPredicate')}"
>           p:matchExpression="#{getObject('shibboleth.authn.Password.matchExpression')}"
>           p:lockoutManager="#{getObject('shibboleth.authn.Password.AccountLockoutManager')}">
>         <property name="loginConfigParameters">
>             <bean class="java.security.URIParameter">
>                 <constructor-arg ref="shibboleth.authn.JAAS.JAASConfigURI" />
>             </bean>
>         </property>
>     </bean>
>
>     <bean id="ValidateUsernamePasswordAgainstKerberos"
>           class="net.shibboleth.idp.authn.impl.ValidateUsernamePasswordAgainstKerberos" scope="prototype"
>           p:savePasswordToCredentialSet="#{getObject('shibboleth.authn.Password.RetainAsPrivateCredential') ?: false}"
>           p:removeContextAfterValidation="#{getObject('shibboleth.authn.Password.RemoveAfterValidation') ?: true}"
>           p:refreshKrb5Config-ref="shibboleth.authn.Krb5.RefreshConfig"
>           p:preserveTicket-ref="shibboleth.authn.Krb5.PreserveTicket"
>           p:servicePrincipal="#{getObject('shibboleth.authn.Krb5.ServicePrincipal')}"
>           p:keytabPath="#{getObject('shibboleth.authn.Krb5.Keytab')}"
>           p:addDefaultPrincipals="#{getObject('shibboleth.authn.Password.addDefaultPrincipals') ?:
>             (getObject('shibboleth.authn.Password.PrincipalOverride') == null
>                 or getObject('shibboleth.authn.Password.PrincipalOverride').isEmpty())}"
>           p:supportedPrincipals="#{getObject('shibboleth.authn.Password.PrincipalOverride')}"
>           p:classifiedMessages-ref="shibboleth.authn.Password.ClassifiedMessageMap"
>           p:resultCachingPredicate="#{getObject('shibboleth.authn.Password.resultCachingPredicate')}"
>           p:matchExpression="#{getObject('shibboleth.authn.Password.matchExpression')}"
>           p:lockoutManager="#{getObject('shibboleth.authn.Password.AccountLockoutManager')}" />
>
>     <!-- Parent beans for custom ldaptive CredentialConfig types. -->
>     <bean id="shibboleth.X509ResourceCredentialConfig"
>           class="net.shibboleth.idp.authn.impl.X509ResourceCredentialConfig" abstract="true" />
>     <bean id="shibboleth.KeystoreResourceCredentialConfig"
>           class="net.shibboleth.idp.authn.impl.KeystoreResourceCredentialConfig" abstract="true" />
>
>     <bean id="ValidateUsernamePasswordAgainstLDAP"
>           class="net.shibboleth.idp.authn.impl.ValidateUsernamePasswordAgainstLDAP" scope="prototype"
>           p:savePasswordToCredentialSet="#{getObject('shibboleth.authn.Password.RetainAsPrivateCredential') ?: false}"
>           p:removeContextAfterValidation="#{getObject('shibboleth.authn.Password.RemoveAfterValidation') ?: true}"
>           p:authenticator-ref="shibboleth.authn.LDAP.authenticator"
>           p:addDefaultPrincipals="#{getObject('shibboleth.authn.Password.addDefaultPrincipals') ?:
>             (getObject('shibboleth.authn.Password.PrincipalOverride') == null
>                 or getObject('shibboleth.authn.Password.PrincipalOverride').isEmpty())}"
>           p:supportedPrincipals="#{getObject('shibboleth.authn.Password.PrincipalOverride')}"
>           p:classifiedMessages-ref="shibboleth.authn.Password.ClassifiedMessageMap"
>           p:resultCachingPredicate="#{getObject('shibboleth.authn.Password.resultCachingPredicate')}"
>           p:matchExpression="#{getObject('shibboleth.authn.Password.matchExpression')}"
>           p:returnAttributes-ref="shibboleth.authn.LDAP.returnAttributes"
>           p:lockoutManager="#{getObject('shibboleth.authn.Password.AccountLockoutManager')}" />
>
>     <bean id="PopulateSubjectCanonicalizationContext"
>           class="net.shibboleth.idp.authn.impl.PopulateSubjectCanonicalizationContext" scope="prototype"
>           p:availableFlows-ref="shibboleth.PostLoginSubjectCanonicalizationFlows" />
>
> </beans>
>
>
> Le mer. 5 mai 2021 à 15:21, Cantor, Scott <cantor.2 at osu.edu> a écrit :
>
>> On 5/5/21, 9:11 AM, "users on behalf of Claude Libois" <
>> users-bounces at shibboleth.net on behalf of clibois.work at gmail.com> wrote:
>>
>> >    If I do understand you, it's never been a good practice to create
>> its own authentication flow(with it's own
>> > spring webflow) such as in our current idp configuration.
>>
>> You can, but you can't do it by copying ours without it breaking, which
>> is why it broke.
>>
>> >    I should only enable authentication flow in general-authn.xml by
>> adding/removing flow in <util:list
>> > id="shibboleth.AvailableAuthenticationFlows"> ?
>>
>> That's itself obsolete information in 4.1 but the Authentication topic
>> discusses doing extensions of this sort and how to register them.
>>
>> >    Am I right ?
>>
>> More or less but if you tell me what the actual goal of the custom flow
>> was then it's easier to give a general pointer.
>>
>> -- Scott
>>
>>
>> --
>> For Consortium Member technical support, see
>> https://wiki.shibboleth.net/confluence/x/coFAAg
>> To unsubscribe from this list send an email to
>> users-unsubscribe at shibboleth.net
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://shibboleth.net/pipermail/users/attachments/20210505/8de6ecdd/attachment.htm>


More information about the users mailing list