diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/DefaultInitialDirContextFactory.java b/core/src/main/java/org/acegisecurity/providers/ldap/DefaultInitialDirContextFactory.java index 6bffd71d4a..20eebf8908 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/DefaultInitialDirContextFactory.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/DefaultInitialDirContextFactory.java @@ -25,7 +25,6 @@ import javax.naming.directory.InitialDirContext; import javax.naming.directory.DirContext; import org.springframework.dao.DataAccessResourceFailureException; -import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.acegisecurity.BadCredentialsException; import org.apache.commons.logging.Log; @@ -39,7 +38,7 @@ import org.apache.commons.logging.LogFactory; * This should be in the form ldap://monkeymachine.co.uk:389/dc=acegisecurity,dc=org. *
*- * To obtain an initial context, th client calls the newInitialDirContext + * To obtain an initial context, the client calls the newInitialDirContext * method. There are two signatures - one with no arguments and one which allows * binding with a specific username and password. *
@@ -53,16 +52,15 @@ import org.apache.commons.logging.LogFactory; * as a specific user. * * - * @see The Java tutorial's guide to - * Connection Pooling + * @see The Java + * tutorial's guide to LDAP connection pooling * * @author Robert Sanders * @author Luke Taylor * @version $Id$ * */ -public class DefaultInitialDirContextFactory implements InitialDirContextFactory, - InitializingBean { +public class DefaultInitialDirContextFactory implements InitialDirContextFactory { //~ Static fields/initializers ============================================= @@ -118,13 +116,39 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory */ private boolean useConnectionPool = true; + //~ Constructors =========================================================== + + public DefaultInitialDirContextFactory(String url) { + this.url = url; + + Assert.hasLength(url, "An LDAP connection URL must be supplied."); + + if(url.startsWith("ldap:")) { + + URI uri = LdapUtils.parseLdapUrl(url); + + rootDn = uri.getPath(); + + } else { + // Assume it's an embedded server + rootDn = url; + } + + if(rootDn.startsWith("/")) { + rootDn = rootDn.substring(1); + } + + // This doesn't necessarily hold for embedded servers. + //Assert.isTrue(uri.getScheme().equals("ldap"), "Ldap URL must start with 'ldap://'"); + } + //~ Methods ================================================================ /** * Connects anonymously unless a manager user has been specified, in which case * it will bind as the manager. * - * @return the resulting + * @return the resulting context object. */ public DirContext newInitialDirContext() { @@ -152,7 +176,8 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory } /** - * @return The Hashtable describing the base DirContext that will be created, minus the username/password if any. + * @return the Hashtable describing the base DirContext that will be created, + * minus the username/password if any. */ protected Hashtable getEnvironment() { Hashtable env = new Hashtable(); @@ -174,8 +199,15 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory private InitialDirContext connect(Hashtable env) { -// Prints the password, so don't use except for debugging. -// logger.debug("Creating initial context with env " + env); + if(logger.isDebugEnabled()) { + Hashtable envClone = (Hashtable)env.clone(); + + if(envClone.containsKey(Context.SECURITY_CREDENTIALS)) { + envClone.put(Context.SECURITY_CREDENTIALS, "******"); + } + + logger.debug("Creating InitialDirContext with environment " + envClone); + } try { return new InitialDirContext(env); @@ -189,27 +221,6 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory } } - public void afterPropertiesSet() throws Exception { - Assert.hasLength(url, "An LDAP connection URL must be supplied."); - - if(url.startsWith("ldap:")) { - - URI uri = new URI(url); - - rootDn = uri.getPath(); - } else { - // Assume it's an embedded server - rootDn = url; - } - - if(rootDn.startsWith("/")) { - rootDn = rootDn.substring(1); - } - - //Assert.isTrue(uri.getScheme().equals("ldap"), "Ldap URL must start with 'ldap://'"); - - } - /** * Returns the root DN of the configured provider URL. For example, * if the URL is ldap://monkeymachine.co.uk:389/dc=acegisecurity,dc=org @@ -222,12 +233,12 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory } public void setAuthenticationType(String authenticationType) { - Assert.hasLength(authenticationType); + Assert.hasLength(authenticationType, "LDAP Authentication type must not be empty or null"); this.authenticationType = authenticationType; } public void setInitialContextFactory(String initialContextFactory) { - Assert.hasLength(initialContextFactory); + Assert.hasLength(initialContextFactory, "Initial context factory name cannot be empty or null"); this.initialContextFactory = initialContextFactory; } @@ -235,6 +246,7 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory * @param managerDn The name of the "manager" user for default authentication. */ public void setManagerDn(String managerDn) { + Assert.hasLength(managerDn, "Manager user name cannot be empty or null."); this.managerDn = managerDn; } @@ -242,18 +254,15 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory * @param managerPassword The "manager" user's password. */ public void setManagerPassword(String managerPassword) { + Assert.hasLength(managerPassword, "Manager password must not be empty or null."); this.managerPassword = managerPassword; } - public void setUrl(String url) { - this.url = url; - } - /** * @param extraEnvVars extra environment variables to be added at config time. */ public void setExtraEnvVars(Map extraEnvVars) { + Assert.notNull(extraEnvVars, "Extra environment map cannot be null."); this.extraEnvVars = extraEnvVars; } - } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java index 060776e474..32009373e5 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java @@ -76,7 +76,24 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio private LdapAuthenticator authenticator; - private LdapAuthoritiesPopulator ldapAuthoritiesPopulator; + private LdapAuthoritiesPopulator authoritiesPopulator; + + + //~ Constructors =========================================================== + + public LdapAuthenticationProvider(LdapAuthenticator authenticator, + LdapAuthoritiesPopulator authoritiesPopulator) { + Assert.notNull(authenticator, "An LdapAuthenticator must be supplied"); + Assert.notNull(authoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied"); + + this.authenticator = authenticator; + this.authoritiesPopulator = authoritiesPopulator; + + // TODO: Check that the role attributes specified for the populator will be retrieved + // by the authenticator. If not, add them to the authenticator's list and log a + // warning. + + } //~ Methods ================================================================ @@ -89,21 +106,11 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio String password = (String)authentication.getCredentials(); Assert.notNull(password, "Null password was supplied in authentication token"); - LdapUserDetails ldapUser = authenticator.authenticate(username, password); + LdapUserInfo ldapUser = authenticator.authenticate(username, password); return createUserDetails(username, password, ldapUser.getDn(), ldapUser.getAttributes()); } - protected void doAfterPropertiesSet() throws Exception { - super.doAfterPropertiesSet(); - Assert.notNull(authenticator, "An LdapAuthenticator must be supplied"); - Assert.notNull(ldapAuthoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied"); - - // TODO: Check that the role attributes specified for the populator will be retrieved - // by the authenticator. If not, add them to the authenticator's list and log a - // warning. - } - /** * Creates the user final UserDetails object that will be returned by the provider * once the user has been authenticated. @@ -124,15 +131,8 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio protected UserDetails createUserDetails(String username, String password, String userDn, Attributes attributes) { return new User(username, password, true, true, true, true, - ldapAuthoritiesPopulator.getGrantedAuthorities(username, userDn, attributes)); + authoritiesPopulator.getGrantedAuthorities(username, userDn, attributes)); } - - public void setAuthenticator(LdapAuthenticator authenticator) { - this.authenticator = authenticator; - } - - public void setLdapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) { - this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator; - } } + diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java index dc251dcf7d..8a78d8d0e3 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java @@ -35,5 +35,5 @@ public interface LdapAuthenticator { * @param password the user's password supplied at login. * @return the details of the successfully authenticated user. */ - LdapUserDetails authenticate(String username, String password); + LdapUserInfo authenticate(String username, String password); } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java index 4bb12004a7..db37e23f2b 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java @@ -32,12 +32,15 @@ import javax.naming.directory.Attributes; public interface LdapAuthoritiesPopulator { /** + * Get the list of authorities for the user. * * @param username the login name which was passed to the LDAP provider. * @param userDn the full DN of the user * @param userAttributes the user's LDAP attributes that were retrieved from the directory. * @return the granted authorities for the given user. + * @throws LdapDataAccessException if there is a problem accessing the directory. */ - GrantedAuthority[] getGrantedAuthorities(String username, String userDn, Attributes userAttributes); + GrantedAuthority[] getGrantedAuthorities(String username, String userDn, Attributes userAttributes) + throws LdapDataAccessException; } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapUserDetails.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapUserInfo.java similarity index 90% rename from core/src/main/java/org/acegisecurity/providers/ldap/LdapUserDetails.java rename to core/src/main/java/org/acegisecurity/providers/ldap/LdapUserInfo.java index 424d590973..8dae67e05d 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapUserDetails.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapUserInfo.java @@ -15,10 +15,6 @@ package org.acegisecurity.providers.ldap; -import org.acegisecurity.userdetails.User; -import org.acegisecurity.GrantedAuthority; -import org.acegisecurity.GrantedAuthorityImpl; - import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.NamingException; @@ -40,7 +36,7 @@ import javax.naming.NamingException; * @author Luke Taylor * @version $Id$ */ -public class LdapUserDetails { +public class LdapUserInfo { //~ Instance fields ======================================================== @@ -54,7 +50,7 @@ public class LdapUserDetails { * @param dn the full DN of the user * @param attributes any attributes loaded from the user's directory entry. */ - public LdapUserDetails(String dn, Attributes attributes) { + public LdapUserInfo(String dn, Attributes attributes) { this.dn = dn; this.attributes = attributes; } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapUtils.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapUtils.java index aabb4ca0aa..e3eddb8b07 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapUtils.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapUtils.java @@ -22,6 +22,8 @@ import org.springframework.util.Assert; import javax.naming.Context; import javax.naming.NamingException; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; /** * LDAP Utility methods. @@ -46,6 +48,22 @@ public class LdapUtils { } } + /** + * Parses the supplied LDAP URL. + * @param url the URL (e.g. ldap://monkeymachine:11389/dc=acegisecurity,dc=org). + * @return the URI object created from the URL + * @throws IllegalArgumentException if the URL is null, empty or the URI syntax is invalid. + */ + public static URI parseLdapUrl(String url) { + Assert.hasLength(url); + + try { + return new URI(url); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Unable to parse url: " + url, e); + } + } + public static byte[] getUtf8Bytes(String s) { try { return s.getBytes("UTF-8"); diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java index 01999bd1fa..eca0a3ca6e 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java @@ -21,6 +21,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import java.text.MessageFormat; +import java.util.List; +import java.util.ArrayList; /** * @author Luke Taylor @@ -31,36 +33,52 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, //~ Instance fields ======================================================== - private String userDnPattern = null; - private MessageFormat userDnFormat = null; + //private String[] userDnPattern = null; + private MessageFormat[] userDnFormat = null; private InitialDirContextFactory initialDirContextFactory; private LdapUserSearch userSearch; private String[] userAttributes = null; + private String dnSuffix = ""; + + //~ Constructors =========================================================== + + protected AbstractLdapAuthenticator(InitialDirContextFactory initialDirContextFactory) { + Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null."); + this.initialDirContextFactory = initialDirContextFactory; + + String rootDn = initialDirContextFactory.getRootDn(); + + if(rootDn.length() > 0) { + dnSuffix = "," + rootDn; + } + } //~ Methods ================================================================ /** - * Returns the DN of the user, worked out from the userDNPattern property. - * The returned value includes the root DN of the provider - * URL used to configure the InitialDirContextfactory. + * Builds list of possible DNs for the user, worked out from the + * userDnPatterns property. The returned value includes the root DN of + * the provider URL used to configure the InitialDirContextfactory. + * + * @param username the user's login name + * @return the list of possible DN matches, empty if userDnPatterns wasn't + * set. */ - protected String getUserDn(String username) { + protected List getUserDns(String username) { if(userDnFormat == null) { - return null; + return new ArrayList(0); } - String rootDn = initialDirContextFactory.getRootDn(); - String userDn; + List userDns = new ArrayList(userDnFormat.length); + String[] args = new String[] {username}; synchronized( userDnFormat ) { - userDn = userDnFormat.format(new String[] {username}); + for(int i=0; i < userDnFormat.length; i++) { + userDns.add( userDnFormat[i].format(args) + dnSuffix ); + } } - if(rootDn.length() > 0) { - userDn = userDn + "," + rootDn; - } - - return userDn; + return userDns; } /** @@ -69,24 +87,32 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, * The pattern argument {0} will contain the username. * An example would be "cn={0},ou=people". */ - public void setUserDnPattern(String dnPattern) { - this.userDnPattern = dnPattern; - userDnFormat = null; + public void setUserDnPatterns(String[] dnPattern) { + Assert.notNull(dnPattern, "The array of DN patterns cannot be set to null"); +// this.userDnPattern = dnPattern; + userDnFormat = new MessageFormat[dnPattern.length]; - if(dnPattern != null) { - userDnFormat = new MessageFormat(dnPattern); + for(int i=0; i < dnPattern.length; i++) { + userDnFormat[i] = new MessageFormat(dnPattern[i]); } } + /** + * Sets the user attributes which will be retrieved from the directory. + * + * @param userAttributes + */ + public void setUserAttributes(String[] userAttributes) { + Assert.notNull(userAttributes, "The userAttributes property cannot be set to null"); + this.userAttributes = userAttributes; + } + public String[] getUserAttributes() { return userAttributes; } - public String getUserDnPattern() { - return userDnPattern; - } - public void setUserSearch(LdapUserSearch userSearch) { + Assert.notNull(userSearch, "The userSearch cannot be set to null"); this.userSearch = userSearch; } @@ -94,25 +120,12 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, return userSearch; } - public void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) { - this.initialDirContextFactory = initialDirContextFactory; - } - - /** - * Sets the user attributes which will be retrieved from the directory. - * - * @param userAttributes - */ - public void setUserAttributes(String[] userAttributes) { - this.userAttributes = userAttributes; - } - protected InitialDirContextFactory getInitialDirContextFactory() { return initialDirContextFactory; } public void afterPropertiesSet() throws Exception { - Assert.notNull(initialDirContextFactory, "initialDirContextFactory must be supplied."); - Assert.isTrue(userDnPattern != null || userSearch != null, "Either an LdapUserSearch or DN pattern (or both) must be supplied."); + Assert.isTrue(userDnFormat != null || userSearch != null, + "Either an LdapUserSearch or DN pattern (or both) must be supplied."); } } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java index ea9c83bf09..253a5728e0 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import javax.naming.directory.DirContext; import javax.naming.directory.Attributes; import javax.naming.NamingException; +import java.util.Iterator; /** * An authenticator which binds as a user. @@ -32,28 +33,35 @@ import javax.naming.NamingException; * @author Luke Taylor * @version $Id$ */ -public class BindAuthenticator extends AbstractLdapAuthenticator { +public final class BindAuthenticator extends AbstractLdapAuthenticator { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(BindAuthenticator.class); + //~ Constructors =========================================================== + + public BindAuthenticator(InitialDirContextFactory initialDirContextFactory) { + super(initialDirContextFactory); + } + //~ Methods ================================================================ - public LdapUserDetails authenticate(String username, String password) { + public LdapUserInfo authenticate(String username, String password) { - String dn = getUserDn(username); - LdapUserDetails user = null; + LdapUserInfo user = null; - // If DN is pattern is configured, try authenticating with that directly - if(dn != null) { - user = authenticateWithDn(dn, password); + // If DN patterns are configured, try authenticating with them directly + Iterator dns = getUserDns(username).iterator(); + + while(dns.hasNext() && user == null) { + user = authenticateWithDn((String)dns.next(), password); } // Otherwise use the configured locator to find the user // and authenticate with the returned DN. if(user == null && getUserSearch() != null) { - LdapUserDetails userFromSearch = getUserSearch().searchForUser(username); + LdapUserInfo userFromSearch = getUserSearch().searchForUser(username); user = authenticateWithDn(userFromSearch.getDn(), password); } @@ -65,13 +73,13 @@ public class BindAuthenticator extends AbstractLdapAuthenticator { } - private LdapUserDetails authenticateWithDn(String userDn, String password) { + private LdapUserInfo authenticateWithDn(String userDn, String password) { DirContext ctx = null; - LdapUserDetails user = null; + LdapUserInfo user = null; Attributes attributes = null; if(logger.isDebugEnabled()) { - logger.debug("Binding with DN = " + userDn); + logger.debug("Attempting to bind with DN = " + userDn); } try { @@ -79,8 +87,8 @@ public class BindAuthenticator extends AbstractLdapAuthenticator { attributes = ctx.getAttributes( LdapUtils.getRelativeName(userDn, ctx), getUserAttributes()); - user = new LdapUserDetails(userDn, attributes); - + user = new LdapUserInfo(userDn, attributes); + } catch(NamingException ne) { throw new LdapDataAccessException("Failed to load attributes for user " + userDn, ne); } catch(BadCredentialsException e) { diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearch.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearch.java index 019a4bb974..e4d01374a5 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearch.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearch.java @@ -84,12 +84,12 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch { //~ Methods ================================================================ /** - * Return the LdapUserDetails containing the user's information, or null if + * Return the LdapUserInfo containing the user's information, or null if * no SearchResult is found. * * @param username the username to search for. */ - public LdapUserDetails searchForUser(String username) { + public LdapUserInfo searchForUser(String username) { DirContext ctx = initialDirContextFactory.newInitialDirContext(); SearchControls ctls = new SearchControls(); ctls.setTimeLimit( searchTimeLimit ); @@ -120,7 +120,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch { userDn.append(","); userDn.append(ctx.getNameInNamespace()); - return new LdapUserDetails(userDn.toString(), searchResult.getAttributes()); + return new LdapUserInfo(userDn.toString(), searchResult.getAttributes()); } catch(NamingException ne) { throw new LdapDataAccessException("User Couldn't be found due to exception", ne); diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapUserSearch.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapUserSearch.java index 68cc9399db..ffe84619fc 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapUserSearch.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapUserSearch.java @@ -15,7 +15,7 @@ package org.acegisecurity.providers.ldap.authenticator; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; /** * Obtains a user's information from the LDAP directory given a login name. @@ -35,9 +35,9 @@ public interface LdapUserSearch { * for that user. * * @param username the login name supplied to the authentication service. - * @return an LdapUserDetails object containing the user's full DN and requested attributes. + * @return an LdapUserInfo object containing the user's full DN and requested attributes. * TODO: Need to optionally supply required attributes here for the search. */ - LdapUserDetails searchForUser(String username); + LdapUserInfo searchForUser(String username); } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java index 0a1c3607aa..03bc93d741 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java @@ -15,8 +15,9 @@ package org.acegisecurity.providers.ldap.authenticator; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; import org.acegisecurity.providers.ldap.LdapUtils; +import org.acegisecurity.providers.ldap.InitialDirContextFactory; import org.acegisecurity.providers.encoding.PasswordEncoder; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.userdetails.UsernameNotFoundException; @@ -29,6 +30,7 @@ import javax.naming.NamingException; import javax.naming.directory.SearchControls; import javax.naming.directory.DirContext; import javax.naming.directory.Attribute; +import java.util.Iterator; /** * An {@link org.acegisecurity.providers.ldap.LdapAuthenticator LdapAuthenticator} @@ -49,7 +51,7 @@ import javax.naming.directory.Attribute; * @author Luke Taylor * @version $Id$ */ -public class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator { +public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class); @@ -64,21 +66,28 @@ public class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator { private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder(); + //~ Constructors =========================================================== + + public PasswordComparisonAuthenticator(InitialDirContextFactory initialDirContextFactory) { + super(initialDirContextFactory); + } + //~ Methods ================================================================ - public LdapUserDetails authenticate(String username, String password) { + public LdapUserInfo authenticate(String username, String password) { // locate the user and check the password - String userDn = getUserDn(username); - LdapUserDetails user = null; + LdapUserInfo user = null; DirContext ctx = getInitialDirContextFactory().newInitialDirContext(); + Iterator dns = getUserDns(username).iterator(); try { - if(userDn != null) { + while(dns.hasNext() && user == null) { + String userDn = (String)dns.next(); String relativeName = LdapUtils.getRelativeName(userDn, ctx); - user = new LdapUserDetails(userDn, + user = new LdapUserInfo(userDn, ctx.getAttributes(relativeName, getUserAttributes())); } @@ -105,6 +114,10 @@ public class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator { } } else { + if(logger.isDebugEnabled()) { + logger.debug("Password attribute " + passwordAttributeName + + " wasn't retrieved for user " + username); + } doPasswordCompare(ctx, user.getRelativeName(ctx), password); } @@ -153,7 +166,7 @@ public class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator { } public void setPasswordAttributeName(String passwordAttribute) { - Assert.hasLength(passwordAttribute, "passwordAttribute must not be empty or null"); + Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null"); this.passwordAttributeName = passwordAttribute; this.passwordCompareFilter = "(" + passwordAttributeName + "={0})"; } 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 e2dd17635d..e48ea55f34 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 @@ -24,7 +24,6 @@ import org.acegisecurity.GrantedAuthorityImpl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.Assert; -import org.springframework.beans.factory.InitializingBean; import javax.naming.directory.Attributes; import javax.naming.directory.Attribute; @@ -85,17 +84,18 @@ import java.util.HashSet; * setting the groupRoleAttribute property (the default is "cn"). * *+ *
* <bean id="ldapAuthoritiesPopulator" class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator"> * TODO * </bean> + ** * * * @author Luke Taylor * @version $Id$ */ -public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator, - InitializingBean { +public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(DefaultLdapAuthoritiesPopulator.class); @@ -105,7 +105,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator /** Attributes of the User's LDAP Object that contain role name information. */ private String[] userRoleAttributes = null; - private String rolePrefix = ""; + private String rolePrefix = "ROLE_"; /** The base DN from which the search for group membership should be performed */ private String groupSearchBase = null; @@ -127,6 +127,30 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator /** An initial context factory is only required if searching for groups is required. */ private InitialDirContextFactory initialDirContextFactory = null; + //~ Constructors =========================================================== + + /** + * Constructor for non-group search scenarios. Typically in this case + * the userRoleAttributes property will be set to obtain roles directly + * from the user's directory entry attributes. + */ + public DefaultLdapAuthoritiesPopulator() { + } + + /** + * Constructor for group search scenarios. userRoleAttributes may still be + * set as a property. + * + * @param initialDirContextFactory + * @param groupSearchBase + */ + public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) { + Assert.notNull(initialDirContextFactory, "InitialDirContextFactory must not be null"); + Assert.hasLength(groupSearchBase, "The groupSearchBase (name to search under), must be specified."); + this.initialDirContextFactory = initialDirContextFactory; + this.groupSearchBase = groupSearchBase; + } + //~ Methods ================================================================ /** @@ -176,6 +200,12 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator return null; } + if(logger.isDebugEnabled()) { + logger.debug("Searching for roles for user '" + + userDn + "', with filter "+ groupSearchFilter + + " in search base '" + groupSearchBase + "'"); + } + DirContext ctx = initialDirContextFactory.newInitialDirContext(); SearchControls ctls = new SearchControls(); @@ -200,11 +230,15 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator } } } catch (NamingException e) { - + throw new LdapDataAccessException("Group search failed for user " + userDn, e); } finally { LdapUtils.closeContext(ctx); } + if(logger.isDebugEnabled()) { + logger.debug("Roles from search: " + userRoles); + } + return userRoles; } @@ -249,10 +283,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator this.rolePrefix = rolePrefix; } - public void setGroupSearchBase(String groupSearchBase) { - this.groupSearchBase = groupSearchBase; - } - public void setGroupSearchFilter(String groupSearchFilter) { Assert.notNull(groupSearchFilter, "groupSearchFilter must not be null"); this.groupSearchFilter = groupSearchFilter; @@ -272,14 +302,4 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator public void setConvertToUpperCase(boolean convertToUpperCase) { this.convertToUpperCase = convertToUpperCase; } - - public void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) { - this.initialDirContextFactory = initialDirContextFactory; - } - - public void afterPropertiesSet() throws Exception { - if(initialDirContextFactory == null && groupSearchBase != null) { - throw new IllegalArgumentException("initialDirContextFactory is required for group role searches."); - } - } } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/InitialDirContextFactoryTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/InitialDirContextFactoryTests.java index 52749c56a5..8a49a5f1c7 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/InitialDirContextFactoryTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/InitialDirContextFactoryTests.java @@ -30,20 +30,19 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { // } public void setUp() { - idf = new DefaultInitialDirContextFactory(); + idf = new DefaultInitialDirContextFactory(PROVIDER_URL); idf.setInitialContextFactory(CONTEXT_FACTORY); idf.setExtraEnvVars(EXTRA_ENV); } public void testConnectionFailure() throws Exception { - - idf.setInitialContextFactory("com.sun.jndi.ldap.LdapCtxFactory"); // Use the wrong port - idf.setUrl("ldap://localhost:60389"); + idf = new DefaultInitialDirContextFactory("ldap://localhost:60389"); + idf.setInitialContextFactory("com.sun.jndi.ldap.LdapCtxFactory"); Hashtable env = new Hashtable(); env.put("com.sun.jndi.ldap.connect.timeout", "200"); idf.setExtraEnvVars(env); - idf.afterPropertiesSet(); + try { idf.newInitialDirContext(); fail("Connection succeeded unexpectedly"); @@ -52,8 +51,6 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testAnonymousBindSucceeds() throws Exception { - idf.setUrl(PROVIDER_URL); - idf.afterPropertiesSet(); DirContext ctx = idf.newInitialDirContext(); // Connection pooling should be set by default for anon users. // Can't rely on this property being there with embedded server @@ -62,10 +59,9 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testBindAsManagerSucceeds() throws Exception { - idf.setUrl(PROVIDER_URL); idf.setManagerPassword(MANAGER_PASSWORD); idf.setManagerDn(MANAGER_USER); - idf.afterPropertiesSet(); + DirContext ctx = idf.newInitialDirContext(); // Can't rely on this property being there with embedded server // assertEquals("true",ctx.getEnvironment().get("com.sun.jndi.ldap.connect.pool")); @@ -73,10 +69,8 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testInvalidPasswordCausesBadCredentialsException() throws Exception { - idf.setUrl(PROVIDER_URL); idf.setManagerDn(MANAGER_USER); idf.setManagerPassword("wrongpassword"); - idf.afterPropertiesSet(); try { DirContext ctx = idf.newInitialDirContext(); fail("Authentication with wrong credentials should fail."); @@ -85,8 +79,6 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testConnectionAsSpecificUserSucceeds() throws Exception { - idf.setUrl(PROVIDER_URL); - idf.afterPropertiesSet(); DirContext ctx = idf.newInitialDirContext("uid=Bob,ou=people,dc=acegisecurity,dc=org", "bobspassword"); // We don't want pooling for specific users. @@ -95,7 +87,7 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testEnvironment() { - idf.setUrl("ldap://acegisecurity.org/"); + idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/"); // check basic env Hashtable env = idf.getEnvironment(); @@ -124,20 +116,15 @@ public class InitialDirContextFactoryTests extends AbstractLdapServerTestCase { } public void testBaseDnIsParsedFromCorrectlyFromUrl() throws Exception { - idf.setUrl("ldap://acegisecurity.org/dc=acegisecurity,dc=org"); - idf.afterPropertiesSet(); + idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/dc=acegisecurity,dc=org"); assertEquals("dc=acegisecurity,dc=org", idf.getRootDn()); // Check with an empty root - idf = new DefaultInitialDirContextFactory(); - idf.setUrl("ldap://acegisecurity.org/"); - idf.afterPropertiesSet(); + idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org/"); assertEquals("", idf.getRootDn()); // Empty root without trailing slash - idf = new DefaultInitialDirContextFactory(); - idf.setUrl("ldap://acegisecurity.org"); - idf.afterPropertiesSet(); + idf = new DefaultInitialDirContextFactory("ldap://acegisecurity.org"); assertEquals("", idf.getRootDn()); } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java index 0ff8b43f17..9f4edeeef0 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java @@ -6,11 +6,7 @@ import javax.naming.directory.BasicAttributes; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.BadCredentialsException; -import org.acegisecurity.Authentication; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; -import org.acegisecurity.providers.ldap.authenticator.FilterBasedLdapUserSearch; -import org.acegisecurity.providers.ldap.authenticator.BindAuthenticator; -import org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator; import org.acegisecurity.userdetails.UserDetails; /** @@ -30,11 +26,8 @@ public class LdapAuthenticationProviderTests extends AbstractLdapServerTestCase } public void testNormalUsage() throws Exception { - LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(); - - ldapProvider.setAuthenticator(new MockAuthenticator()); - ldapProvider.setLdapAuthoritiesPopulator(new MockAuthoritiesPopulator()); - ldapProvider.afterPropertiesSet(); + LdapAuthenticationProvider ldapProvider + = new LdapAuthenticationProvider(new MockAuthenticator(), new MockAuthoritiesPopulator()); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("bob","bobspassword"); UserDetails user = ldapProvider.retrieveUser("bob", token); @@ -59,7 +52,7 @@ public class LdapAuthenticationProviderTests extends AbstractLdapServerTestCase BindAuthenticator authenticator = new BindAuthenticator(); //PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(); authenticator.setInitialDirContextFactory(dirCtxFactory); - //authenticator.setUserDnPattern("cn={0},ou=people"); + //authenticator.setUserDnPatterns("cn={0},ou=people"); FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch(); userSearch.setSearchBase("ou=people"); @@ -78,7 +71,7 @@ public class LdapAuthenticationProviderTests extends AbstractLdapServerTestCase populator.setGroupSearchBase("ou=groups"); populator.afterPropertiesSet(); - ldapProvider.setLdapAuthoritiesPopulator(populator); + ldapProvider.setAuthoritiesPopulator(populator); ldapProvider.setAuthenticator(authenticator); Authentication auth = ldapProvider.authenticate(new UsernamePasswordAuthenticationToken("Ben Alex","benspassword")); assertEquals(2, auth.getAuthorities().length); @@ -94,10 +87,10 @@ public class LdapAuthenticationProviderTests extends AbstractLdapServerTestCase class MockAuthenticator implements LdapAuthenticator { Attributes userAttributes = new BasicAttributes("cn","bob"); - public LdapUserDetails authenticate(String username, String password) { + public LdapUserInfo authenticate(String username, String password) { if(username.equals("bob") && password.equals("bobspassword")) { - return new LdapUserDetails("cn=bob,ou=people,dc=acegisecurity,dc=org", userAttributes); + return new LdapUserInfo("cn=bob,ou=people,dc=acegisecurity,dc=org", userAttributes); } throw new BadCredentialsException("Authentication of Bob failed."); } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/LdapTestServer.java b/core/src/test/java/org/acegisecurity/providers/ldap/LdapTestServer.java index 9495f97cda..00152ccfc6 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/LdapTestServer.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/LdapTestServer.java @@ -90,33 +90,6 @@ public class LdapTestServer { } } - -// private void startLdapServer() { -// ApplicationContext factory = new ClassPathXmlApplicationContext( "org/acegisecurity/providers/ldap/apacheds-context.xml"); -// MutableServerStartupConfiguration cfg = ( MutableServerStartupConfiguration ) factory.getBean( "configuration" ); -// ClassPathResource ldifDir = new ClassPathResource("org/acegisecurity/providers/ldap/ldif"); -// -// try { -// cfg.setLdifDirectory(ldifDir.getFile()); -// } catch (IOException e) { -// System.err.println("Failed to set LDIF directory for server"); -// e.printStackTrace(); -// } -// -// Properties env = ( Properties ) factory.getBean( "environment" ); -// -// env.setProperty( Context.PROVIDER_URL, "dc=acegisecurity,dc=org" ); -// env.setProperty( Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName() ); -// env.putAll( cfg.toJndiEnvironment() ); -// -// try { -// serverContext = new InitialDirContext( env ); -// } catch (NamingException e) { -// System.err.println("Failed to start Apache DS"); -// e.printStackTrace(); -// } -// } - private void initTestData() { createOu("people"); createOu("groups"); @@ -125,7 +98,7 @@ public class LdapTestServer { 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("managers","manager", new String[] { developers[0]}); } private void createManagerUser() { @@ -257,5 +230,4 @@ public class LdapTestServer { LdapTestServer server = new LdapTestServer(false); } - } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java index e03f0a1205..9944f2c3fd 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java @@ -1,12 +1,12 @@ package org.acegisecurity.providers.ldap.authenticator; import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase; import org.acegisecurity.BadCredentialsException; /** - * Tests {@link BindAuthenticator}. + * Tests for {@link BindAuthenticator}. * * @author Luke Taylor * @version $Id$ @@ -17,30 +17,26 @@ public class BindAuthenticatorTests extends AbstractLdapServerTestCase { private BindAuthenticator authenticator; public void setUp() throws Exception { - dirCtxFactory = new DefaultInitialDirContextFactory(); + dirCtxFactory = new DefaultInitialDirContextFactory(PROVIDER_URL); dirCtxFactory.setInitialContextFactory(CONTEXT_FACTORY); dirCtxFactory.setExtraEnvVars(EXTRA_ENV); - dirCtxFactory.setUrl(PROVIDER_URL); - dirCtxFactory.afterPropertiesSet(); - authenticator = new BindAuthenticator(); - authenticator.setInitialDirContextFactory(dirCtxFactory); + authenticator = new BindAuthenticator(dirCtxFactory); } public void testUserDnPatternReturnsCorrectDn() throws Exception { - authenticator.setUserDnPattern("cn={0},ou=people"); - assertEquals("cn=Joe,ou=people,"+ ROOT_DN, authenticator.getUserDn("Joe")); + authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"}); + assertEquals("cn=Joe,ou=people,"+ ROOT_DN, authenticator.getUserDns("Joe").get(0)); } public void testAuthenticationWithCorrectPasswordSucceeds() throws Exception { - authenticator.setUserDnPattern("uid={0},ou=people"); - LdapUserDetails user = authenticator.authenticate("bob","bobspassword"); + authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); + LdapUserInfo user = authenticator.authenticate("bob","bobspassword"); } public void testAuthenticationWithWrongPasswordFails() { - BindAuthenticator authenticator = new BindAuthenticator(); + BindAuthenticator authenticator = new BindAuthenticator(dirCtxFactory); - authenticator.setInitialDirContextFactory(dirCtxFactory); - authenticator.setUserDnPattern("uid={0},ou=people"); + authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); try { authenticator.authenticate("bob","wrongpassword"); @@ -50,7 +46,7 @@ public class BindAuthenticatorTests extends AbstractLdapServerTestCase { } public void testAuthenticationWithUserSearch() throws Exception { - LdapUserDetails user = new LdapUserDetails("uid=bob,ou=people," + ROOT_DN, null); + LdapUserInfo user = new LdapUserInfo("uid=bob,ou=people," + ROOT_DN, null); authenticator.setUserSearch(new MockUserSearch(user)); authenticator.afterPropertiesSet(); authenticator.authenticate("bob","bobspassword"); @@ -63,7 +59,7 @@ public class BindAuthenticatorTests extends AbstractLdapServerTestCase { // BindAuthenticator authenticator = new BindAuthenticator(); // // authenticator.setInitialDirContextFactory(dirCtxFactory); -// authenticator.setUserDnPattern("cn={0},ou=people"); +// authenticator.setUserDnPatterns("cn={0},ou=people"); // try { // authenticator.authenticate("Baz","bobspassword"); // fail("Shouldn't be able to bind with invalid username"); diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearchTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearchTests.java index 1c6648ae64..0058928d31 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearchTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/FilterBasedLdapUserSearchTests.java @@ -2,7 +2,7 @@ package org.acegisecurity.providers.ldap.authenticator; import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase; import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.BadCredentialsException; @@ -17,13 +17,11 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase { private FilterBasedLdapUserSearch locator; public void setUp() throws Exception { - dirCtxFactory = new DefaultInitialDirContextFactory(); + dirCtxFactory = new DefaultInitialDirContextFactory(PROVIDER_URL); dirCtxFactory.setInitialContextFactory(CONTEXT_FACTORY); dirCtxFactory.setExtraEnvVars(EXTRA_ENV); - dirCtxFactory.setUrl(PROVIDER_URL); dirCtxFactory.setManagerDn(MANAGER_USER); dirCtxFactory.setManagerPassword(MANAGER_PASSWORD); - dirCtxFactory.afterPropertiesSet(); locator = new FilterBasedLdapUserSearch(); locator.setSearchSubtree(false); locator.setSearchTimeLimit(0); @@ -42,7 +40,7 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase { locator.setSearchBase("ou=people"); locator.setSearchFilter("(uid={0})"); locator.afterPropertiesSet(); - LdapUserDetails bob = locator.searchForUser("bob"); + LdapUserInfo bob = locator.searchForUser("bob"); // name is wrong with embedded apacheDS // assertEquals("uid=bob,ou=people,"+ROOT_DN, bob.getDn()); } @@ -52,7 +50,7 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase { locator.setSearchFilter("(cn={0})"); locator.setSearchSubtree(true); locator.afterPropertiesSet(); - LdapUserDetails ben = locator.searchForUser("Ben Alex"); + LdapUserInfo ben = locator.searchForUser("Ben Alex"); // assertEquals("uid=ben,ou=people,"+ROOT_DN, bob.getDn()); } @@ -82,10 +80,10 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase { public void testExtraFilterPartToExcludeBob() throws Exception { locator.setSearchBase("ou=people"); - locator.setSearchFilter("(&(cn=*)(!(uid={0})))"); + locator.setSearchFilter("(&(cn=*)(!(|(uid={0})(uid=marissa))))"); // Search for bob, get back ben... - LdapUserDetails ben = locator.searchForUser("bob"); + LdapUserInfo ben = locator.searchForUser("bob"); String cn = (String)ben.getAttributes().get("cn").get(); assertEquals("Ben Alex", cn); // assertEquals("uid=ben,ou=people,"+ROOT_DN, ben.getDn()); diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java index 7c2fae09a3..864d790c56 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java @@ -1,19 +1,19 @@ package org.acegisecurity.providers.ldap.authenticator; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; /** * @author Luke Taylor * @version $Id$ */ public class MockUserSearch implements LdapUserSearch { - LdapUserDetails user; + LdapUserInfo user; - public MockUserSearch(LdapUserDetails user) { + public MockUserSearch(LdapUserInfo user) { this.user = user; } - public LdapUserDetails searchForUser(String username) { + public LdapUserInfo searchForUser(String username) { return user; } } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java index 334ceb7879..85375cd2d4 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java @@ -17,11 +17,14 @@ public class PasswordComparisonAuthenticatorMockTests extends MockObjectTestCase public void testLdapCompareIsUsedWhenPasswordIsNotRetrieved() throws Exception { Mock mockCtx = new Mock(DirContext.class); - PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(); - authenticator.setUserDnPattern("cn={0},ou=people"); - authenticator.setInitialDirContextFactory( - new MockInitialDirContextFactory((DirContext)mockCtx.proxy(), - "dc=acegisecurity,dc=org")); + PasswordComparisonAuthenticator authenticator = + new PasswordComparisonAuthenticator(new MockInitialDirContextFactory( + (DirContext)mockCtx.proxy(), + "dc=acegisecurity,dc=org") + ); + + authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"}); + // Get the mock to return an empty attribute set mockCtx.expects(atLeastOnce()).method("getNameInNamespace").will(returnValue("dc=acegisecurity,dc=org")); mockCtx.expects(once()).method("getAttributes").with(eq("cn=Bob,ou=people"), NULL).will(returnValue(new BasicAttributes())); diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java index febecc77c9..5ebe65b4dc 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java @@ -1,15 +1,16 @@ package org.acegisecurity.providers.ldap.authenticator; import org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory; -import org.acegisecurity.providers.ldap.LdapUserDetails; +import org.acegisecurity.providers.ldap.LdapUserInfo; import org.acegisecurity.providers.ldap.AbstractLdapServerTestCase; -import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.userdetails.UsernameNotFoundException; import javax.naming.directory.BasicAttributes; /** + * Tests for {@link PasswordComparisonAuthenticator}. + * * @author Luke Taylor * @version $Id$ */ @@ -18,17 +19,13 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest private PasswordComparisonAuthenticator authenticator; public void setUp() throws Exception { - // Connection information - dirCtxFactory = new DefaultInitialDirContextFactory(); + dirCtxFactory = new DefaultInitialDirContextFactory(PROVIDER_URL); dirCtxFactory.setInitialContextFactory(CONTEXT_FACTORY); dirCtxFactory.setExtraEnvVars(EXTRA_ENV); - dirCtxFactory.setUrl(PROVIDER_URL); dirCtxFactory.setManagerDn(MANAGER_USER); dirCtxFactory.setManagerPassword(MANAGER_PASSWORD); - dirCtxFactory.afterPropertiesSet(); - authenticator = new PasswordComparisonAuthenticator(); - authenticator.setInitialDirContextFactory(dirCtxFactory); - authenticator.setUserDnPattern("uid={0},ou=people"); + authenticator = new PasswordComparisonAuthenticator(dirCtxFactory); + authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); } public void tearDown() { @@ -46,7 +43,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest public void testLdapCompareSucceedsWithShaEncodedPassword() { authenticator = new PasswordComparisonAuthenticator(); authenticator.setInitialDirContextFactory(dirCtxFactory); - authenticator.setUserDnPattern("uid={0},ou=people"); + authenticator.setUserDnPatterns("uid={0},ou=people"); // Don't retrieve the password authenticator.setUserAttributes(new String[] {"cn"}); authenticator.authenticate("ben", "benspassword"); @@ -76,9 +73,8 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest } public void testLocalCompareSucceedsWithShaEncodedPassword() { - authenticator = new PasswordComparisonAuthenticator(); - authenticator.setInitialDirContextFactory(dirCtxFactory); - authenticator.setUserDnPattern("uid={0},ou=people"); + authenticator = new PasswordComparisonAuthenticator(dirCtxFactory); + authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); authenticator.authenticate("ben", "benspassword"); } @@ -91,7 +87,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest } public void testAllAttributesAreRetrivedByDefault() { - LdapUserDetails user = authenticator.authenticate("Bob", "bobspassword"); + LdapUserInfo user = authenticator.authenticate("Bob", "bobspassword"); System.out.println(user.getAttributes().toString()); assertEquals("User should have 5 attributes", 5, user.getAttributes().size()); @@ -100,7 +96,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest public void testOnlySpecifiedAttributesAreRetrieved() throws Exception { authenticator.setUserAttributes(new String[] {"cn", "uid"}); authenticator.setPasswordEncoder(new PlaintextPasswordEncoder()); - LdapUserDetails user = authenticator.authenticate("Bob", "bobspassword"); + LdapUserInfo user = authenticator.authenticate("Bob", "bobspassword"); assertEquals("Should have retrieved 2 attributes (cn, uid)",2, user.getAttributes().size()); assertEquals("Bob Hamilton", user.getAttributes().get("cn").get()); assertEquals("bob", user.getAttributes().get("uid").get()); @@ -120,17 +116,19 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapServerTest */ public void testWithUserSearch() { - LdapUserDetails user = new LdapUserDetails("uid=Bob,ou=people" + ROOT_DN, + authenticator = new PasswordComparisonAuthenticator(dirCtxFactory); + assertTrue("User DN matches shouldn't be available", + authenticator.getUserDns("Bob").isEmpty()); + LdapUserInfo user = new LdapUserInfo("uid=Bob,ou=people" + ROOT_DN, new BasicAttributes("userPassword","bobspassword")); - authenticator.setUserDnPattern(null); - assertNull(authenticator.getUserDnPattern()); - assertNull(authenticator.getUserDn("Bob")); authenticator.setUserSearch(new MockUserSearch(user)); authenticator.authenticate("ShouldntBeUsed","bobspassword"); } public void testFailedSearchGivesUserNotFoundException() throws Exception { - authenticator.setUserDnPattern(null); + authenticator = new PasswordComparisonAuthenticator(dirCtxFactory); + assertTrue("User DN matches shouldn't be available", + authenticator.getUserDns("Bob").isEmpty()); authenticator.setUserSearch(new MockUserSearch(null)); authenticator.afterPropertiesSet(); diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java index 25d11241b2..f0f97206a0 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java @@ -17,31 +17,17 @@ import java.util.HashSet; */ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapServerTestCase { private DefaultInitialDirContextFactory dirCtxFactory; - private DefaultLdapAuthoritiesPopulator populator; public void setUp() { - dirCtxFactory = new DefaultInitialDirContextFactory(); - dirCtxFactory.setUrl(PROVIDER_URL); + dirCtxFactory = new DefaultInitialDirContextFactory(PROVIDER_URL); dirCtxFactory.setInitialContextFactory(CONTEXT_FACTORY); dirCtxFactory.setExtraEnvVars(EXTRA_ENV); dirCtxFactory.setManagerDn(MANAGER_USER); dirCtxFactory.setManagerPassword(MANAGER_PASSWORD); - - populator = new DefaultLdapAuthoritiesPopulator(); - populator.setRolePrefix("ROLE_"); - } - - public void testCtxFactoryMustBeSetIfSearchBaseIsSet() throws Exception { - populator.setGroupSearchBase(""); - - try { - populator.afterPropertiesSet(); - fail("expected exception."); - } catch (IllegalArgumentException expected) { - } } public void testUserAttributeMappingToRoles() { + DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(); populator.setUserRoleAttributes(new String[] {"userRole", "otherUserRole"}); populator.getUserRoleAttributes(); @@ -58,14 +44,13 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapServerTest } public void testGroupSearch() throws Exception { - populator.setInitialDirContextFactory(dirCtxFactory); - populator.setGroupSearchBase("ou=groups"); + DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(dirCtxFactory, "ou=groups"); + populator.setRolePrefix("ROLE_"); populator.setGroupRoleAttribute("ou"); populator.setSearchSubtree(true); populator.setSearchSubtree(false); populator.setConvertToUpperCase(true); populator.setGroupSearchFilter("(member={0})"); - populator.afterPropertiesSet(); GrantedAuthority[] authorities = populator.getGrantedAuthorities("ben", "uid=ben,ou=people,"+ROOT_DN, new BasicAttributes()); assertEquals("Should have 2 roles", 2, authorities.length);