0
votes

I am pretty new for Java/Spring. If need more information, please don't hesitate to point out.

First, I tested below codes in Javascript(nodejs) which worked fine.

var ldap = require('ldapjs');
var client = ldap.createClient({
  url: 'ldap://xx.xx.xx.xx:389'
});

client.bind('domain\\user1', 'user1_password', function (err) {

  if (err) {
    throw err;
    return
  }
  var opts = {
    filter: '(sAMAccountName=user2)',
    scope: 'sub',
    attributes: ['l', 'sn', 'cn', 'mail', 'displayName', 'postalCode', 'physicalDeliveryOfficeName', 'telephoneNumber' ]
  };
  client.search('dc=aaa,dc=bbb,dc=ccc', opts, function(err, res) {
    res.on('searchEntry', function(entry) {
      Object.entries(entry.object).forEach(([key, value]) => {
        console.log('Found Attribute: ', key, '; value:', value)
      })
    });
  });

Then follow this guide:

http://forum.spring.io/forum/spring-projects/security/110491-how-to-modify-authority-after-loading-it-from-ldap

I applied the configurations which are same as above into spring/security.xml, like:

<bean id="customUserContextMapper" class="com.my.own.util.CustomUserDetailsMapper"/>
<sec:authentication-manager>
  <sec:ldap-authentication-provider
    user-search-base="dc=aaa,dc=bbb,dc=ccc"
    user-search-filter="(sAMAccountName={0})"
    user-context-mapper-ref="customUserContextMapper" 
  />
</sec:authentication-manager>
<sec:ldap-server url="ldap://xx.xx.xx.xx:389/"
     manager-dn="domain\user1" 
     manager-password="user1_password" 
/>

But it failed.

  1. If submitted (http-post) one form (username uses domain\username) like username=domain%5Cuser1&password=user1_password&submit=Login, it returned Reason: Bad credentials

  2. If submitted (http-post) one form (username doesn't enclose domain) like username=user1&password=user1_password&submit=Login, it returned 500 error:

    org.springframework.ldap.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of: '' ]; nested exception is javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of: '' ]; remaining name ''

For the first case, it seems the entry exists but failed to validate the password. so return bad credentials.

For the second case, the error seems to say search filter is wrong ( as CAS AD LDAP 32 error pointed out)

But I am sure I typed in correct username and password, and the search-filter is same as which already been tested in nodejs.

Already stuggled with this issue for a long time, but can't find one solution.

Appreciate for any suggestions and solutions.

Note: I set up breakpoint at the entry of the class=com.my.own.util.CustomUserDetailsMapper, but it never been triggered, so I didn't enclose its codes.

1

1 Answers

0
votes

Finally, I found below configuration working well.

Below <bean id="ldapAuthProvider"> construct two arguments,

First arg: it will verify the username and password which the end user fills in at the front end.

Second arg: once succeed to pass through the authenticator, it will invoke our own populator (<bean class="com.my.own.util.MyCustomLdapAuthPopulator">) to assign the appropriate roles or do something else you'd like.

<sec:authentication-manager>  
    <sec:authentication-provider 
        ref="ldapAuthProvider"
        >  
    </sec:authentication-provider>  
</sec:authentication-manager> 

<bean id="ldapAuthProvider"  
    class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider" >  
    <constructor-arg>  
        <bean id="authenticator"  
            class="org.springframework.security.ldap.authentication.BindAuthenticator">  
            <constructor-arg ref="contextSource" />  
            <property name="userSearch">  
                <bean  
                    class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">  
                    <constructor-arg value="dc=aaa,dc=bbb,dc=ccc" />  
                    <constructor-arg value="(sAMAccountName={0})" />  
                    <constructor-arg ref="contextSource" />  
                </bean>  
            </property>  
        </bean>  
    </constructor-arg>
        <constructor-arg>
            <bean class="com.my.own.util.MyCustomLdapAuthPopulator">
                <constructor-arg ref="contextSource" />
                <constructor-arg value="dc=aaa,dc=bbb,dc=ccc" />
                <property name="searchSubtree" value="true" />
                <property name="ignorePartialResultException" value="true" />
                <property name="groupSearchFilter" value="(member={0})" />
            </bean>
        </constructor-arg>
</bean> 
<bean id="contextSource"  
    class="org.springframework.security.ldap.DefaultSpringSecurityContextSource" >  
    <constructor-arg value="ldap://xx.xx.xx.xx:389/" />
    <property name="userDn" value="domain\user1" />    
    <property name="password" value="user1_password" />
</bean> 

Below is one simple implementation for our own populator.

import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
public class MyCustomLdapAuthPopulator extends DefaultLdapAuthoritiesPopulator {

    @Autowired
    public MyCustomLdapAuthPopulator(ContextSource contextSource, String groupSearchBase) {
        super(contextSource, groupSearchBase);
        // TODO Auto-generated constructor stub
    }

    @Override
    protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        authorities.add((new SimpleGrantedAuthority("ROLE_XXX")));
        return authorities;
    }
}