diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java index df7f069267..eca180aa40 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java @@ -40,10 +40,11 @@ import javax.naming.directory.SearchControls; /** * The default strategy for obtaining user role information from the directory. - * + *
*It obtains roles by performing a search for "groups" the user is a member of.
- * - *A typical group search scenario would be where each group/role is specified using the groupOfNames + *
+ * + * A typical group search scenario would be where each group/role is specified using the groupOfNames * (or groupOfUniqueNames) LDAP objectClass and the user's DN is listed in the member (or * uniqueMember) attribute to indicate that they should be assigned that role. The following LDIF sample has * the groups stored under the DN ou=groups,dc=acegisecurity,dc=org and a group called "developers" with @@ -55,13 +56,14 @@ import javax.naming.directory.SearchControls; * member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer * * - *The group search is performed within a DN specified by the groupSearchBase property, which should + *
+ * The group search is performed within a DN specified by the groupSearchBase property, which should * be relative to the root DN of its InitialDirContextFactory. If the search base is null, group searching is * disabled. The filter used in the search is defined by the groupSearchFilter property, with the filter * argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted * with the username. You can also specify which attribute defines the role name by setting * the groupRoleAttribute property (the default is "cn"). - * + * *The configuration below shows how the group search might be performed with the above schema. *
* <bean id="ldapAuthoritiesPopulator" @@ -76,7 +78,11 @@ import javax.naming.directory.SearchControls; * </bean> ** A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority - * "ROLE_DEVELOPER". + * "ROLE_DEVELOPER". + * + * + * The single-level search is performed by default. Setting the searchSubTree property to true will enable + * a search of the entire subtree under groupSearchBase. * * @author Luke Taylor * @version $Id$ @@ -88,10 +94,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator //~ Instance fields ================================================================================================ - /** A default role which will be assigned to all authenticated users if set */ + /** + * A default role which will be assigned to all authenticated users if set + */ private GrantedAuthority defaultRole = null; - /** An initial context factory is only required if searching for groups is required. */ + /** + * An initial context factory is only required if searching for groups is required. + */ private InitialDirContextFactory initialDirContextFactory = null; private LdapTemplate ldapTemplate; @@ -101,16 +111,24 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator */ private SearchControls searchControls = new SearchControls(); - /** The ID of the attribute which contains the role name for a group */ + /** + * The ID of the attribute which contains the role name for a group + */ private String groupRoleAttribute = "cn"; - /** The base DN from which the search for group membership should be performed */ + /** + * The base DN from which the search for group membership should be performed + */ private String groupSearchBase = null; - /** The pattern to be used for the user search. {0} is the user's DN */ + /** + * The pattern to be used for the user search. {0} is the user's DN + */ private String groupSearchFilter = "(member={0})"; - /** Attributes of the User's LDAP Object that contain role name information. */ + /** + * Attributes of the User's LDAP Object that contain role name information. + */ // private String[] userRoleAttributes = null; private String rolePrefix = "ROLE_"; @@ -123,8 +141,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator * set as a property. * * @param initialDirContextFactory supplies the contexts used to search for user roles. - * @param groupSearchBase if this is an empty string the search will be performed from the root DN of the - * context factory. + * @param groupSearchBase if this is an empty string the search will be performed from the root DN of the + * context factory. */ public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) { this.setInitialDirContextFactory(initialDirContextFactory); @@ -138,7 +156,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator * roles for the given user (on top of those obtained from the standard * search implemented by this class). * - * * @param ldapUser the user who's roles are required * @return the extra roles which will be merged with those returned by the group search */ @@ -152,7 +169,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator * the supplied LdapUserDetails object. * * @param userDetails the user who's authorities are required - * * @return the set of roles granted to the user. */ public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) { @@ -206,11 +222,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator if (logger.isDebugEnabled()) { logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter " - + groupSearchFilter + " in search base '" + getGroupSearchBase() + "'"); + + groupSearchFilter + " in search base '" + getGroupSearchBase() + "'"); } Set userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter, - new String[] {userDn, username}, groupRoleAttribute); + new String[]{userDn, username}, groupRoleAttribute); if (logger.isDebugEnabled()) { logger.debug("Roles from search: " + userRoles); @@ -234,12 +250,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator /** * Searches for groups the user is a member of. * - * @param userDn the user's distinguished name. + * @param userDn the user's distinguished name. * @param userAttributes the retrieved user's attributes (unused by default). - * * @return the set of roles obtained from a group membership search, or null if groupSearchBase has been * set. - * * @deprecated Subclasses should implement getAdditionalRoles instead. */ protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) { @@ -267,14 +281,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator * Set the group search base (name to search under) * * @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context - * factory. + * factory. */ private void setGroupSearchBase(String groupSearchBase) { Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null."); this.groupSearchBase = groupSearchBase; if (groupSearchBase.length() == 0) { logger.info("groupSearchBase is empty. Searches will be performed from the root: " - + getInitialDirContextFactory().getRootDn()); + + getInitialDirContextFactory().getRootDn()); } } @@ -311,6 +325,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator this.rolePrefix = rolePrefix; } + /** + * If set to true, a subtree scope search will be performed. If false a single-level search is used. + * + * @param searchSubtree set to true to enable searching of the entire tree below the groupSearchBase. + */ public void setSearchSubtree(boolean searchSubtree) { int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE; searchControls.setSearchScope(searchScope); diff --git a/core/src/test/java/org/acegisecurity/ldap/AbstractLdapServerTestCase.java b/core/src/test/java/org/acegisecurity/ldap/AbstractLdapServerTestCase.java index 84b024ec58..dc8bebb276 100644 --- a/core/src/test/java/org/acegisecurity/ldap/AbstractLdapServerTestCase.java +++ b/core/src/test/java/org/acegisecurity/ldap/AbstractLdapServerTestCase.java @@ -23,8 +23,6 @@ import java.util.Hashtable; /** - * - * * @author Luke Taylor * @version $Id$ */ @@ -36,7 +34,7 @@ public abstract class AbstractLdapServerTestCase extends TestCase { protected static final String MANAGER_PASSWORD = "acegisecurity"; // External server config -// private static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN; +// private static final String PROVIDER_URL = "ldap://gorille:389/"+ROOT_DN; // private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; // private static final Hashtable EXTRA_ENV = new Hashtable(); @@ -52,7 +50,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase { //~ Constructors =================================================================================================== - protected AbstractLdapServerTestCase() {} + protected AbstractLdapServerTestCase() { + } protected AbstractLdapServerTestCase(String string) { super(string); @@ -64,7 +63,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase { return idf; } - protected void onSetUp() {} + protected void onSetUp() { + } public final void setUp() { idf = new DefaultInitialDirContextFactory(PROVIDER_URL); diff --git a/core/src/test/java/org/acegisecurity/ldap/LdapTemplateTests.java b/core/src/test/java/org/acegisecurity/ldap/LdapTemplateTests.java index 6accba3353..4943df03e8 100644 --- a/core/src/test/java/org/acegisecurity/ldap/LdapTemplateTests.java +++ b/core/src/test/java/org/acegisecurity/ldap/LdapTemplateTests.java @@ -22,8 +22,6 @@ import javax.naming.directory.DirContext; /** - * - * * @author Luke Taylor * @version $Id$ */ @@ -67,22 +65,24 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase { public void testNamingExceptionIsTranslatedCorrectly() { try { template.execute(new LdapCallback() { - public Object doInDirContext(DirContext dirContext) + public Object doInDirContext(DirContext dirContext) throws NamingException { - throw new NamingException(); - } - }); + throw new NamingException(); + } + }); fail("Expected LdapDataAccessException on NamingException"); - } catch (LdapDataAccessException expected) {} + } catch (LdapDataAccessException expected) { + } } public void testSearchForSingleAttributeValues() { String param = "uid=ben,ou=people,dc=acegisecurity,dc=org"; - Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou"); + Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou"); - assertEquals("Expected 2 results from search", 2, values.size()); + assertEquals("Expected 3 results from search", 3, values.size()); assertTrue(values.contains("developer")); assertTrue(values.contains("manager")); + assertTrue(values.contains("submanager")); } } diff --git a/core/src/test/java/org/acegisecurity/ldap/LdapTestServer.java b/core/src/test/java/org/acegisecurity/ldap/LdapTestServer.java index 4047859380..0e51dad031 100644 --- a/core/src/test/java/org/acegisecurity/ldap/LdapTestServer.java +++ b/core/src/test/java/org/acegisecurity/ldap/LdapTestServer.java @@ -64,7 +64,7 @@ public class LdapTestServer { //~ Methods ======================================================================================================== - public void createGroup(String cn, String ou, String[] memberDns) { + public void createGroup(String cn, String groupContext, String ou, String[] memberDns) { Attributes group = new BasicAttributes("cn", cn); Attribute members = new BasicAttribute("member"); Attribute orgUnit = new BasicAttribute("ou", ou); @@ -82,7 +82,8 @@ public class LdapTestServer { group.put(orgUnit); try { - serverContext.createSubcontext("cn=" + cn + ",ou=groups", group); + DirContext ctx = serverContext.createSubcontext("cn=" + cn + "," + groupContext, group); + System.out.println("Created group " + ctx.getNameInNamespace()); } catch (NameAlreadyBoundException ignore) { // System.out.println(" group " + cn + " already exists."); } catch (NamingException ne) { @@ -122,7 +123,7 @@ public class LdapTestServer { ou.put(objectClass); try { - serverContext.createSubcontext("ou=" + name, ou); + serverContext.createSubcontext(name, ou); } catch (NameAlreadyBoundException ignore) { // System.out.println(" ou " + name + " already exists."); } catch (NamingException ne) { @@ -188,16 +189,19 @@ public class LdapTestServer { } private void initTestData() { - createOu("people"); - createOu("groups"); + createOu("ou=people"); + createOu("ou=groups"); + createOu("ou=subgroups,ou=groups"); + createUser("bob", "Bob Hamilton", "bobspassword"); createUser("ben", "Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ="); - String[] developers = new String[] { + String[] developers = new String[]{ "uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org" - }; - createGroup("developers", "developer", developers); - createGroup("managers", "manager", new String[] {developers[0]}); + }; + createGroup("developers", "ou=groups", "developer", developers); + createGroup("managers", "ou=groups", "manager", new String[]{developers[0]}); + createGroup("submanagers", "ou=subgroups,ou=groups", "submanager", new String[]{developers[0]}); } public static void main(String[] args) { @@ -243,11 +247,13 @@ public class LdapTestServer { } } - /** Recursively deletes a directory */ + /** + * Recursively deletes a directory + */ private boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); - for (int i=0; i