Spring LDAP Context.REFERRAL to follow

6.4k views Asked by At

How do I set the LDAP Context.REFERRAL to follow in a Spring Security configuration? This is related to a problem I already reported and for which I found an unsatisfactory solution before discovering the real solution I am seeking for involve setting this environment attribute in the LDAP context to follow the referral for the ActiveDirectoryLdapAuthenticationProvider.

Here is the reference to my original question: Spring Security 4.0.0 + ActiveDirectoryLdapAuthenticationProvider + BadCredentialsException PartialResultException

Addendum: It seems no one is having such an environment here. Despite the silence on this problem, I post here my configuration hoping someone will be able to help me with this. I just don't know what I should do to solve this problem.

Here are the error messages in my log:

2015-06-15 10:32:19,810 DEBUG (o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter) [http-8443-1] /identite.proc at position 7 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2015-06-15 10:32:19,810 DEBUG (o.s.s.w.u.m.AntPathRequestMatcher.matches) [http-8443-1] Checking match of request : '/identite.proc'; against '/identite.proc'
2015-06-15 10:32:19,810 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.doFilter) [http-8443-1] Request is to process authentication
2015-06-15 10:32:19,811 DEBUG (o.s.s.a.ProviderManager.authenticate) [http-8443-1] Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
2015-06-15 10:32:19,811 DEBUG (o.s.s.l.a.AbstractLdapAuthenticationProvider.authenticate) [http-8443-1] Processing authentication request for user: myusername
2015-06-15 10:32:19,841 DEBUG (o.s.s.l.SpringSecurityLdapTemplate.searchForSingleEntryInternal) [http-8443-1] Searching for entry under DN '', base = 'dc=dept,dc=company,dc=com', filter = '(&(userPrincipalName={0})(objectClass=user)(objectCategory=inetOrgPerson))'
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Updated SecurityContextHolder to contain null Authentication
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@a5d7f2

And here is the configuration:

<b:bean id="monFournisseurAD" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <b:constructor-arg value="dept.company.com" />
    <b:constructor-arg value="ldap://dept.company.com:3268/" />
    <b:constructor-arg value="dc=dept,dc=company,dc=com" />
    <b:property name="searchFilter" value="(&amp;(userPrincipalName={0})(objectClass=user)(objectCategory=inetOrgPerson))" />
    <b:property name="userDetailsContextMapper">
        <b:bean class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />
    </b:property>
    <b:property name="authoritiesMapper" ref="grantedAuthoritiesMapper" />
    <b:property name="convertSubErrorCodesToExceptions" value="true" />
</b:bean>

<b:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <b:constructor-arg value="ldap://dept.company.com:3268/dc=dept,dc=company,dc=com" />
    <b:property name="baseEnvironmentProperties">
        <b:map>
            <b:entry key="java.naming.referral" value="follow" />
        </b:map>
    </b:property>
</b:bean>

<b:bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <b:constructor-arg ref="contextSource" />
</b:bean>

<b:bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />
<b:bean id="myDeconnexionHandler" class="com.company.dept.web.my.DeconnexionHandler" />

The two beans with ids contextSource and ldapTemplate seems not to change anything. I was trying to get the referral follow behavior. It seems not the right way to configure it. Also, here the port in the ldap URL is set to 3268 because it is the general catalog and someone in another description elsewhere suggested to use it. But, the results are exactly the same with port 389.

If I change the first constructor argument in the bean monFournisseurAD to set it to a single userPrincipalName domain, it will work for all users into that domain. While I can actually authenticate anyone from the command line using ldapsearch command using dept.company.com instead of the userPrincipalName domain associated directly to the user. In fact, if I enter a wrong password, I will get the specific wrong password message. This seems to prove the user is actually authenticated by AD/LDAP, however Spring fails later to fetch the attributes for that user.

How can I solve this problem?

1

There are 1 answers

0
Achille On BEST ANSWER

Finally, the only way to solve this problem is to modify the code since the method org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.searchForUser() is wired to pass the bindPrincipal to SpringSecurityLdapTemplate.searchForSingleEntryInternal() which actually recover the record for the user using the search filter defined in the XML or set with setSearchFilter(). Since the bindPrincipal is actually the username@domain, this value is not suitable for sAMAccountName usage in the search filter, it will always fail. Since the bind to the Active Directory server is performed using the userPrincipalName and then the bindPrincipal you have no way to specify to searchForUser() it must use the username only in the search rather than the bindPrincipal (UPN). I fixed my problem by copying the whole class org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider in my package and modify the searchForUser() method to pass the username instead of the bindPrincipal to the method searchForSingleEntryInternal(). This works, but it is still a wired/hardcoded solution. A more elegant solution would be to introduce a list of patterns and a method to compose the values for the call to searchForSingleEntryInternal() or a method that would detect the kind of arguments required from the searchFilter() string.