Explorar o código

SEC-607: Deprecated InitialDirContextFactory and replaced it with SpringSecurityContextSource.

Also some refactoring of LdapUserDetailsManager to use a strategy for creating DNs from usernames.
Luke Taylor %!s(int64=18) %!d(string=hai) anos
pai
achega
9e2f372bad
Modificáronse 31 ficheiros con 522 adicións e 384 borrados
  1. 1 1
      core/pom.xml
  2. 30 30
      core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java
  3. 18 1
      core/src/main/java/org/springframework/security/ldap/DefaultInitialDirContextFactory.java
  4. 34 0
      core/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java
  5. 116 0
      core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java
  6. 1 1
      core/src/main/java/org/springframework/security/ldap/InitialDirContextFactory.java
  7. 13 0
      core/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java
  8. 19 31
      core/src/main/java/org/springframework/security/ldap/LdapUtils.java
  9. 26 0
      core/src/main/java/org/springframework/security/ldap/SpringSecurityContextSource.java
  10. 28 31
      core/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java
  11. 12 13
      core/src/main/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearch.java
  12. 12 36
      core/src/main/java/org/springframework/security/providers/ldap/authenticator/AbstractLdapAuthenticator.java
  13. 21 18
      core/src/main/java/org/springframework/security/providers/ldap/authenticator/BindAuthenticator.java
  14. 12 14
      core/src/main/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticator.java
  15. 19 26
      core/src/main/java/org/springframework/security/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java
  16. 41 72
      core/src/main/java/org/springframework/security/userdetails/ldap/LdapUserDetailsManager.java
  17. 5 6
      core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java
  18. 3 3
      core/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java
  19. 15 0
      core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java
  20. 3 0
      core/src/test/java/org/springframework/security/ldap/LdapUtilsTests.java
  21. 11 10
      core/src/test/java/org/springframework/security/ldap/MockSpringSecurityContextSource.java
  22. 13 13
      core/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java
  23. 12 22
      core/src/test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java
  24. 11 13
      core/src/test/java/org/springframework/security/providers/ldap/authenticator/BindAuthenticatorTests.java
  25. 6 6
      core/src/test/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java
  26. 3 4
      core/src/test/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java
  27. 1 2
      core/src/test/java/org/springframework/security/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java
  28. 13 13
      core/src/test/java/org/springframework/security/userdetails/ldap/LdapUserDetailsManagerTests.java
  29. 10 9
      ntlm/src/main/java/org/springframework/security/ui/ntlm/ldap/authenticator/NtlmAwareLdapAuthenticator.java
  30. 8 4
      pom.xml
  31. 5 5
      samples/contacts/src/main/webapp/WEB-INF/applicationContext-acegi-security-ldap.xml

+ 1 - 1
core/pom.xml

@@ -56,7 +56,7 @@
         <dependency>
             <groupId>org.springframework.ldap</groupId>
             <artifactId>spring-ldap</artifactId>
-            <version>1.2</version>
+            <version>1.2.1-SNAPSHOT</version>
             <optional>true</optional>
         </dependency>
         <dependency>

+ 30 - 30
core/src/main/java/org/springframework/security/config/LdapBeanDefinitionParser.java

@@ -1,22 +1,23 @@
 package org.springframework.security.config;
 
-import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.beans.factory.BeanDefinitionStoreException;
-import org.springframework.util.StringUtils;
-import org.springframework.util.Assert;
-import org.springframework.security.ldap.DefaultInitialDirContextFactory;
+import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
 import org.springframework.security.providers.ldap.LdapAuthenticationProvider;
-import org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator;
 import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
+import org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.ldap.core.DirContextAdapter;
-import org.w3c.dom.Element;
-import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
-import org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
+import org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration;
+import org.w3c.dom.Element;
 
 import javax.naming.NamingException;
 import java.util.HashSet;
@@ -63,13 +64,13 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser {
     protected AbstractBeanDefinition parseInternal(Element elt, ParserContext parserContext) {
         String url = elt.getAttribute(URL_ATTRIBUTE);
 
-        RootBeanDefinition initialDirContextFactory;
+        RootBeanDefinition contextSource;
 
         if (!StringUtils.hasText(url)) {
-            initialDirContextFactory = createEmbeddedServer(elt, parserContext);
+            contextSource = createEmbeddedServer(elt, parserContext);
         } else {
-            initialDirContextFactory = new RootBeanDefinition(DefaultInitialDirContextFactory.class);
-            initialDirContextFactory.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
+            contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
+            contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
         }
 
         String managerDn = elt.getAttribute(PRINCIPAL_ATTRIBUTE);
@@ -79,14 +80,14 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser {
             Assert.hasText(managerPassword, "You must specify the " + PASSWORD_ATTRIBUTE +
                     " if you supply a " + managerDn);
 
-            initialDirContextFactory.getPropertyValues().addPropertyValue("managerDn", managerDn);
-            initialDirContextFactory.getPropertyValues().addPropertyValue("managerPassword", managerPassword);
+            contextSource.getPropertyValues().addPropertyValue("userDn", managerDn);
+            contextSource.getPropertyValues().addPropertyValue("password", managerPassword);
         }
 
 
         // TODO: Make these default values for 2.0
-        initialDirContextFactory.getPropertyValues().addPropertyValue("useLdapContext", Boolean.TRUE);
-        initialDirContextFactory.getPropertyValues().addPropertyValue("dirObjectFactory", "org.springframework.ldap.core.support.DefaultDirObjectFactory");
+//        contextSource.getPropertyValues().addPropertyValue("useLdapContext", Boolean.TRUE);
+//        contextSource.getPropertyValues().addPropertyValue("dirObjectFactory", "org.springframework.ldap.core.support.DefaultDirObjectFactory");
 
         String id = elt.getAttribute(ID_ATTRIBUTE);
         String contextSourceId = "contextSource";
@@ -99,13 +100,13 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser {
             logger.warn("Bean already exists with Id '" + contextSourceId + "'");
         }
 
-        parserContext.getRegistry().registerBeanDefinition(contextSourceId, initialDirContextFactory);
+        parserContext.getRegistry().registerBeanDefinition(contextSourceId, contextSource);
 
         RootBeanDefinition bindAuthenticator = new RootBeanDefinition(BindAuthenticator.class);
-        bindAuthenticator.getConstructorArgumentValues().addGenericArgumentValue(initialDirContextFactory);
+        bindAuthenticator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
         bindAuthenticator.getPropertyValues().addPropertyValue("userDnPatterns", new String[] {DEFAULT_DN_PATTERN});
         RootBeanDefinition authoritiesPopulator = new RootBeanDefinition(DefaultLdapAuthoritiesPopulator.class);
-        authoritiesPopulator.getConstructorArgumentValues().addGenericArgumentValue(initialDirContextFactory);
+        authoritiesPopulator.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
         authoritiesPopulator.getConstructorArgumentValues().addGenericArgumentValue(DEFAULT_GROUP_CONTEXT);
 
         RootBeanDefinition ldapProvider = new RootBeanDefinition(LdapAuthenticationProvider.class);
@@ -170,16 +171,15 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser {
         configuration.setExitVmOnShutdown(false);
         configuration.setContextPartitionConfigurations(partitions);
 
-        RootBeanDefinition initialDirContextFactory = new RootBeanDefinition(DefaultInitialDirContextFactory.class);
-        initialDirContextFactory.getConstructorArgumentValues().addIndexedArgumentValue(0,
-                "ldap://127.0.0.1:" + port + "/" + suffix);
+        RootBeanDefinition contextSource = new RootBeanDefinition(DefaultSpringSecurityContextSource.class);
+        contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, "ldap://127.0.0.1:" + port + "/" + suffix);
 
-        initialDirContextFactory.getPropertyValues().addPropertyValue("managerDn", "uid=admin,ou=system");
-        initialDirContextFactory.getPropertyValues().addPropertyValue("managerPassword", "secret");
+        contextSource.getPropertyValues().addPropertyValue("userDn", "uid=admin,ou=system");
+        contextSource.getPropertyValues().addPropertyValue("password", "secret");
 
         RootBeanDefinition apacheDSStartStop = new RootBeanDefinition(ApacheDSContainer.class);
         apacheDSStartStop.getConstructorArgumentValues().addGenericArgumentValue(configuration);
-        apacheDSStartStop.getConstructorArgumentValues().addGenericArgumentValue(initialDirContextFactory);
+        apacheDSStartStop.getConstructorArgumentValues().addGenericArgumentValue(contextSource);
 
         if (parserContext.getRegistry().containsBeanDefinition("_apacheDSStartStopBean")) {
             parserContext.getReaderContext().error("Only one embedded server bean is allowed per application context",
@@ -188,7 +188,7 @@ public class LdapBeanDefinitionParser extends AbstractBeanDefinitionParser {
 
         parserContext.getRegistry().registerBeanDefinition("_apacheDSStartStopBean", apacheDSStartStop);
 
-        return initialDirContextFactory;
+        return contextSource;
     }
 
 

+ 18 - 1
core/src/main/java/org/springframework/security/ldap/DefaultInitialDirContextFactory.java

@@ -28,6 +28,7 @@ import org.springframework.context.support.MessageSourceAccessor;
 import org.springframework.util.Assert;
 import org.springframework.ldap.UncategorizedLdapException;
 import org.springframework.ldap.core.support.DefaultDirObjectFactory;
+import org.springframework.ldap.core.DistinguishedName;
 import org.springframework.dao.DataAccessException;
 
 import java.util.Hashtable;
@@ -64,10 +65,14 @@ import javax.naming.directory.InitialDirContext;
  * @author Luke Taylor
  * @version $Id$
  *
+ *
+ * @deprecated use {@link DefaultSpringSecurityContextSource} instead.
+ *
  * @see <a href="http://java.sun.com/products/jndi/tutorial/ldap/connect/pool.html">The Java tutorial's guide to LDAP
  *      connection pooling</a>
  */
-public class DefaultInitialDirContextFactory implements InitialDirContextFactory, MessageSourceAware {
+public class DefaultInitialDirContextFactory implements InitialDirContextFactory,
+        SpringSecurityContextSource, MessageSourceAware {
     //~ Static fields/initializers =====================================================================================
 
     private static final Log logger = LogFactory.getLog(DefaultInitialDirContextFactory.class);
@@ -344,4 +349,16 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory
     public void setDirObjectFactory(String dirObjectFactory) {
         this.dirObjectFactoryClass = dirObjectFactory;
     }
+
+    public DirContext getReadWriteContext(String userDn, Object credentials) {
+        return newInitialDirContext(userDn, (String) credentials);
+    }
+
+    public DistinguishedName getBaseLdapPath() {
+        return new DistinguishedName(rootDn);
+    }
+
+    public String getBaseLdapPathAsString() {
+        return getBaseLdapPath().toString();
+    }
 }

+ 34 - 0
core/src/main/java/org/springframework/security/ldap/DefaultLdapUsernameToDnMapper.java

@@ -0,0 +1,34 @@
+package org.springframework.security.ldap;
+
+import org.springframework.ldap.core.DistinguishedName;
+
+/**
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {
+    private String userDnBase;
+    private String usernameAttribute;
+
+   /**
+    * This implementation appends a name component to the <tt>userDnBase</tt> context using the
+    * <tt>usernameAttributeName</tt> property. So if the <tt>uid</tt> attribute is used to store the username, and the
+    * base DN is <tt>cn=users</tt> and we are creating a new user called "sam", then the DN will be
+    * <tt>uid=sam,cn=users</tt>.
+    *
+    * @param userDnBase the base name of the DN
+    * @param usernameAttribute the attribute to append for the username component.
+    */
+    public DefaultLdapUsernameToDnMapper(String userDnBase, String usernameAttribute) {
+        this.userDnBase = userDnBase;
+        this.usernameAttribute = usernameAttribute;
+    }
+
+    public DistinguishedName buildDn(String username) {
+        DistinguishedName dn = new DistinguishedName(userDnBase);
+
+        dn.add(usernameAttribute, username);
+
+        return dn;
+    }
+}

+ 116 - 0
core/src/main/java/org/springframework/security/ldap/DefaultSpringSecurityContextSource.java

@@ -0,0 +1,116 @@
+package org.springframework.security.ldap;
+
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.SpringSecurityMessageSource;
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceAware;
+import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.ldap.core.support.LdapContextSource;
+import org.springframework.util.Assert;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * SpringSecurityContextSource implementation which uses Spring LDAP's <tt>LdapContextSource</tt> as a base
+ * class. Intended as a replacement for <tt>DefaultInitialDirContextFactory</tt> from versions of the framework prior
+ * to 2.0.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.0
+ */
+public class DefaultSpringSecurityContextSource extends LdapContextSource implements SpringSecurityContextSource,
+        MessageSourceAware {
+
+    private static final Log logger = LogFactory.getLog(DefaultSpringSecurityContextSource.class);
+    private String rootDn;
+
+    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
+
+    /**
+     * Create and initialize an instance which will connect to the supplied LDAP URL.
+     *
+     * @param providerUrl an LDAP URL of the form <code>ldap://localhost:389/base_dn<code>
+     */
+    public DefaultSpringSecurityContextSource(String providerUrl) {
+        Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied.");
+
+        StringTokenizer st = new StringTokenizer(providerUrl);
+
+        ArrayList urls = new ArrayList();
+
+        // Work out rootDn from the first URL and check that the other URLs (if any) match
+        while (st.hasMoreTokens()) {
+            String url = st.nextToken();
+            String urlRootDn = LdapUtils.parseRootDnFromUrl(url);
+
+            urls.add(url.substring(0, url.lastIndexOf(urlRootDn)));
+
+            logger.info(" URL '" + url + "', root DN is '" + urlRootDn + "'");
+
+            if (rootDn == null) {
+                rootDn = urlRootDn;
+            } else if (!rootDn.equals(urlRootDn)) {
+                throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
+            }
+        }
+
+        super.setUrls((String[]) urls.toArray(new String[urls.size()]));
+        super.setBase(rootDn);
+    }
+
+    public DirContext getReadWriteContext(String userDn, Object credentials) {
+        Hashtable env = new Hashtable(getAnonymousEnv());
+
+        env.put(Context.SECURITY_PRINCIPAL, userDn);
+        env.put(Context.SECURITY_CREDENTIALS, credentials);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Creating context with principal: '" + userDn + "'");
+        }
+
+        try {
+            return createContext(env);
+        } catch (org.springframework.ldap.NamingException e) {
+            if ((e instanceof org.springframework.ldap.AuthenticationException)
+                    || (e instanceof org.springframework.ldap.OperationNotSupportedException)) {
+                throw new BadCredentialsException(
+                        messages.getMessage("DefaultSpringSecurityContextSource.badCredentials", "Bad credentials"), e);
+            }
+            throw e;
+        }
+    }
+
+    /** Copied from parent <tt>AbstractContextSource</tt> as package private */
+    DirContext createContext(Hashtable environment) {
+		DirContext ctx = null;
+
+		try {
+			ctx = getDirContextInstance(environment);
+
+			if (logger.isInfoEnabled()) {
+				Hashtable ctxEnv = ctx.getEnvironment();
+				String ldapUrl = (String) ctxEnv.get(Context.PROVIDER_URL);
+				logger.debug("Got Ldap context on server '" + ldapUrl + "'");
+			}
+
+			return ctx;
+		}
+		catch (NamingException e) {
+			LdapUtils.closeContext(ctx);
+			throw org.springframework.ldap.support.LdapUtils.convertLdapException(e);
+		}
+	}
+
+    public void setMessageSource(MessageSource messageSource) {
+        this.messages = new MessageSourceAccessor(messageSource);
+    }
+}

+ 1 - 1
core/src/main/java/org/springframework/security/ldap/InitialDirContextFactory.java

@@ -28,7 +28,7 @@ import javax.naming.directory.DirContext;
  * @author Luke Taylor
  * @version $Id$
  */
-public interface InitialDirContextFactory extends ContextSource {
+public interface InitialDirContextFactory {
     //~ Methods ========================================================================================================
 
     /**

+ 13 - 0
core/src/main/java/org/springframework/security/ldap/LdapUsernameToDnMapper.java

@@ -0,0 +1,13 @@
+package org.springframework.security.ldap;
+
+import org.springframework.ldap.core.DistinguishedName;
+
+/**
+ * Constructs an Ldap Distinguished Name from a username.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public interface LdapUsernameToDnMapper {
+    DistinguishedName buildDn(String username);
+}

+ 19 - 31
core/src/main/java/org/springframework/security/ldap/LdapUtils.java

@@ -15,17 +15,18 @@
 
 package org.springframework.security.ldap;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.springframework.util.Assert;
 import org.springframework.ldap.core.DirContextAdapter;
 import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.util.Assert;
 
-import java.io.UnsupportedEncodingException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 import javax.naming.Context;
 import javax.naming.NamingException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
 
 
 /**
@@ -156,20 +157,8 @@ public final class LdapUtils {
         String urlRootDn = "";
 
         if (url.startsWith("ldap:") || url.startsWith("ldaps:")) {
-//            URI uri = parseLdapUrl(url);
-
-//            urlRootDn = uri.getPath();
-            // skip past the "://"
-            int colon = url.indexOf(':');
-
-            url = url.substring(colon + 3);
-
-            // Match the slash at the end of the address (if there)
-            int slash = url.indexOf('/');
-
-            if (slash >= 0) {
-                urlRootDn = url.substring(slash);
-            }
+            URI uri = parseLdapUrl(url);
+            urlRootDn = uri.getPath();
         } else {
             // Assume it's an embedded server
             urlRootDn = url;
@@ -182,7 +171,6 @@ public final class LdapUtils {
         return urlRootDn;
     }
 
-    // removed for 1.3 compatibility
     /**
      * Parses the supplied LDAP URL.
      * @param url the URL (e.g. <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt>).
@@ -190,15 +178,15 @@ public final class LdapUtils {
      * @throws IllegalArgumentException if the URL is null, empty or the URI syntax is invalid.
      */
 
-//    private static URI parseLdapUrl(String url) {
-//        Assert.hasLength(url);
-//
-//        try {
-//            return new URI(url);
-//        } catch (URISyntaxException e) {
-//            IllegalArgumentException iae = new IllegalArgumentException("Unable to parse url: " + url);
-//            iae.initCause(e);
-//            throw iae;
-//        }
-//    }
+    private static URI parseLdapUrl(String url) {
+        Assert.hasLength(url);
+
+        try {
+            return new URI(url);
+        } catch (URISyntaxException e) {
+            IllegalArgumentException iae = new IllegalArgumentException("Unable to parse url: " + url);
+            iae.initCause(e);
+            throw iae;
+        }
+    }
 }

+ 26 - 0
core/src/main/java/org/springframework/security/ldap/SpringSecurityContextSource.java

@@ -0,0 +1,26 @@
+package org.springframework.security.ldap;
+
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
+import org.springframework.ldap.core.ContextSource;
+
+import javax.naming.directory.DirContext;
+
+/**
+ * Extension of {@link ContextSource} which allows binding explicitly as a particular user.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.0
+ */
+public interface SpringSecurityContextSource extends BaseLdapPathContextSource {
+
+    /**
+     * Obtains a context using the supplied distinguished name and credentials.
+     *
+     * @param userDn the distinguished name of the user to authenticate as
+     * @param credentials the user's password
+     * @return a context authenticated as the supplied user
+     */
+    DirContext getReadWriteContext(String userDn, Object credentials);
+
+}

+ 28 - 31
core/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java

@@ -18,7 +18,6 @@ package org.springframework.security.ldap;
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
 
 import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
 import org.springframework.ldap.core.ContextExecutor;
 import org.springframework.ldap.core.ContextSource;
 import org.springframework.ldap.core.DirContextAdapter;
@@ -35,10 +34,8 @@ import java.util.List;
 import java.util.ArrayList;
 import java.text.MessageFormat;
 
-import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
-import javax.naming.Context;
 import javax.naming.NameClassPair;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
@@ -97,9 +94,9 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
                 ctls.setReturningAttributes(NO_ATTRS);
                 ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
 
-                String relativeName = LdapUtils.getRelativeName(dn, ctx);
+//                String relativeName = LdapUtils.getRelativeName(dn, ctx);
 
-                NamingEnumeration results = ctx.search(relativeName, comparisonFilter, new Object[] {value}, ctls);
+                NamingEnumeration results = ctx.search(dn, comparisonFilter, new Object[] {value}, ctls);
 
                 return Boolean.valueOf(results.hasMore());
             }
@@ -110,25 +107,25 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
         return matches.booleanValue();
     }
 
-    public boolean nameExists(final String dn) {
-        Boolean exists = (Boolean) executeReadOnly(new ContextExecutor() {
-                public Object executeWithContext(DirContext ctx) throws NamingException {
-                    try {
-                        Object obj = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
-                        if (obj instanceof Context) {
-                            LdapUtils.closeContext((Context) obj);
-                        }
-
-                    } catch (NameNotFoundException nnfe) {
-                        return Boolean.FALSE;
-                    }
-
-                    return Boolean.TRUE;
-                }
-            });
-
-        return exists.booleanValue();
-    }
+//    public boolean nameExists(final String dn) {
+//        Boolean exists = (Boolean) executeReadOnly(new ContextExecutor() {
+//                public Object executeWithContext(DirContext ctx) throws NamingException {
+//                    try {
+//                        Object obj = ctx.lookup(dn);
+//                        if (obj instanceof Context) {
+//                            LdapUtils.closeContext((Context) obj);
+//                        }
+//
+//                    } catch (NameNotFoundException nnfe) {
+//                        return Boolean.FALSE;
+//                    }
+//
+//                    return Boolean.TRUE;
+//                }
+//            });
+//
+//        return exists.booleanValue();
+//    }
 
     /**
      * Composes an object from the attributes of the given DN.
@@ -142,7 +139,7 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
 
         return (DirContextOperations) executeReadOnly(new ContextExecutor() {
                 public Object executeWithContext(DirContext ctx) throws NamingException {
-                    Attributes attrs = ctx.getAttributes(LdapUtils.getRelativeName(dn, ctx), attributesToRetrieve);
+                    Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
 
                     // Object object = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
 
@@ -255,12 +252,12 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
                         dn.append(base);
                     }
 
-                    String nameInNamespace = ctx.getNameInNamespace();
-
-                    if (StringUtils.hasLength(nameInNamespace)) {
-                        dn.append(",");
-                        dn.append(nameInNamespace);
-                    }
+//                    String nameInNamespace = ctx.getNameInNamespace();
+//
+//                    if (StringUtils.hasLength(nameInNamespace)) {
+//                        dn.append(",");
+//                        dn.append(nameInNamespace);
+//                    }
 
                     return new DirContextAdapter(searchResult.getAttributes(), new DistinguishedName(dn.toString()));
                 }

+ 12 - 13
core/src/main/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearch.java

@@ -15,7 +15,6 @@
 
 package org.springframework.security.ldap.search;
 
-import org.springframework.security.ldap.InitialDirContextFactory;
 import org.springframework.security.ldap.SpringSecurityLdapTemplate;
 import org.springframework.security.ldap.LdapUserSearch;
 
@@ -30,6 +29,7 @@ import org.springframework.util.Assert;
 
 import org.springframework.ldap.core.ContextSource;
 import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 
 import javax.naming.directory.SearchControls;
 
@@ -50,7 +50,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
 
     //~ Instance fields ================================================================================================
 
-    private ContextSource initialDirContextFactory;
+    private ContextSource contextSource;
 
     /**
      * The LDAP SearchControls object used for the search. Shared between searches so shouldn't be modified
@@ -58,14 +58,15 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
      */
     private SearchControls searchControls = new SearchControls();
 
-    /** Context name to search in, relative to the root DN of the configured InitialDirContextFactory. */
+    /** Context name to search in, relative to the base of the configured ContextSource. */
     private String searchBase = "";
 
     /**
      * The filter expression used in the user search. This is an LDAP search filter (as defined in 'RFC 2254')
      * with optional arguments. See the documentation for the <tt>search</tt> methods in {@link
-     * javax.naming.directory.DirContext DirContext} for more information.<p>In this case, the username is the
-     * only parameter.</p>
+     * javax.naming.directory.DirContext DirContext} for more information.
+     *
+     * <p>In this case, the username is the only parameter.</p>
      *  Possible examples are:
      *  <ul>
      *      <li>(uid={0}) - this would search for a username match on the uid attribute.</li>
@@ -75,19 +76,18 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
 
     //~ Constructors ===================================================================================================
 
-    public FilterBasedLdapUserSearch(String searchBase, String searchFilter,
-            InitialDirContextFactory initialDirContextFactory) {
-        Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null");
+    public FilterBasedLdapUserSearch(String searchBase, String searchFilter, BaseLdapPathContextSource contextSource) {
+        Assert.notNull(contextSource, "contextSource must not be null");
         Assert.notNull(searchFilter, "searchFilter must not be null.");
         Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
 
         this.searchFilter = searchFilter;
-        this.initialDirContextFactory = initialDirContextFactory;
+        this.contextSource = contextSource;
         this.searchBase = searchBase;
 
         if (searchBase.length() == 0) {
             logger.info("SearchBase not set. Searches will be performed from the root: "
-                + initialDirContextFactory.getRootDn());
+                + contextSource.getBaseLdapPath());
         }
     }
 
@@ -104,11 +104,10 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
      */
     public DirContextOperations searchForUser(String username) {
         if (logger.isDebugEnabled()) {
-            logger.debug("Searching for user '" + username + "', with user search "
-                + this.toString());
+            logger.debug("Searching for user '" + username + "', with user search " + this.toString());
         }
 
-        SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(initialDirContextFactory);
+        SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
 
         template.setSearchControls(searchControls);
 

+ 12 - 36
core/src/main/java/org/springframework/security/providers/ldap/authenticator/AbstractLdapAuthenticator.java

@@ -16,13 +16,13 @@
 package org.springframework.security.providers.ldap.authenticator;
 
 import org.springframework.security.SpringSecurityMessageSource;
-import org.springframework.security.ldap.InitialDirContextFactory;
 import org.springframework.security.ldap.LdapUserSearch;
 import org.springframework.security.providers.ldap.LdapAuthenticator;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.MessageSource;
 import org.springframework.context.MessageSourceAware;
 import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.ldap.core.ContextSource;
 import org.springframework.util.Assert;
 
 import java.text.MessageFormat;
@@ -40,18 +40,12 @@ import java.util.List;
 public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, InitializingBean, MessageSourceAware {
     //~ Instance fields ================================================================================================
 
-    private InitialDirContextFactory initialDirContextFactory;
+    private ContextSource contextSource;
 
     /** Optional search object which can be used to locate a user when a simple DN match isn't sufficient */
     private LdapUserSearch userSearch;
     protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
 
-    /**
-     * The suffix to be added to the DN patterns, worked out internally from the root DN of the configured
-     * InitialDirContextFactory.
-     */
-    private String dnSuffix = "";
-
     /** The attributes which will be retrieved from the directory. Null means all attributes */
     private String[] userAttributes = null;
 
@@ -62,12 +56,13 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
     //~ Constructors ===================================================================================================
 
     /**
-     * Create an initialized instance to the {@link InitialDirContextFactory} provided.
+     * Create an initialized instance with the {@link ContextSource} provided.
      *
-     * @param initialDirContextFactory
+     * @param contextSource
      */
-    public AbstractLdapAuthenticator(InitialDirContextFactory initialDirContextFactory) {
-        this.setInitialDirContextFactory(initialDirContextFactory);
+    public AbstractLdapAuthenticator(ContextSource contextSource) {
+        Assert.notNull(contextSource, "contextSource must not be null.");
+        this.contextSource = contextSource;
     }
 
     //~ Methods ========================================================================================================
@@ -77,24 +72,8 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
                 "Either an LdapUserSearch or DN pattern (or both) must be supplied.");
     }
 
-    /**
-     * Set the {@link InitialDirContextFactory} and initialize this instance from its data.
-     *
-     * @param initialDirContextFactory
-     */
-    private void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) {
-        Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null.");
-        this.initialDirContextFactory = initialDirContextFactory;
-
-        String rootDn = initialDirContextFactory.getRootDn();
-
-        if (rootDn.length() > 0) {
-            dnSuffix = "," + rootDn;
-        }
-    }
-
-    protected InitialDirContextFactory getInitialDirContextFactory() {
-        return initialDirContextFactory;
+    protected ContextSource getContextSource() {
+        return contextSource;
     }
 
     public String[] getUserAttributes() {
@@ -102,9 +81,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
     }
 
     /**
-     * Builds list of possible DNs for the user, worked out from the <tt>userDnPatterns</tt> property. The
-     * returned value includes the root DN of the provider URL used to configure the
-     * <tt>InitialDirContextfactory</tt>.
+     * Builds list of possible DNs for the user, worked out from the <tt>userDnPatterns</tt> property.
      *
      * @param username the user's login name
      *
@@ -120,7 +97,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
 
         synchronized (userDnFormat) {
             for (int i = 0; i < userDnFormat.length; i++) {
-                userDns.add(userDnFormat[i].format(args) + dnSuffix);
+                userDns.add(userDnFormat[i].format(args));
             }
         }
 
@@ -150,8 +127,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
      * Sets the pattern which will be used to supply a DN for the user. The pattern should be the name relative
      * to the root DN. The pattern argument {0} will contain the username. An example would be "cn={0},ou=people".
      *
-     * @param dnPattern the array of patterns which will be tried when obtaining a username
-     * to a DN.
+     * @param dnPattern the array of patterns which will be tried when converting a username to a DN.
      */
     public void setUserDnPatterns(String[] dnPattern) {
         Assert.notNull(dnPattern, "The array of DN patterns cannot be set to null");

+ 21 - 18
core/src/main/java/org/springframework/security/providers/ldap/authenticator/BindAuthenticator.java

@@ -15,20 +15,20 @@
 
 package org.springframework.security.providers.ldap.authenticator;
 
-import org.springframework.security.BadCredentialsException;
 import org.springframework.security.Authentication;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
-import org.springframework.security.ldap.InitialDirContextFactory;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.ldap.SpringSecurityContextSource;
 import org.springframework.security.ldap.SpringSecurityLdapTemplate;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.dao.DataAccessException;
 import org.springframework.ldap.core.ContextSource;
 import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
 import org.springframework.util.Assert;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import javax.naming.directory.DirContext;
 import java.util.Iterator;
 
@@ -49,12 +49,14 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
     //~ Constructors ===================================================================================================
 
     /**
-     * Create an initialized instance to the {@link InitialDirContextFactory} provided.
+     * Create an initialized instance using the {@link SpringSecurityContextSource} provided.
+     *
+     * @param contextSource the SpringSecurityContextSource instance against which bind operations will be
+     * performed.
      *
-     * @param initialDirContextFactory
      */
-    public BindAuthenticator(InitialDirContextFactory initialDirContextFactory) {
-        super(initialDirContextFactory);
+    public BindAuthenticator(SpringSecurityContextSource contextSource) {
+        super(contextSource);
     }
 
     //~ Methods ========================================================================================================
@@ -91,7 +93,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
 
     private DirContextOperations bindWithDn(String userDn, String username, String password) {
         SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(
-                new BindWithSpecificDnContextSource(getInitialDirContextFactory(), userDn, password));
+                new BindWithSpecificDnContextSource((SpringSecurityContextSource) getContextSource(), userDn, password));
 
         try {
             return template.retrieveEntry(userDn, getUserAttributes());
@@ -110,25 +112,26 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
      * Allows subclasses to inspect the exception thrown by an attempt to bind with a particular DN.
      * The default implementation just reports the failure to the debug log.
      */
-    void handleBindException(String userDn, String username, Throwable cause) {
+    protected void handleBindException(String userDn, String username, Throwable cause) {
         if (logger.isDebugEnabled()) {
             logger.debug("Failed to bind as " + userDn + ": " + cause);
         }
     }
 
     private class BindWithSpecificDnContextSource implements ContextSource {
-        private InitialDirContextFactory ctxFactory;
-        private String userDn;
+        private SpringSecurityContextSource ctxFactory;
+        DistinguishedName userDn;
         private String password;
 
-        public BindWithSpecificDnContextSource(InitialDirContextFactory ctxFactory, String userDn, String password) {
+        public BindWithSpecificDnContextSource(SpringSecurityContextSource ctxFactory, String userDn, String password) {
             this.ctxFactory = ctxFactory;
-            this.userDn = userDn;
+            this.userDn = new DistinguishedName(userDn);
+            this.userDn.prepend(ctxFactory.getBaseLdapPath());
             this.password = password;
         }
 
         public DirContext getReadOnlyContext() throws DataAccessException {
-            return ctxFactory.newInitialDirContext(userDn, password);
+            return ctxFactory.getReadWriteContext(userDn.toString(), password);
         }
 
         public DirContext getReadWriteContext() throws DataAccessException {

+ 12 - 14
core/src/main/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticator.java

@@ -15,24 +15,21 @@
 
 package org.springframework.security.providers.ldap.authenticator;
 
-import org.springframework.security.BadCredentialsException;
 import org.springframework.security.Authentication;
-
-import org.springframework.security.ldap.InitialDirContextFactory;
-import org.springframework.security.ldap.SpringSecurityLdapTemplate;
+import org.springframework.security.BadCredentialsException;
 import org.springframework.security.ldap.LdapUtils;
-
-import org.springframework.security.providers.encoding.PasswordEncoder;
+import org.springframework.security.ldap.SpringSecurityLdapTemplate;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
+import org.springframework.security.providers.encoding.PasswordEncoder;
 import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.ldap.NameNotFoundException;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
+import org.springframework.util.Assert;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import org.springframework.util.Assert;
-import org.springframework.ldap.core.DirContextOperations;
-
 import java.util.Iterator;
 
 
@@ -65,8 +62,8 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
 
     //~ Constructors ===================================================================================================
 
-    public PasswordComparisonAuthenticator(InitialDirContextFactory initialDirContextFactory) {
-        super(initialDirContextFactory);
+    public PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource) {
+        super(contextSource);
     }
 
     //~ Methods ========================================================================================================
@@ -82,13 +79,14 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
 
         Iterator dns = getUserDns(username).iterator();
 
-        SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getInitialDirContextFactory());
+        SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());
 
         while (dns.hasNext() && user == null) {
             final String userDn = (String) dns.next();
 
-            if (ldapTemplate.nameExists(userDn)) {
+            try {
                 user = ldapTemplate.retrieveEntry(userDn, getUserAttributes());
+            } catch (NameNotFoundException ignore) {
             }
         }
 

+ 19 - 26
core/src/main/java/org/springframework/security/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java

@@ -17,24 +17,20 @@ package org.springframework.security.providers.ldap.populator;
 
 import org.springframework.security.GrantedAuthority;
 import org.springframework.security.GrantedAuthorityImpl;
-
-import org.springframework.security.ldap.InitialDirContextFactory;
 import org.springframework.security.ldap.SpringSecurityLdapTemplate;
-
 import org.springframework.security.providers.ldap.LdapAuthoritiesPopulator;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.util.Assert;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import org.springframework.util.Assert;
-import org.springframework.ldap.core.DirContextOperations;
-
+import javax.naming.directory.SearchControls;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 
-import javax.naming.directory.SearchControls;
-
 
 /**
  * The default strategy for obtaining user role information from the directory.
@@ -73,7 +69,7 @@ import javax.naming.directory.SearchControls;
  * <pre>
  * &lt;bean id="ldapAuthoritiesPopulator"
  *       class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
- *   &lt;constructor-arg>&lt;ref local="initialDirContextFactory"/>&lt;/constructor-arg>
+ *   &lt;constructor-arg>&lt;ref local="contextSource"/>&lt;/constructor-arg>
  *   &lt;constructor-arg>&lt;value>ou=groups&lt;/value>&lt;/constructor-arg>
  *   &lt;property name="groupRoleAttribute">&lt;value>ou&lt;/value>&lt;/property>
  * &lt;!-- the following properties are shown with their default values -->
@@ -104,10 +100,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
      */
     private GrantedAuthority defaultRole = null;
 
-    /**
-     * An initial context factory is only required if searching for groups is required.
-     */
-    private InitialDirContextFactory initialDirContextFactory = null;
+    private ContextSource contextSource = null;
+
     private SpringSecurityLdapTemplate ldapTemplate;
 
     /**
@@ -145,12 +139,12 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
      * Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
      * set as a property.
      *
-     * @param initialDirContextFactory supplies the contexts used to search for user roles.
+     * @param contextSource 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.
      */
-    public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
-        this.setInitialDirContextFactory(initialDirContextFactory);
+    public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
+        this.setContextSource(contextSource);
         this.setGroupSearchBase(groupSearchBase);
     }
 
@@ -232,20 +226,20 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
         return authorities;
     }
 
-    protected InitialDirContextFactory getInitialDirContextFactory() {
-        return initialDirContextFactory;
+    protected ContextSource getContextSource() {
+        return contextSource;
     }
 
     /**
-     * Set the {@link InitialDirContextFactory}
+     * Set the {@link ContextSource}
      *
-     * @param initialDirContextFactory supplies the contexts used to search for user roles.
+     * @param contextSource supplies the contexts used to search for user roles.
      */
-    private void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) {
-        Assert.notNull(initialDirContextFactory, "InitialDirContextFactory must not be null");
-        this.initialDirContextFactory = initialDirContextFactory;
+    private void setContextSource(ContextSource contextSource) {
+        Assert.notNull(contextSource, "contextSource must not be null");
+        this.contextSource = contextSource;
 
-        ldapTemplate = new SpringSecurityLdapTemplate(initialDirContextFactory);
+        ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
         ldapTemplate.setSearchControls(searchControls);
     }
 
@@ -259,8 +253,7 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
         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());
+            logger.info("groupSearchBase is empty. Searches will be performed from the context source base");
         }
     }
 

+ 41 - 72
core/src/main/java/org/springframework/security/userdetails/ldap/LdapUserDetailsManager.java

@@ -14,42 +14,46 @@
  */
 package org.springframework.security.userdetails.ldap;
 
-import org.springframework.security.userdetails.UserDetails;
-import org.springframework.security.userdetails.UsernameNotFoundException;
-import org.springframework.security.userdetails.UserDetailsManager;
-import org.springframework.security.ldap.LdapUtils;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
 import org.springframework.security.Authentication;
 import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
 import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.ldap.LdapUtils;
+import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UserDetailsManager;
+import org.springframework.security.userdetails.UsernameNotFoundException;
 import org.springframework.dao.DataAccessException;
-import org.springframework.util.Assert;
-import org.springframework.ldap.core.DistinguishedName;
 import org.springframework.ldap.core.AttributesMapper;
-import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.AttributesMapperCallbackHandler;
+import org.springframework.ldap.core.ContextExecutor;
 import org.springframework.ldap.core.ContextSource;
 import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.ContextExecutor;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
 import org.springframework.ldap.core.SearchExecutor;
-import org.springframework.ldap.core.AttributesMapperCallbackHandler;
+import org.springframework.util.Assert;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import javax.naming.ldap.LdapContext;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
 import javax.naming.Context;
-import javax.naming.Name;
 import javax.naming.NameNotFoundException;
-import javax.naming.directory.Attributes;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.ModificationItem;
-import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.SearchControls;
-
-import java.util.*;
+import javax.naming.ldap.LdapContext;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
 
 /**
  * An Ldap implementation of UserDetailsManager.
@@ -71,13 +75,15 @@ import java.util.*;
 public class LdapUserDetailsManager implements UserDetailsManager {
     private final Log logger = LogFactory.getLog(LdapUserDetailsManager.class);
 
-    /** The DN under which users entries are stored */
-    private DistinguishedName userDnBase = new DistinguishedName("cn=users");
+    /**
+     * The strategy for mapping usernames to LDAP distinguished names.
+     * This will be used when building DNs for creating new users etc.
+     */
+    LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid");
+
     /** The DN under which groups are stored */
     private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups");
 
-    /** The attribute which contains the user login name, and which is used by default to build the DN for new users */
-    private String usernameAttributeName = "uid";
     /** Password attribute name */
     private String passwordAttributeName = "userPassword";
 
@@ -120,7 +126,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
     }
 
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
-        DistinguishedName dn = buildDn(username);
+        DistinguishedName dn = usernameMapper.buildDn(username);
         GrantedAuthority[] authorities = getUserAuthorities(dn, username);
 
         logger.debug("Loading user '"+ username + "' with DN '" + dn + "'");
@@ -130,12 +136,12 @@ public class LdapUserDetailsManager implements UserDetailsManager {
         return userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
     }
 
-    private UserContext loadUserAsContext(final DistinguishedName dn, final String username) {
-        return (UserContext) template.executeReadOnly(new ContextExecutor() {
+    private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) {
+        return (DirContextAdapter) template.executeReadOnly(new ContextExecutor() {
             public Object executeWithContext(DirContext ctx) throws NamingException {
                 try {
                     Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
-                    return new UserContext(attrs, LdapUtils.getFullDn(dn, ctx));
+                    return new DirContextAdapter(attrs, LdapUtils.getFullDn(dn, ctx));
                 } catch(NameNotFoundException notFound) {
                     throw new UsernameNotFoundException("User " + username + " not found", notFound);
                 }
@@ -163,7 +169,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
 
         logger.debug("Changing password for user '"+ username);
 
-        final DistinguishedName dn = buildDn(username);
+        final DistinguishedName dn = usernameMapper.buildDn(username);
         final ModificationItem[] passwordChange = new ModificationItem[] {
                 new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword))
         };
@@ -227,7 +233,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
     public void createUser(UserDetails user) {
         DirContextAdapter ctx = new DirContextAdapter();
         copyToContext(user, ctx);
-        DistinguishedName dn = buildDn(user.getUsername());
+        DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
         // Check for any existing authorities which might be set for this DN
         GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
 
@@ -244,13 +250,13 @@ public class LdapUserDetailsManager implements UserDetailsManager {
 
     public void updateUser(UserDetails user) {
 //        Assert.notNull(attributesToRetrieve, "Configuration must specify a list of attributes in order to use update.");
-        DistinguishedName dn = buildDn(user.getUsername());
+        DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
 
         logger.debug("Updating user '"+ user.getUsername() + "' with DN '" + dn + "'");
 
         GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
 
-        UserContext ctx = loadUserAsContext(dn, user.getUsername());
+        DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());
         ctx.setUpdateMode(true);
         copyToContext(user, ctx);
 
@@ -275,13 +281,13 @@ public class LdapUserDetailsManager implements UserDetailsManager {
     }
 
     public void deleteUser(String username) {
-        DistinguishedName dn = buildDn(username);
+        DistinguishedName dn = usernameMapper.buildDn(username);
         removeAuthorities(dn, getUserAuthorities(dn, username));
         template.unbind(dn);
     }
 
     public boolean userExists(String username) {
-        DistinguishedName dn = buildDn(username);
+        DistinguishedName dn = usernameMapper.buildDn(username);
 
         try {
             Object obj = template.lookup(dn);
@@ -294,25 +300,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
         }
     }
 
-    /**
-     * Constructs a DN from a username.
-     * <p>
-     * The default implementation appends a name component to the <tt>userDnBase</tt> context using the
-     * <tt>usernameAttributeName</tt> property. So if the <tt>uid</tt> attribute is used to store the username, and the
-     * base DN is <tt>cn=users</tt> and we are creating a new user called "sam", then the DN will be
-     * <tt>uid=sam,cn=users</tt>.
-     *
-     * @param username the user name used for authentication.
-     * @return the corresponding DN, relative to the base context.
-     */
-    protected DistinguishedName buildDn(String username) {
-        DistinguishedName dn = new DistinguishedName(userDnBase);
-
-        dn.add(usernameAttributeName, username);
-
-        return dn;
-    }
-
     /**
      * Creates a DN from a group name.
      *
@@ -365,8 +352,8 @@ public class LdapUserDetailsManager implements UserDetailsManager {
         return group;
     }
 
-    public void setUsernameAttributeName(String usernameAttributeName) {
-        this.usernameAttributeName = usernameAttributeName;
+    public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+        this.usernameMapper = usernameMapper;
     }
 
     public void setPasswordAttributeName(String passwordAttributeName) {
@@ -381,10 +368,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
         this.groupRoleAttributeName = groupRoleAttributeName;
     }
 
-    public void setUserDnBase(String userDnBase) {
-        this.userDnBase = new DistinguishedName(userDnBase);
-    }
-
     public void setAttributesToRetrieve(String[] attributesToRetrieve) {
         Assert.notNull(attributesToRetrieve);
         this.attributesToRetrieve = attributesToRetrieve;
@@ -411,18 +394,4 @@ public class LdapUserDetailsManager implements UserDetailsManager {
     public void setRoleMapper(AttributesMapper roleMapper) {
         this.roleMapper = roleMapper;
     }
-
-    /**
-     * This class allows us to set the <tt>updateMode</tt> property of DirContextAdapter when updating existing users.
-     * TODO: No longer needed as of Ldap 1.2.
-     */
-    private static class UserContext extends DirContextAdapter {
-        public UserContext(Attributes pAttrs, Name dn) {
-            super(pAttrs, dn);
-        }
-
-        public void setUpdateMode(boolean mode) {
-            super.setUpdateMode(mode);
-        }
-    }
 }

+ 5 - 6
core/src/test/java/org/springframework/security/config/LdapBeanDefinitionParserTests.java

@@ -1,12 +1,12 @@
 package org.springframework.security.config;
 
-import org.springframework.security.ldap.InitialDirContextFactory;
-import org.springframework.ldap.core.LdapTemplate;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 
+import org.junit.AfterClass;
 import static org.junit.Assert.*;
 import org.junit.BeforeClass;
-import org.junit.AfterClass;
 import org.junit.Test;
 
 
@@ -32,14 +32,13 @@ public class LdapBeanDefinitionParserTests {
 
     @Test
     public void testContextContainsExpectedBeansAndData() {
-        InitialDirContextFactory idcf = (InitialDirContextFactory) appContext.getBean("contextSource");
+        BaseLdapPathContextSource idcf = (BaseLdapPathContextSource) appContext.getBean("contextSource");
 
-        assertEquals("dc=springframework,dc=org", idcf.getRootDn());
+//        assertEquals("dc=springframework, dc=org", idcf.getBaseLdapPathAsString());
 
         // Check data is loaded
         LdapTemplate template = new LdapTemplate(idcf);
 
         template.lookup("uid=ben,ou=people");
-
     }
 }

+ 3 - 3
core/src/test/java/org/springframework/security/ldap/AbstractLdapIntegrationTests.java

@@ -107,11 +107,11 @@ public abstract class AbstractLdapIntegrationTests {
             loader.execute();
         } finally {
             ctx.close();
-        }        
+        }
     }
 
-    public ContextSource getContextSource() {
-        return (ContextSource) appContext.getBean("contextSource");
+    public SpringSecurityContextSource getContextSource() {
+        return (SpringSecurityContextSource) appContext.getBean("contextSource");
     }
 
     /**

+ 15 - 0
core/src/test/java/org/springframework/security/ldap/DefaultSpringSecurityContextSourceTests.java

@@ -0,0 +1,15 @@
+package org.springframework.security.ldap;
+
+import org.junit.Test;
+
+/**
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class DefaultSpringSecurityContextSourceTests {
+
+    @Test
+    public void instantiationSucceeds() {
+        new DefaultSpringSecurityContextSource("ldap://blah:789/dc=springframework,dc=org");
+    }
+}

+ 3 - 0
core/src/test/java/org/springframework/security/ldap/LdapUtilsTests.java

@@ -71,6 +71,7 @@ public class LdapUtilsTests extends MockObjectTestCase {
 
     public void testRootDnsAreParsedFromUrlsCorrectly() {
         assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine"));
+        assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine:11389"));        
         assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/"));
         assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/"));
         assertEquals("dc=springframework,dc=org",
@@ -80,5 +81,7 @@ public class LdapUtilsTests extends MockObjectTestCase {
             LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/dc=springframework,dc=org"));
         assertEquals("dc=springframework,dc=org/ou=blah",
             LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk/dc=springframework,dc=org/ou=blah"));
+        assertEquals("dc=springframework,dc=org/ou=blah",
+            LdapUtils.parseRootDnFromUrl("ldap://monkeymachine.co.uk:389/dc=springframework,dc=org/ou=blah"));
     }
 }

+ 11 - 10
core/src/test/java/org/springframework/security/ldap/MockInitialDirContextFactory.java → core/src/test/java/org/springframework/security/ldap/MockSpringSecurityContextSource.java

@@ -16,6 +16,7 @@
 package org.springframework.security.ldap;
 
 import org.springframework.dao.DataAccessException;
+import org.springframework.ldap.core.DistinguishedName;
 
 import javax.naming.directory.DirContext;
 
@@ -25,7 +26,7 @@ import javax.naming.directory.DirContext;
  * @author Luke Taylor
  * @version $Id$
  */
-public class MockInitialDirContextFactory implements InitialDirContextFactory {
+public class MockSpringSecurityContextSource implements SpringSecurityContextSource {
     //~ Instance fields ================================================================================================
 
     private DirContext ctx;
@@ -33,30 +34,30 @@ public class MockInitialDirContextFactory implements InitialDirContextFactory {
 
     //~ Constructors ===================================================================================================
 
-    public MockInitialDirContextFactory(DirContext ctx, String baseDn) {
+    public MockSpringSecurityContextSource(DirContext ctx, String baseDn) {
         this.baseDn = baseDn;
         this.ctx = ctx;
     }
 
     //~ Methods ========================================================================================================
 
-    public String getRootDn() {
-        return baseDn;
+    public DirContext getReadOnlyContext() throws DataAccessException {
+        return ctx;
     }
 
-    public DirContext newInitialDirContext() {
+    public DirContext getReadWriteContext() throws DataAccessException {
         return ctx;
     }
 
-    public DirContext newInitialDirContext(String username, String password) {
+    public DirContext getReadWriteContext(String userDn, Object credentials) {
         return ctx;
     }
 
-    public DirContext getReadOnlyContext() throws DataAccessException {
-        return ctx;
+    public DistinguishedName getBaseLdapPath() {
+        return new DistinguishedName(baseDn);
     }
 
-    public DirContext getReadWriteContext() throws DataAccessException {
-        return ctx;
+    public String getBaseLdapPathAsString() {
+        return getBaseLdapPath().toString();
     }
 }

+ 13 - 13
core/src/test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateTests.java

@@ -44,33 +44,33 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
 
     @Test
     public void testCompareOfCorrectValueSucceeds() {
-        assertTrue(template.compare("uid=bob,ou=people,dc=springframework,dc=org", "uid", "bob"));
+        assertTrue(template.compare("uid=bob,ou=people", "uid", "bob"));
     }
 
     @Test
     public void testCompareOfCorrectByteValueSucceeds() {
-        assertTrue(template.compare("uid=bob,ou=people,dc=springframework,dc=org", "userPassword", LdapUtils.getUtf8Bytes("bobspassword")));
+        assertTrue(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("bobspassword")));
     }
 
     @Test
     public void testCompareOfWrongByteValueFails() {
-        assertFalse(template.compare("uid=bob,ou=people,dc=springframework,dc=org", "userPassword", LdapUtils.getUtf8Bytes("wrongvalue")));
+        assertFalse(template.compare("uid=bob,ou=people", "userPassword", LdapUtils.getUtf8Bytes("wrongvalue")));
     }
 
     @Test
     public void testCompareOfWrongValueFails() {
-        assertFalse(template.compare("uid=bob,ou=people,dc=springframework,dc=org", "uid", "wrongvalue"));
+        assertFalse(template.compare("uid=bob,ou=people", "uid", "wrongvalue"));
     }
 
-    @Test
-    public void testNameExistsForInValidNameFails() {
-        assertFalse(template.nameExists("ou=doesntexist,dc=springframework,dc=org"));
-    }
-
-    @Test
-    public void testNameExistsForValidNameSucceeds() {
-        assertTrue(template.nameExists("ou=groups,dc=springframework,dc=org"));
-    }
+//    @Test
+//    public void testNameExistsForInValidNameFails() {
+//        assertFalse(template.nameExists("ou=doesntexist,dc=springframework,dc=org"));
+//    }
+//
+//    @Test
+//    public void testNameExistsForValidNameSucceeds() {
+//        assertTrue(template.nameExists("ou=groups,dc=springframework,dc=org"));
+//    }
 
     @Test
     public void testNamingExceptionIsTranslatedCorrectly() {

+ 12 - 22
core/src/test/java/org/springframework/security/ldap/search/FilterBasedLdapUserSearchTests.java

@@ -15,16 +15,15 @@
 
 package org.springframework.security.ldap.search;
 
-import org.springframework.security.ldap.DefaultInitialDirContextFactory;
 import org.springframework.security.ldap.AbstractLdapIntegrationTests;
-
 import org.springframework.security.userdetails.UsernameNotFoundException;
-
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.ldap.core.DirContextOperations;
-import org.junit.Test;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
 
 /**
  * Tests for FilterBasedLdapUserSearch.
@@ -35,13 +34,13 @@ import static org.junit.Assert.*;
 public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests {
     //~ Instance fields ================================================================================================
 
-    private DefaultInitialDirContextFactory dirCtxFactory;
+    private BaseLdapPathContextSource dirCtxFactory;
 
     //~ Methods ========================================================================================================
 
     public void onSetUp() throws Exception {
         super.onSetUp();
-        dirCtxFactory = (DefaultInitialDirContextFactory) getContextSource();
+        dirCtxFactory = getContextSource();
     }
 
     @Test
@@ -54,8 +53,7 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests
         DirContextOperations bob = locator.searchForUser("bob");
         assertEquals("bob", bob.getStringAttribute("uid"));
 
-        // name is wrong with embedded apacheDS
-//        assertEquals("uid=bob,ou=people,dc=springframework,dc=org", bob.getDn());
+        assertEquals(new DistinguishedName("uid=bob,ou=people"), bob.getDn());
     }
 
     // Try some funny business with filters.
@@ -71,24 +69,16 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests
 //        assertEquals("uid=ben,ou=people,"+ROOT_DN, ben.getDn());
     }
 
-    @Test
+    @Test(expected=IncorrectResultSizeDataAccessException.class)
     public void testFailsOnMultipleMatches() {
         FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(cn=*)", dirCtxFactory);
-
-        try {
-            locator.searchForUser("Ignored");
-            fail("Expected exception for multiple search matches.");
-        } catch (IncorrectResultSizeDataAccessException expected) {}
+        locator.searchForUser("Ignored");
     }
 
-    @Test
+    @Test(expected=UsernameNotFoundException.class)
     public void testSearchForInvalidUserFails() {
         FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", dirCtxFactory);
-
-        try {
-            locator.searchForUser("Joe");
-            fail("Expected UsernameNotFoundException for non-existent user.");
-        } catch (UsernameNotFoundException expected) {}
+        locator.searchForUser("Joe");
     }
 
     @Test
@@ -100,7 +90,7 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests
         DirContextOperations ben = locator.searchForUser("Ben Alex");
         assertEquals("ben", ben.getStringAttribute("uid"));
 
-//        assertEquals("uid=ben,ou=people,dc=springframework,dc=org", ben.getDn());
+        assertEquals(new DistinguishedName("uid=ben,ou=people"), ben.getDn());
     }
 
     // TODO: Add test with non-uid username

+ 11 - 13
core/src/test/java/org/springframework/security/providers/ldap/authenticator/BindAuthenticatorTests.java

@@ -15,19 +15,17 @@
 
 package org.springframework.security.providers.ldap.authenticator;
 
-import org.springframework.security.SpringSecurityMessageSource;
-import org.springframework.security.BadCredentialsException;
 import org.springframework.security.Authentication;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.SpringSecurityMessageSource;
 import org.springframework.security.ldap.AbstractLdapIntegrationTests;
-import org.springframework.security.ldap.InitialDirContextFactory;
-
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DistinguishedName;
 import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import org.junit.Test;
 
 /**
@@ -41,16 +39,16 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
 
     private BindAuthenticator authenticator;
     private Authentication bob;
-    private Authentication ben;
+//    private Authentication ben;
 
 
     //~ Methods ========================================================================================================
 
     public void onSetUp() {
-        authenticator = new BindAuthenticator((InitialDirContextFactory) getContextSource());
+        authenticator = new BindAuthenticator(getContextSource());
         authenticator.setMessageSource(new SpringSecurityMessageSource());
         bob = new UsernamePasswordAuthenticationToken("bob", "bobspassword");
-        ben = new UsernamePasswordAuthenticationToken("ben", "benspassword");
+//        ben = new UsernamePasswordAuthenticationToken("ben", "benspassword");
 
     }
 
@@ -74,7 +72,7 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
 
     @Test
     public void testAuthenticationWithUserSearch() throws Exception {
-        DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=bob,ou=people,dc=springframework,dc=org"));
+        DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=bob,ou=people"));
 
         authenticator.setUserSearch(new MockUserSearch(ctx));
         authenticator.afterPropertiesSet();
@@ -94,6 +92,6 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
     @Test
     public void testUserDnPatternReturnsCorrectDn() {
         authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"});
-        assertEquals("cn=Joe,ou=people," + ((InitialDirContextFactory)getContextSource()).getRootDn(), authenticator.getUserDns("Joe").get(0));
+        assertEquals("cn=Joe,ou=people", authenticator.getUserDns("Joe").get(0));
     }
 }

+ 6 - 6
core/src/test/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticatorMockTests.java

@@ -15,7 +15,7 @@
 
 package org.springframework.security.providers.ldap.authenticator;
 
-import org.springframework.security.ldap.MockInitialDirContextFactory;
+import org.springframework.security.ldap.MockSpringSecurityContextSource;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 
 import org.jmock.Mock;
@@ -40,15 +40,15 @@ public class PasswordComparisonAuthenticatorMockTests extends MockObjectTestCase
         BasicAttributes attrs = new BasicAttributes();
         attrs.put(new BasicAttribute("uid", "bob"));
 
-        PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(new MockInitialDirContextFactory(
-                    (DirContext) mockCtx.proxy(), "dc=springframework,dc=org"));
+        PasswordComparisonAuthenticator authenticator = new PasswordComparisonAuthenticator(new MockSpringSecurityContextSource(
+                    (DirContext) mockCtx.proxy(), ""));
 
         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=springframework,dc=org"));
-        mockCtx.expects(once()).method("lookup").with(eq("cn=Bob, ou=people")).will(returnValue(true));
-        mockCtx.expects(once()).method("getAttributes").with(eq("cn=Bob, ou=people"), NULL)
+//        mockCtx.expects(atLeastOnce()).method("getNameInNamespace").will(returnValue("dc=springframework,dc=org"));
+//        mockCtx.expects(once()).method("lookup").with(eq("cn=Bob,ou=people")).will(returnValue(true));
+        mockCtx.expects(once()).method("getAttributes").with(eq("cn=Bob,ou=people"), NULL)
                .will(returnValue(attrs));
 
         // Setup a single return value (i.e. success)

+ 3 - 4
core/src/test/java/org/springframework/security/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java

@@ -19,7 +19,6 @@ import org.springframework.security.BadCredentialsException;
 import org.springframework.security.Authentication;
 
 import org.springframework.security.ldap.AbstractLdapIntegrationTests;
-import org.springframework.security.ldap.InitialDirContextFactory;
 
 import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
@@ -49,7 +48,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio
 
     public void onSetUp() throws Exception {
         super.onSetUp();
-        authenticator = new PasswordComparisonAuthenticator((InitialDirContextFactory) getContextSource());
+        authenticator = new PasswordComparisonAuthenticator(getContextSource());
         authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"});
         bob = new UsernamePasswordAuthenticationToken("bob", "bobspassword");
         ben = new UsernamePasswordAuthenticationToken("ben", "benspassword");
@@ -64,7 +63,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio
 
     @Test
     public void testFailedSearchGivesUserNotFoundException() throws Exception {
-        authenticator = new PasswordComparisonAuthenticator((InitialDirContextFactory) getContextSource());
+        authenticator = new PasswordComparisonAuthenticator(getContextSource());
         assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty());
         authenticator.setUserSearch(new MockUserSearch(null));
         authenticator.afterPropertiesSet();
@@ -164,7 +163,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio
 
     @Test
     public void testWithUserSearch() {
-        authenticator = new PasswordComparisonAuthenticator((InitialDirContextFactory) getContextSource());
+        authenticator = new PasswordComparisonAuthenticator(getContextSource());
         assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty());
 
         DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=Bob,ou=people,dc=springframework,dc=org"));

+ 1 - 2
core/src/test/java/org/springframework/security/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java

@@ -18,7 +18,6 @@ package org.springframework.security.providers.ldap.populator;
 import org.springframework.security.GrantedAuthority;
 
 import org.springframework.security.ldap.AbstractLdapIntegrationTests;
-import org.springframework.security.ldap.InitialDirContextFactory;
 
 import org.springframework.ldap.core.DirContextAdapter;
 import org.springframework.ldap.core.DistinguishedName;
@@ -42,7 +41,7 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio
     public void onSetUp() throws Exception {
         super.onSetUp();
 
-        populator = new DefaultLdapAuthoritiesPopulator((InitialDirContextFactory) getContextSource(), "ou=groups");
+        populator = new DefaultLdapAuthoritiesPopulator(getContextSource(), "ou=groups");
 
     }
 

+ 13 - 13
core/src/test/java/org/springframework/security/userdetails/ldap/LdapUserDetailsManagerTests.java

@@ -14,20 +14,20 @@
  */
 package org.springframework.security.userdetails.ldap;
 
-import org.springframework.security.ldap.SpringSecurityLdapTemplate;
-import org.springframework.security.ldap.AbstractLdapIntegrationTests;
-import org.springframework.security.userdetails.UserDetails;
-import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.BadCredentialsException;
 import org.springframework.security.GrantedAuthority;
 import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.context.SecurityContextHolder;
-
+import org.springframework.security.ldap.AbstractLdapIntegrationTests;
+import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
+import org.springframework.security.ldap.SpringSecurityLdapTemplate;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
 import org.springframework.ldap.core.DirContextAdapter;
 
-import static org.junit.Assert.*;
 import org.junit.After;
+import static org.junit.Assert.*;
 import org.junit.Test;
 
 /**
@@ -63,7 +63,7 @@ public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
         group.setAttributeValue("cn", "acrobats");
         template.bind("cn=acrobats,ou=testgroups", group, null);
 
-        mgr.setUserDnBase("ou=testpeople");
+        mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=testpeople","uid"));
         mgr.setGroupSearchBase("ou=testgroups");
         mgr.setGroupRoleAttributeName("cn");
         mgr.setGroupMemberAttributeName("member");
@@ -88,7 +88,7 @@ public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
 
     @Test
     public void testLoadUserByUsernameReturnsCorrectData() {
-        mgr.setUserDnBase("ou=people");
+        mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=people","uid"));
         mgr.setGroupSearchBase("ou=groups");
         UserDetails bob = mgr.loadUserByUsername("bob");
         assertEquals("bob", bob.getUsername());
@@ -111,7 +111,7 @@ public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
 
     @Test
     public void testUserExistsReturnsTrueForValidUser() {
-        mgr.setUserDnBase("ou=people");
+        mgr.setUsernameMapper(new DefaultLdapUsernameToDnMapper("ou=people","uid"));
         assertTrue(mgr.userExists("bob"));
     }
 
@@ -156,7 +156,7 @@ public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
         }
 
         // Check that no authorities are left
-        assertEquals(0, mgr.getUserAuthorities(mgr.buildDn("don"), "don").length);
+        assertEquals(0, mgr.getUserAuthorities(mgr.usernameMapper.buildDn("don"), "don").length);
     }
 
     @Test
@@ -175,7 +175,7 @@ public class LdapUserDetailsManagerTests extends AbstractLdapIntegrationTests {
 
         mgr.changePassword("yossarianspassword", "yossariansnewpassword");
 
-        assertTrue(template.compare("uid=johnyossarian,ou=testpeople,dc=springframework,dc=org",
+        assertTrue(template.compare("uid=johnyossarian,ou=testpeople",
                 "userPassword", "yossariansnewpassword"));
     }
 

+ 10 - 9
ntlm/src/main/java/org/springframework/security/ui/ntlm/ldap/authenticator/NtlmAwareLdapAuthenticator.java

@@ -3,18 +3,19 @@
  */
 package org.springframework.security.ui.ntlm.ldap.authenticator;
 
-import java.util.Iterator;
-
-import org.springframework.security.BadCredentialsException;
 import org.springframework.security.Authentication;
-import org.springframework.security.ldap.InitialDirContextFactory;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.ldap.SpringSecurityContextSource;
 import org.springframework.security.ldap.SpringSecurityLdapTemplate;
 import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
 import org.springframework.security.ui.ntlm.NtlmUsernamePasswordAuthenticationToken;
+import org.springframework.ldap.NameNotFoundException;
+import org.springframework.ldap.core.DirContextOperations;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.springframework.ldap.core.DirContextOperations;
-import org.springframework.ldap.NameNotFoundException;
+
+import java.util.Iterator;
 
 /**
  * Loads the UserDetails if authentication was already performed by NTLM (indicated by the type of authentication
@@ -31,8 +32,8 @@ public class NtlmAwareLdapAuthenticator extends BindAuthenticator {
 
     //~ Constructors ===================================================================================================
 
-	public NtlmAwareLdapAuthenticator(InitialDirContextFactory initialDirContextFactory) {
-		super(initialDirContextFactory);
+	public NtlmAwareLdapAuthenticator(SpringSecurityContextSource contextSource) {
+		super(contextSource);
 	}
 
     //~ Methods ========================================================================================================
@@ -41,7 +42,7 @@ public class NtlmAwareLdapAuthenticator extends BindAuthenticator {
      * Loads the user context information without binding.
 	 */
 	protected DirContextOperations loadUser(String aUserDn, String aUserName) {
-		SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(getInitialDirContextFactory());
+		SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(getContextSource());
 
 		try {
 			DirContextOperations user =  template.retrieveEntry(aUserDn, getUserAttributes());

+ 8 - 4
pom.xml

@@ -76,15 +76,19 @@
 
     <repositories>
         <repository>
-            <id>sourceforge.net</id>
+            <id>acegisnapshots</id>
             <name>Acegi snapshot repository</name>
-            <url>
-                http://acegisecurity.sourceforge.net/repository/snapshots
-            </url>
+            <url>http://acegisecurity.sourceforge.net/repository/snapshots</url>
             <releases>
                 <enabled>false</enabled>
             </releases>
         </repository>
+        <!-- TODO: Added for spring-ldap-1.2.1-SNAPSHOT -->
+        <repository>
+            <id>acegirepo</id>
+            <name>Acegi maven repository</name>
+            <url>http://acegisecurity.sourceforge.net/maven</url>
+        </repository>
         <repository>
             <id>spring-milestone</id>
             <name>Springframework Maven Milestone Repository</name>

+ 5 - 5
samples/contacts/src/main/webapp/WEB-INF/applicationContext-acegi-security-ldap.xml

@@ -36,22 +36,22 @@
       </property>
    </bean>
 
-   <bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory">
+   <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
      <constructor-arg value="ldap://monkeymachine.co.uk:389/dc=springframework,dc=org"/>
-     <property name="managerDn" value="cn=manager,dc=springframework,dc=org" />
-     <property name="managerPassword" value="acegisecurity"/>
+     <property name="userDn" value="cn=manager,dc=springframework,dc=org" />
+     <property name="password" value="acegisecurity"/>
    </bean>
 
    <bean id="ldapAuthenticationProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
      <constructor-arg>
        <bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
-          <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
+          <constructor-arg><ref local="contextSource"/></constructor-arg>
           <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property>
        </bean>
      </constructor-arg>
      <constructor-arg>
        <bean class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
-          <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
+          <constructor-arg><ref local="contextSource"/></constructor-arg>
           <constructor-arg><value>ou=groups</value></constructor-arg>
           <property name="groupRoleAttribute"><value>ou</value></property>
        </bean>