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

Also some refactoring of LdapUserDetailsManager to use a strategy for creating DNs from usernames.
This commit is contained in:
Luke Taylor 2007-11-20 20:54:48 +00:00
parent 79adaa2d3d
commit 9e2f372bad
31 changed files with 527 additions and 389 deletions

View File

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

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -15,7 +15,6 @@
package org.springframework.security.ldap.search; package org.springframework.security.ldap.search;
import org.springframework.security.ldap.InitialDirContextFactory;
import org.springframework.security.ldap.SpringSecurityLdapTemplate; import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.ldap.LdapUserSearch; 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.ContextSource;
import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
@ -50,7 +50,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private ContextSource initialDirContextFactory; private ContextSource contextSource;
/** /**
* The LDAP SearchControls object used for the search. Shared between searches so shouldn't be modified * 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(); 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 = ""; private String searchBase = "";
/** /**
* The filter expression used in the user search. This is an LDAP search filter (as defined in 'RFC 2254') * 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 * 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 * javax.naming.directory.DirContext DirContext} for more information.
* only parameter.</p> *
* <p>In this case, the username is the only parameter.</p>
* Possible examples are: * Possible examples are:
* <ul> * <ul>
* <li>(uid={0}) - this would search for a username match on the uid attribute.</li> * <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 =================================================================================================== //~ Constructors ===================================================================================================
public FilterBasedLdapUserSearch(String searchBase, String searchFilter, public FilterBasedLdapUserSearch(String searchBase, String searchFilter, BaseLdapPathContextSource contextSource) {
InitialDirContextFactory initialDirContextFactory) { Assert.notNull(contextSource, "contextSource must not be null");
Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null");
Assert.notNull(searchFilter, "searchFilter 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)."); Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
this.searchFilter = searchFilter; this.searchFilter = searchFilter;
this.initialDirContextFactory = initialDirContextFactory; this.contextSource = contextSource;
this.searchBase = searchBase; this.searchBase = searchBase;
if (searchBase.length() == 0) { if (searchBase.length() == 0) {
logger.info("SearchBase not set. Searches will be performed from the root: " 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) { public DirContextOperations searchForUser(String username) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Searching for user '" + username + "', with user search " logger.debug("Searching for user '" + username + "', with user search " + this.toString());
+ this.toString());
} }
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(initialDirContextFactory); SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
template.setSearchControls(searchControls); template.setSearchControls(searchControls);

View File

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

View File

@ -15,20 +15,20 @@
package org.springframework.security.providers.ldap.authenticator; package org.springframework.security.providers.ldap.authenticator;
import org.springframework.security.BadCredentialsException;
import org.springframework.security.Authentication; import org.springframework.security.Authentication;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken; import org.springframework.security.BadCredentialsException;
import org.springframework.security.ldap.SpringSecurityContextSource;
import org.springframework.security.ldap.InitialDirContextFactory;
import org.springframework.security.ldap.SpringSecurityLdapTemplate; import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.ldap.core.ContextSource; import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.naming.directory.DirContext; import javax.naming.directory.DirContext;
import java.util.Iterator; import java.util.Iterator;
@ -49,12 +49,14 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
//~ Constructors =================================================================================================== //~ 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) { public BindAuthenticator(SpringSecurityContextSource contextSource) {
super(initialDirContextFactory); super(contextSource);
} }
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
@ -91,7 +93,7 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
private DirContextOperations bindWithDn(String userDn, String username, String password) { private DirContextOperations bindWithDn(String userDn, String username, String password) {
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate( SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(
new BindWithSpecificDnContextSource(getInitialDirContextFactory(), userDn, password)); new BindWithSpecificDnContextSource((SpringSecurityContextSource) getContextSource(), userDn, password));
try { try {
return template.retrieveEntry(userDn, getUserAttributes()); 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. * 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. * 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()) { if (logger.isDebugEnabled()) {
logger.debug("Failed to bind as " + userDn + ": " + cause); logger.debug("Failed to bind as " + userDn + ": " + cause);
} }
} }
private class BindWithSpecificDnContextSource implements ContextSource { private class BindWithSpecificDnContextSource implements ContextSource {
private InitialDirContextFactory ctxFactory; private SpringSecurityContextSource ctxFactory;
private String userDn; DistinguishedName userDn;
private String password; private String password;
public BindWithSpecificDnContextSource(InitialDirContextFactory ctxFactory, String userDn, String password) { public BindWithSpecificDnContextSource(SpringSecurityContextSource ctxFactory, String userDn, String password) {
this.ctxFactory = ctxFactory; this.ctxFactory = ctxFactory;
this.userDn = userDn; this.userDn = new DistinguishedName(userDn);
this.userDn.prepend(ctxFactory.getBaseLdapPath());
this.password = password; this.password = password;
} }
public DirContext getReadOnlyContext() throws DataAccessException { public DirContext getReadOnlyContext() throws DataAccessException {
return ctxFactory.newInitialDirContext(userDn, password); return ctxFactory.getReadWriteContext(userDn.toString(), password);
} }
public DirContext getReadWriteContext() throws DataAccessException { public DirContext getReadWriteContext() throws DataAccessException {

View File

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

View File

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

View File

@ -14,42 +14,46 @@
*/ */
package org.springframework.security.userdetails.ldap; 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.Authentication;
import org.springframework.security.BadCredentialsException; 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.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.dao.DataAccessException;
import org.springframework.util.Assert;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.AttributesMapper; 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.ContextSource;
import org.springframework.ldap.core.DirContextAdapter; 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.SearchExecutor;
import org.springframework.ldap.core.AttributesMapperCallbackHandler; import org.springframework.util.Assert;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import javax.naming.ldap.LdapContext; import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.directory.Attributes;
import javax.naming.directory.Attribute; import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext; import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem; import javax.naming.directory.ModificationItem;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
import javax.naming.ldap.LdapContext;
import java.util.*; import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
/** /**
* An Ldap implementation of UserDetailsManager. * An Ldap implementation of UserDetailsManager.
@ -71,13 +75,15 @@ import java.util.*;
public class LdapUserDetailsManager implements UserDetailsManager { public class LdapUserDetailsManager implements UserDetailsManager {
private final Log logger = LogFactory.getLog(LdapUserDetailsManager.class); 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 */ /** The DN under which groups are stored */
private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups"); 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 */ /** Password attribute name */
private String passwordAttributeName = "userPassword"; private String passwordAttributeName = "userPassword";
@ -120,7 +126,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
} }
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
DistinguishedName dn = buildDn(username); DistinguishedName dn = usernameMapper.buildDn(username);
GrantedAuthority[] authorities = getUserAuthorities(dn, username); GrantedAuthority[] authorities = getUserAuthorities(dn, username);
logger.debug("Loading user '"+ username + "' with DN '" + dn + "'"); logger.debug("Loading user '"+ username + "' with DN '" + dn + "'");
@ -130,12 +136,12 @@ public class LdapUserDetailsManager implements UserDetailsManager {
return userDetailsMapper.mapUserFromContext(userCtx, username, authorities); return userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
} }
private UserContext loadUserAsContext(final DistinguishedName dn, final String username) { private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) {
return (UserContext) template.executeReadOnly(new ContextExecutor() { return (DirContextAdapter) template.executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx) throws NamingException { public Object executeWithContext(DirContext ctx) throws NamingException {
try { try {
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve); 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) { } catch(NameNotFoundException notFound) {
throw new UsernameNotFoundException("User " + username + " not found", 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); logger.debug("Changing password for user '"+ username);
final DistinguishedName dn = buildDn(username); final DistinguishedName dn = usernameMapper.buildDn(username);
final ModificationItem[] passwordChange = new ModificationItem[] { final ModificationItem[] passwordChange = new ModificationItem[] {
new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword)) new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword))
}; };
@ -227,7 +233,7 @@ public class LdapUserDetailsManager implements UserDetailsManager {
public void createUser(UserDetails user) { public void createUser(UserDetails user) {
DirContextAdapter ctx = new DirContextAdapter(); DirContextAdapter ctx = new DirContextAdapter();
copyToContext(user, ctx); 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 // Check for any existing authorities which might be set for this DN
GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername()); GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
@ -244,13 +250,13 @@ public class LdapUserDetailsManager implements UserDetailsManager {
public void updateUser(UserDetails user) { public void updateUser(UserDetails user) {
// Assert.notNull(attributesToRetrieve, "Configuration must specify a list of attributes in order to use update."); // 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 + "'"); logger.debug("Updating user '"+ user.getUsername() + "' with DN '" + dn + "'");
GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername()); GrantedAuthority[] authorities = getUserAuthorities(dn, user.getUsername());
UserContext ctx = loadUserAsContext(dn, user.getUsername()); DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());
ctx.setUpdateMode(true); ctx.setUpdateMode(true);
copyToContext(user, ctx); copyToContext(user, ctx);
@ -275,13 +281,13 @@ public class LdapUserDetailsManager implements UserDetailsManager {
} }
public void deleteUser(String username) { public void deleteUser(String username) {
DistinguishedName dn = buildDn(username); DistinguishedName dn = usernameMapper.buildDn(username);
removeAuthorities(dn, getUserAuthorities(dn, username)); removeAuthorities(dn, getUserAuthorities(dn, username));
template.unbind(dn); template.unbind(dn);
} }
public boolean userExists(String username) { public boolean userExists(String username) {
DistinguishedName dn = buildDn(username); DistinguishedName dn = usernameMapper.buildDn(username);
try { try {
Object obj = template.lookup(dn); 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. * Creates a DN from a group name.
* *
@ -365,8 +352,8 @@ public class LdapUserDetailsManager implements UserDetailsManager {
return group; return group;
} }
public void setUsernameAttributeName(String usernameAttributeName) { public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
this.usernameAttributeName = usernameAttributeName; this.usernameMapper = usernameMapper;
} }
public void setPasswordAttributeName(String passwordAttributeName) { public void setPasswordAttributeName(String passwordAttributeName) {
@ -381,10 +368,6 @@ public class LdapUserDetailsManager implements UserDetailsManager {
this.groupRoleAttributeName = groupRoleAttributeName; this.groupRoleAttributeName = groupRoleAttributeName;
} }
public void setUserDnBase(String userDnBase) {
this.userDnBase = new DistinguishedName(userDnBase);
}
public void setAttributesToRetrieve(String[] attributesToRetrieve) { public void setAttributesToRetrieve(String[] attributesToRetrieve) {
Assert.notNull(attributesToRetrieve); Assert.notNull(attributesToRetrieve);
this.attributesToRetrieve = attributesToRetrieve; this.attributesToRetrieve = attributesToRetrieve;
@ -411,18 +394,4 @@ public class LdapUserDetailsManager implements UserDetailsManager {
public void setRoleMapper(AttributesMapper roleMapper) { public void setRoleMapper(AttributesMapper roleMapper) {
this.roleMapper = 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);
}
}
} }

View File

@ -1,12 +1,12 @@
package org.springframework.security.config; 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.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 static org.junit.Assert.*;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
@ -32,14 +32,13 @@ public class LdapBeanDefinitionParserTests {
@Test @Test
public void testContextContainsExpectedBeansAndData() { 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 // Check data is loaded
LdapTemplate template = new LdapTemplate(idcf); LdapTemplate template = new LdapTemplate(idcf);
template.lookup("uid=ben,ou=people"); template.lookup("uid=ben,ou=people");
} }
} }

View File

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

View File

@ -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");
}
}

View File

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

View File

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

View File

@ -44,33 +44,33 @@ public class SpringSecurityLdapTemplateTests extends AbstractLdapIntegrationTest
@Test @Test
public void testCompareOfCorrectValueSucceeds() { 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 @Test
public void testCompareOfCorrectByteValueSucceeds() { 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 @Test
public void testCompareOfWrongByteValueFails() { 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 @Test
public void testCompareOfWrongValueFails() { 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 // @Test
public void testNameExistsForInValidNameFails() { // public void testNameExistsForInValidNameFails() {
assertFalse(template.nameExists("ou=doesntexist,dc=springframework,dc=org")); // assertFalse(template.nameExists("ou=doesntexist,dc=springframework,dc=org"));
} // }
//
@Test // @Test
public void testNameExistsForValidNameSucceeds() { // public void testNameExistsForValidNameSucceeds() {
assertTrue(template.nameExists("ou=groups,dc=springframework,dc=org")); // assertTrue(template.nameExists("ou=groups,dc=springframework,dc=org"));
} // }
@Test @Test
public void testNamingExceptionIsTranslatedCorrectly() { public void testNamingExceptionIsTranslatedCorrectly() {

View File

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

View File

@ -15,19 +15,17 @@
package org.springframework.security.providers.ldap.authenticator; 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.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.AbstractLdapIntegrationTests;
import org.springframework.security.ldap.InitialDirContextFactory; import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.DirContextOperations; 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; import org.junit.Test;
/** /**
@ -41,16 +39,16 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
private BindAuthenticator authenticator; private BindAuthenticator authenticator;
private Authentication bob; private Authentication bob;
private Authentication ben; // private Authentication ben;
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
public void onSetUp() { public void onSetUp() {
authenticator = new BindAuthenticator((InitialDirContextFactory) getContextSource()); authenticator = new BindAuthenticator(getContextSource());
authenticator.setMessageSource(new SpringSecurityMessageSource()); authenticator.setMessageSource(new SpringSecurityMessageSource());
bob = new UsernamePasswordAuthenticationToken("bob", "bobspassword"); 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 @Test
public void testAuthenticationWithUserSearch() throws Exception { 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.setUserSearch(new MockUserSearch(ctx));
authenticator.afterPropertiesSet(); authenticator.afterPropertiesSet();
@ -94,6 +92,6 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests {
@Test @Test
public void testUserDnPatternReturnsCorrectDn() { public void testUserDnPatternReturnsCorrectDn() {
authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"}); 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));
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -3,18 +3,19 @@
*/ */
package org.springframework.security.ui.ntlm.ldap.authenticator; 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.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.ldap.SpringSecurityLdapTemplate;
import org.springframework.security.providers.ldap.authenticator.BindAuthenticator; import org.springframework.security.providers.ldap.authenticator.BindAuthenticator;
import org.springframework.security.ui.ntlm.NtlmUsernamePasswordAuthenticationToken; 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.Log;
import org.apache.commons.logging.LogFactory; 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 * 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 =================================================================================================== //~ Constructors ===================================================================================================
public NtlmAwareLdapAuthenticator(InitialDirContextFactory initialDirContextFactory) { public NtlmAwareLdapAuthenticator(SpringSecurityContextSource contextSource) {
super(initialDirContextFactory); super(contextSource);
} }
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
@ -41,7 +42,7 @@ public class NtlmAwareLdapAuthenticator extends BindAuthenticator {
* Loads the user context information without binding. * Loads the user context information without binding.
*/ */
protected DirContextOperations loadUser(String aUserDn, String aUserName) { protected DirContextOperations loadUser(String aUserDn, String aUserName) {
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(getInitialDirContextFactory()); SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(getContextSource());
try { try {
DirContextOperations user = template.retrieveEntry(aUserDn, getUserAttributes()); DirContextOperations user = template.retrieveEntry(aUserDn, getUserAttributes());

12
pom.xml
View File

@ -76,15 +76,19 @@
<repositories> <repositories>
<repository> <repository>
<id>sourceforge.net</id> <id>acegisnapshots</id>
<name>Acegi snapshot repository</name> <name>Acegi snapshot repository</name>
<url> <url>http://acegisecurity.sourceforge.net/repository/snapshots</url>
http://acegisecurity.sourceforge.net/repository/snapshots
</url>
<releases> <releases>
<enabled>false</enabled> <enabled>false</enabled>
</releases> </releases>
</repository> </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> <repository>
<id>spring-milestone</id> <id>spring-milestone</id>
<name>Springframework Maven Milestone Repository</name> <name>Springframework Maven Milestone Repository</name>

View File

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