2
votes

I am trying to authorize users with spring security. So far I am able to authenticate them with ldap, but could not get the authorities yet.

I also connect ldap with Apache Directory Studio and query user information, i could find the authorities in user. But spring security ldap configuration could not gather them.

Here is the ldap part of the application-config.xml:

<sec:authentication-manager>
  <sec:ldap-authentication-provider 
    user-dn-pattern="uid={0},ou=myComp,o=myComp,dc=myComp,dc=ldap"
    group-search-base="dc=myComp,dc=ldap"
    group-search-filter="uniqueMember={0}"
    role-prefix="none" />

And here is the screen shot of the Apache Directory Studio with my information:

ADS screenshot of my user info

As you can see, my role information is in nsRole attribute of my user. But, unless I query nsRole attribute, it is not seen on my user info. So the question is, could i get these authorities with adding/changing the configuration of ldap configuration or do i have to write my own authentication-provider and connect ldap server and query the nsRole attribute manually?

Thanks a lot..

Note: This is a custom java code, that connects ldap server and queries my user roles successfully. package ldap;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class LdapTest2 {

    public static void main(String[] args) {
        Hashtable env = new Hashtable(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://myCompldap.mycomp.local:389/");
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, "uid=myUser,ou=myComp,o=myComp,dc=myComp,dc=ldap");
        env.put(Context.SECURITY_CREDENTIALS, "myPass");

        try {
            LdapContext ctx = new InitialLdapContext(env, null);
            ctx.setRequestControls(null);
            NamingEnumeration<?> namingEnum = ctx.search("uid=myUser,ou=myComp,o=myComp,dc=myComp,dc=ldap", "(objectClass=*)", getSimpleSearchControls());
            while (namingEnum.hasMore ()) {
                SearchResult result = (SearchResult) namingEnum.next ();
                Attributes attrs = result.getAttributes ();
                Attribute lattr = attrs.get("nsrole");

                NamingEnumeration allRoles = lattr.getAll();
                while(allRoles.hasMore()) {
                    Object role = allRoles.next();
                    System.out.println(role);
                }

            } 
            namingEnum.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static SearchControls getSimpleSearchControls() {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchControls.setTimeLimit(30000);
        String[] attrIDs = {"nsRole"};
        searchControls.setReturningAttributes(attrIDs);
        return searchControls;
    }

}
2

2 Answers

2
votes

Since your user roles are part of the user directory entry, you should be able to load them using a custom UserDetailsContextMapper. Move your custom role loading code to the mapUserFromContext method. You can skip the normal search for roles by using a NullLdapAuthoritiesPopulator.

You'll need to configure the authentication provider explicitly:

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
   <constructor-arg>
       <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
           <constructor-arg ref="contextSource"/>
           <property name="userDnPatterns">
             <list><value>uid={0},ou=myComp,o=myComp,dc=myComp,dc=ldap</value></list>
           </property>
       </bean>
   </constructor-arg>
   <constructor-arg>
       <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator" />
   </constructor-arg>
   <property name="userDetailsContextMapper" ref="yourContextMapperImplementation" />
</bean>
1
votes

spring security assume your ldap tree like that.

o=myComp,dc=com,dc=tr
      |
      |----ou=users (node)
      |      |
      |      |
      |      |-----uid=myuser1,dc=myComp,dc=com,dc=tr (node)
      |      |
      |      |-----uid=myuser2,dc=myComp,dc=com,dc=tr (node)
      |
      |
      |----ou=groups (node)
             |
             |
             |-----cn=myRole (node)
                       |
                  uniqueMember: uid=myuser1,dc=myComp,dc=com,dc=tr (attribute on myRole)
                  uniqueMember: uid=myuser2,dc=myComp,dc=com,dc=tr (attribute on myRole)

but your ldap tree is diffrent. so it can't find roles. your tree isn't wrong. you can use a custom userDetailsContextMapper as @LukeTaylor said. but roles and users should be in different nodes. if you put roles as attibute on user nodes I think this isn't useful when you want to update roles. e.g. you wanna remove a role or change role name then you should update all user nodes. but if you create nodes for each role then you just remove or rename one node.

UPDATE:

if you update your ldap tree. it should work

  <sec:ldap-authentication-provider 
    user-dn-pattern="uid={0},ou=myComp,o=myComp,dc=myComp,dc=ldap"
    group-search-base="ou=groups"
    group-search-filter="roleOccupant={0}"
    role-prefix="none" />

Note: use organizationalRole objectClass for role nodes. because groupOfUniqueNames requires minimum one uniqueMember. but organizationalRole doesn't require that.