SEC-449: Mostly changes to aid moving towards compatibility with spring-ldap.

This commit is contained in:
Luke Taylor 2007-09-07 19:55:45 +00:00
parent 98a91f5ac1
commit 9b71b5aa00
16 changed files with 236 additions and 112 deletions

View File

@ -26,6 +26,10 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.ldap.ContextSource;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.support.DefaultDirObjectFactory;
import org.springframework.dao.DataAccessException;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
@ -64,7 +68,7 @@ import javax.naming.directory.InitialDirContext;
* @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, MessageSourceAware, ContextSource {
//~ Static fields/initializers ===================================================================================== //~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(DefaultInitialDirContextFactory.class); private static final Log logger = LogFactory.getLog(DefaultInitialDirContextFactory.class);
@ -86,6 +90,8 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory
*/ */
private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
private String dirObjectFactoryClass = DefaultDirObjectFactory.class.getName();
/** /**
* If your LDAP server does not allow anonymous searches then you will need to provide a "manager" user's * If your LDAP server does not allow anonymous searches then you will need to provide a "manager" user's
* DN to log in with. * DN to log in with.
@ -186,11 +192,11 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory
} }
if (ne instanceof CommunicationException) { if (ne instanceof CommunicationException) {
throw new LdapDataAccessException(messages.getMessage( throw new UncategorizedLdapException(messages.getMessage(
"DefaultIntitalDirContextFactory.communicationFailure", "Unable to connect to LDAP server"), ne); "DefaultIntitalDirContextFactory.communicationFailure", "Unable to connect to LDAP server"), ne);
} }
throw new LdapDataAccessException(messages.getMessage( throw new UncategorizedLdapException(messages.getMessage(
"DefaultIntitalDirContextFactory.unexpectedException", "DefaultIntitalDirContextFactory.unexpectedException",
"Failed to obtain InitialDirContext due to unexpected exception"), ne); "Failed to obtain InitialDirContext due to unexpected exception"), ne);
} }
@ -258,9 +264,23 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory
env.put(Context.SECURITY_PRINCIPAL, username); env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password); env.put(Context.SECURITY_CREDENTIALS, password);
if(dirObjectFactoryClass != null) {
env.put(Context.OBJECT_FACTORIES, dirObjectFactoryClass);
}
return connect(env); return connect(env);
} }
/** Spring LDAP <tt>ContextSource</tt> method */
public DirContext getReadOnlyContext() throws DataAccessException {
return newInitialDirContext();
}
/** Spring LDAP <tt>ContextSource</tt> method */
public DirContext getReadWriteContext() throws DataAccessException {
return newInitialDirContext();
}
public void setAuthenticationType(String authenticationType) { public void setAuthenticationType(String authenticationType) {
Assert.hasLength(authenticationType, "LDAP Authentication type must not be empty or null"); Assert.hasLength(authenticationType, "LDAP Authentication type must not be empty or null");
this.authenticationType = authenticationType; this.authenticationType = authenticationType;
@ -321,4 +341,8 @@ public class DefaultInitialDirContextFactory implements InitialDirContextFactory
public void setUseLdapContext(boolean useLdapContext) { public void setUseLdapContext(boolean useLdapContext) {
this.useLdapContext = useLdapContext; this.useLdapContext = useLdapContext;
} }
public void setDirObjectFactory(String dirObjectFactory) {
this.dirObjectFactoryClass = dirObjectFactory;
}
} }

View File

@ -15,6 +15,8 @@
package org.acegisecurity.ldap; package org.acegisecurity.ldap;
import org.springframework.ldap.ContextSource;
import javax.naming.directory.DirContext; import javax.naming.directory.DirContext;
@ -26,7 +28,7 @@ import javax.naming.directory.DirContext;
* @author Luke Taylor * @author Luke Taylor
* @version $Id$ * @version $Id$
*/ */
public interface InitialDirContextFactory { public interface InitialDirContextFactory extends ContextSource {
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
/** /**

View File

@ -22,6 +22,9 @@ import javax.naming.directory.DirContext;
/** /**
* Callback object for use with LdapTemplate. * Callback object for use with LdapTemplate.
* *
* @deprecated use spring-ldap ContextExecutor instead.
* @TODO: Delete before 2.0 release
*
* @author Ben Alex * @author Ben Alex
*/ */
public interface LdapCallback { public interface LdapCallback {

View File

@ -22,6 +22,7 @@ import org.springframework.dao.DataAccessException;
* Used to wrap unexpected NamingExceptions while accessing the LDAP server or for other LDAP-related data problems * Used to wrap unexpected NamingExceptions while accessing the LDAP server or for other LDAP-related data problems
* such as data we can't handle. * such as data we can't handle.
* *
* @deprecated Spring LDAP classes are now used instead.
* @author Luke Taylor * @author Luke Taylor
* @version $Id$ * @version $Id$
*/ */

View File

@ -24,6 +24,7 @@ import javax.naming.directory.Attributes;
* a set of attributes retrieved from a directory entry. * a set of attributes retrieved from a directory entry.
* *
* @author Luke Taylor * @author Luke Taylor
* @deprecated in favour of Spring LDAP ContextMapper
* @version $Id$ * @version $Id$
*/ */
public interface LdapEntryMapper { public interface LdapEntryMapper {

View File

@ -20,6 +20,11 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.ldap.ContextSource;
import org.springframework.ldap.ContextExecutor;
import org.springframework.ldap.ContextMapper;
import org.springframework.ldap.support.DirContextAdapter;
import org.springframework.ldap.support.DistinguishedName;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -36,52 +41,35 @@ import javax.naming.directory.SearchResult;
/** /**
* LDAP equivalent of the Spring JdbcTemplate class.<p>This is mainly intended to simplify Ldap access within Acegi * LDAP equivalent of the Spring JdbcTemplate class.
* Security's LDAP-related services.</p> * <p>
* This is mainly intended to simplify Ldap access within Acegi Security's LDAP-related services.
* </p>
* *
* @author Ben Alex * @author Ben Alex
* @author Luke Taylor * @author Luke Taylor
*/ */
public class LdapTemplate { public class LdapTemplate extends org.springframework.ldap.LdapTemplate {
//~ Static fields/initializers ===================================================================================== //~ Static fields/initializers =====================================================================================
public static final String[] NO_ATTRS = new String[0]; public static final String[] NO_ATTRS = new String[0];
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private InitialDirContextFactory dirContextFactory;
private NamingExceptionTranslator exceptionTranslator = new LdapExceptionTranslator(); private NamingExceptionTranslator exceptionTranslator = new LdapExceptionTranslator();
/** Default search controls */ /** Default search controls */
private SearchControls searchControls = new SearchControls(); private SearchControls searchControls = new SearchControls();
private String password = null;
private String principalDn = null;
//~ Constructors =================================================================================================== //~ Constructors ===================================================================================================
public LdapTemplate(InitialDirContextFactory dirContextFactory) { public LdapTemplate(ContextSource contextSource) {
Assert.notNull(dirContextFactory, "An InitialDirContextFactory is required"); Assert.notNull(contextSource, "ContextSource cannot be null");
this.dirContextFactory = dirContextFactory; setContextSource(contextSource);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
} }
/**
*
* @param dirContextFactory the source of DirContexts
* @param userDn the user name to authenticate as when obtaining new contexts
* @param password the user's password
*/
public LdapTemplate(InitialDirContextFactory dirContextFactory, String userDn, String password) {
this(dirContextFactory);
Assert.hasLength(userDn, "userDn must not be null or empty");
Assert.notNull(password, "password cannot be null");
this.principalDn = userDn;
this.password = password;
}
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
/** /**
@ -96,9 +84,9 @@ public class LdapTemplate {
public boolean compare(final String dn, final String attributeName, final Object value) { public boolean compare(final String dn, final String attributeName, final Object value) {
final String comparisonFilter = "(" + attributeName + "={0})"; final String comparisonFilter = "(" + attributeName + "={0})";
class LdapCompareCallback implements LdapCallback { class LdapCompareCallback implements ContextExecutor {
public Object doInDirContext(DirContext ctx)
throws NamingException { public Object executeWithContext(DirContext ctx) throws NamingException {
SearchControls ctls = new SearchControls(); SearchControls ctls = new SearchControls();
ctls.setReturningAttributes(NO_ATTRS); ctls.setReturningAttributes(NO_ATTRS);
ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
@ -111,30 +99,28 @@ public class LdapTemplate {
} }
} }
Boolean matches = (Boolean) execute(new LdapCompareCallback()); Boolean matches = (Boolean) executeReadOnly(new LdapCompareCallback());
return matches.booleanValue(); return matches.booleanValue();
} }
public Object execute(LdapCallback callback) throws DataAccessException { // public Object execute(LdapCallback callback) throws DataAccessException {
DirContext ctx = null; // DirContext ctx = null;
//
try { // try {
ctx = (principalDn == null) ? dirContextFactory.newInitialDirContext() // ctx = dirContextFactory.getReadOnlyContext();
: dirContextFactory.newInitialDirContext(principalDn, password); //
// return callback.doInDirContext(ctx);
return callback.doInDirContext(ctx); // } catch (NamingException exception) {
} catch (NamingException exception) { // throw exceptionTranslator.translate("LdapCallback", exception);
throw exceptionTranslator.translate("LdapCallback", exception); // } finally {
} finally { // LdapUtils.closeContext(ctx);
LdapUtils.closeContext(ctx); // }
} // }
}
public boolean nameExists(final String dn) { public boolean nameExists(final String dn) {
Boolean exists = (Boolean) execute(new LdapCallback() { Boolean exists = (Boolean) executeReadOnly(new ContextExecutor() {
public Object doInDirContext(DirContext ctx) public Object executeWithContext(DirContext ctx) throws NamingException {
throws NamingException {
try { try {
Object obj = ctx.lookup(LdapUtils.getRelativeName(dn, ctx)); Object obj = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
if (obj instanceof Context) { if (obj instanceof Context) {
@ -161,12 +147,17 @@ public class LdapTemplate {
* *
* @return the object created by the mapper * @return the object created by the mapper
*/ */
public Object retrieveEntry(final String dn, final LdapEntryMapper mapper, final String[] attributesToRetrieve) { public Object retrieveEntry(final String dn, final ContextMapper mapper, final String[] attributesToRetrieve) {
return execute(new LdapCallback() {
public Object doInDirContext(DirContext ctx) return executeReadOnly(new ContextExecutor() {
throws NamingException { public Object executeWithContext(DirContext ctx) throws NamingException {
return mapper.mapAttributes(dn, Attributes attrs = ctx.getAttributes(LdapUtils.getRelativeName(dn, ctx), attributesToRetrieve);
ctx.getAttributes(LdapUtils.getRelativeName(dn, ctx), attributesToRetrieve));
// Object object = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
DirContextAdapter ctxAdapter = new DirContextAdapter(attrs, new DistinguishedName(dn));
return mapper.mapFromContext(ctxAdapter);
} }
}); });
} }
@ -185,8 +176,8 @@ public class LdapTemplate {
*/ */
public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params, public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
final String attributeName) { final String attributeName) {
class SingleAttributeSearchCallback implements LdapCallback { class SingleAttributeSearchCallback implements ContextExecutor {
public Object doInDirContext(DirContext ctx) public Object executeWithContext(DirContext ctx)
throws NamingException { throws NamingException {
Set unionOfValues = new HashSet(); Set unionOfValues = new HashSet();
@ -224,7 +215,7 @@ public class LdapTemplate {
} }
} }
return (Set) execute(new SingleAttributeSearchCallback()); return (Set) executeReadOnly(new SingleAttributeSearchCallback());
} }
/** /**
@ -242,10 +233,12 @@ public class LdapTemplate {
* result. * result.
*/ */
public Object searchForSingleEntry(final String base, final String filter, final Object[] params, public Object searchForSingleEntry(final String base, final String filter, final Object[] params,
final LdapEntryMapper mapper) { final ContextMapper mapper) {
return execute(new LdapCallback() {
public Object doInDirContext(DirContext ctx) return executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx)
throws NamingException { throws NamingException {
NamingEnumeration results = ctx.search(base, filter, params, searchControls); NamingEnumeration results = ctx.search(base, filter, params, searchControls);
if (!results.hasMore()) { if (!results.hasMore()) {
@ -274,7 +267,10 @@ public class LdapTemplate {
dn.append(nameInNamespace); dn.append(nameInNamespace);
} }
return mapper.mapAttributes(dn.toString(), searchResult.getAttributes()); DirContextAdapter ctxAdapter = new DirContextAdapter(
searchResult.getAttributes(), new DistinguishedName(dn.toString()));
return mapper.mapFromContext(ctxAdapter);
} }
}); });
} }

View File

@ -19,6 +19,8 @@ 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.util.Assert;
import org.springframework.ldap.support.DistinguishedName;
import org.springframework.ldap.support.DirContextAdapter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -45,6 +47,10 @@ public final class LdapUtils {
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
public static void closeContext(Context ctx) { public static void closeContext(Context ctx) {
if(ctx instanceof DirContextAdapter) {
return;
}
try { try {
if (ctx != null) { if (ctx != null) {
ctx.close(); ctx.close();
@ -55,9 +61,10 @@ public final class LdapUtils {
} }
/** /**
* Obtains the part of a DN relative to a supplied base context.<p>If the DN is * Obtains the part of a DN relative to a supplied base context.
* "cn=bob,ou=people,dc=acegisecurity,dc=org" and the base context name is "ou=people,dc=acegisecurity,dc=org" it * <p>If the DN is "cn=bob,ou=people,dc=acegisecurity,dc=org" and the base context name is
* would return "cn=bob".</p> * "ou=people,dc=acegisecurity,dc=org" it would return "cn=bob".
* </p>
* *
* @param fullDn the DN * @param fullDn the DN
* @param baseCtx the context to work out the name relative to. * @param baseCtx the context to work out the name relative to.
@ -68,22 +75,42 @@ public final class LdapUtils {
*/ */
public static String getRelativeName(String fullDn, Context baseCtx) public static String getRelativeName(String fullDn, Context baseCtx)
throws NamingException { throws NamingException {
String baseDn = baseCtx.getNameInNamespace(); String baseDn = baseCtx.getNameInNamespace();
if (baseDn.length() == 0) { if (baseDn.length() == 0) {
return fullDn; return fullDn;
} }
if (baseDn.equals(fullDn)) { DistinguishedName base = new DistinguishedName(baseDn);
DistinguishedName full = new DistinguishedName(fullDn);
if(base.equals(full)) {
return ""; return "";
} }
int index = fullDn.lastIndexOf(baseDn); Assert.isTrue(full.startsWith(base), "Full DN does not start with base DN");
Assert.isTrue(index > 0, "Context base DN is not contained in the full DN"); full.removeFirst(base);
// remove the base name and preceding comma. return full.toString();
return fullDn.substring(0, index - 1); }
/**
* Gets the full dn of a name by prepending the name of the context it is relative to.
* If the name already contains the base name, it is returned unaltered.
*/
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx)
throws NamingException {
DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace());
if(dn.contains(baseDn)) {
return dn;
}
baseDn.append(dn);
return baseDn;
} }
public static byte[] getUtf8Bytes(String s) { public static byte[] getUtf8Bytes(String s) {
@ -95,6 +122,27 @@ public final class LdapUtils {
} }
} }
public static String getUtf8BytesAsString(byte[] utf8) {
try {
return new String(utf8, "UTF-8");
} catch (UnsupportedEncodingException e) {
// Should be impossible since UTF-8 is required by all implementations
throw new IllegalStateException("Failed to convert string to UTF-8 bytes. Shouldn't be possible");
}
}
public static String convertPasswordToString(Object passObj) {
Assert.notNull(passObj, "Password object to convert must not be null");
if(passObj instanceof byte[]) {
return getUtf8BytesAsString((byte[])passObj);
} else if (passObj instanceof String) {
return (String)passObj;
} else {
throw new IllegalArgumentException("Password object was not a String or byte array.");
}
}
/** /**
* Works out the root DN for an LDAP URL.<p>For example, the URL * Works out the root DN for an LDAP URL.<p>For example, the URL
* <tt>ldap://monkeymachine:11389/dc=acegisecurity,dc=org</tt> has the root DN "dc=acegisecurity,dc=org".</p> * <tt>ldap://monkeymachine:11389/dc=acegisecurity,dc=org</tt> has the root DN "dc=acegisecurity,dc=org".</p>
@ -136,7 +184,7 @@ public final class LdapUtils {
} }
// removed for 1.3 compatibility // 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=acegisecurity,dc=org</tt>). * @param url the URL (e.g. <tt>ldap://monkeymachine:11389/dc=acegisecurity,dc=org</tt>).
* @return the URI object created from the URL * @return the URI object created from the URL

View File

@ -30,6 +30,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.ldap.ContextSource;
import javax.naming.directory.DirContext; import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls; import javax.naming.directory.SearchControls;
@ -51,7 +52,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private InitialDirContextFactory initialDirContextFactory; private ContextSource initialDirContextFactory;
private LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper(); private LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper();
/** /**
@ -72,7 +73,6 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
* <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>
* </ul> * </ul>
* TODO: more examples.
*/ */
private String searchFilter; private String searchFilter;
@ -116,9 +116,14 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
template.setSearchControls(searchControls); template.setSearchControls(searchControls);
try { try {
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.searchForSingleEntry(searchBase,
searchFilter, new String[] {username}, userDetailsMapper); LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.searchForSingleEntry(
searchBase, searchFilter, new String[] {username}, userDetailsMapper);
user.setUsername(username); user.setUsername(username);
// if (!username.equals(user.getUsername())) {
// logger.debug("Search returned user object with different username: " + user.getUsername());
// }
return user.createUserDetails(); return user.createUserDetails();
} catch (IncorrectResultSizeDataAccessException notFound) { } catch (IncorrectResultSizeDataAccessException notFound) {

View File

@ -18,7 +18,6 @@ package org.acegisecurity.providers.ldap.authenticator;
import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.ldap.InitialDirContextFactory; import org.acegisecurity.ldap.InitialDirContextFactory;
import org.acegisecurity.ldap.LdapEntryMapper;
import org.acegisecurity.ldap.LdapUserSearch; import org.acegisecurity.ldap.LdapUserSearch;
import org.acegisecurity.providers.ldap.LdapAuthenticator; import org.acegisecurity.providers.ldap.LdapAuthenticator;
@ -32,6 +31,7 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.ldap.ContextMapper;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -79,8 +79,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
this.setInitialDirContextFactory(initialDirContextFactory); this.setInitialDirContextFactory(initialDirContextFactory);
} }
// ~ Methods //~ Methods ========================================================================================================
// ========================================================================================================
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.isTrue((userDnFormat != null) || (userSearch != null), Assert.isTrue((userDnFormat != null) || (userSearch != null),
@ -111,7 +110,7 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
return userAttributes; return userAttributes;
} }
protected LdapEntryMapper getUserDetailsMapper() { protected ContextMapper getUserDetailsMapper() {
return userDetailsMapper; return userDetailsMapper;
} }

View File

@ -25,7 +25,10 @@ import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
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.ContextSource;
import org.springframework.dao.DataAccessException;
import javax.naming.directory.DirContext;
import java.util.Iterator; import java.util.Iterator;
@ -81,7 +84,8 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
} }
private LdapUserDetails bindWithDn(String userDn, String username, String password) { private LdapUserDetails bindWithDn(String userDn, String username, String password) {
LdapTemplate template = new LdapTemplate(getInitialDirContextFactory(), userDn, password); LdapTemplate template = new LdapTemplate(
new BindWithSpecificDnContextSource(getInitialDirContextFactory(), userDn, password));
try { try {
LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.retrieveEntry(userDn, LdapUserDetailsImpl.Essence user = (LdapUserDetailsImpl.Essence) template.retrieveEntry(userDn,
@ -108,4 +112,26 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
logger.debug("Failed to bind as " + userDn + ": " + cause); logger.debug("Failed to bind as " + userDn + ": " + cause);
} }
} }
private class BindWithSpecificDnContextSource implements ContextSource {
private InitialDirContextFactory ctxFactory;
private String userDn;
private String password;
public BindWithSpecificDnContextSource(InitialDirContextFactory ctxFactory, String userDn, String password) {
this.ctxFactory = ctxFactory;
this.userDn = userDn;
this.password = password;
}
public DirContext getReadOnlyContext() throws DataAccessException {
return ctxFactory.newInitialDirContext(userDn, password);
}
public DirContext getReadWriteContext() throws DataAccessException {
return getReadOnlyContext();
}
}
} }

View File

@ -17,6 +17,8 @@ package org.acegisecurity.ldap;
import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.BadCredentialsException; import org.acegisecurity.BadCredentialsException;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.support.DirContextAdapter;
import java.util.Hashtable; import java.util.Hashtable;
@ -112,7 +114,7 @@ public class DefaultInitialDirContextFactoryTests extends AbstractLdapServerTest
try { try {
idf.newInitialDirContext(); idf.newInitialDirContext();
fail("Connection succeeded unexpectedly"); fail("Connection succeeded unexpectedly");
} catch (LdapDataAccessException expected) {} } catch (UncategorizedLdapException expected) {}
} }
public void testEnvironment() { public void testEnvironment() {

View File

@ -15,6 +15,9 @@
package org.acegisecurity.ldap; package org.acegisecurity.ldap;
import org.springframework.ldap.ContextExecutor;
import org.springframework.ldap.UncategorizedLdapException;
import java.util.Set; import java.util.Set;
import javax.naming.NamingException; import javax.naming.NamingException;
@ -64,21 +67,19 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase {
public void testNamingExceptionIsTranslatedCorrectly() { public void testNamingExceptionIsTranslatedCorrectly() {
try { try {
template.execute(new LdapCallback() { template.executeReadOnly(new ContextExecutor() {
public Object doInDirContext(DirContext dirContext) public Object executeWithContext(DirContext dirContext) throws NamingException {
throws NamingException {
throw new NamingException(); throw new NamingException();
} }
}); });
fail("Expected LdapDataAccessException on NamingException"); fail("Expected UncategorizedLdapException on NamingException");
} catch (LdapDataAccessException expected) { } catch (UncategorizedLdapException expected) {}
}
} }
public void testSearchForSingleAttributeValues() { public void testSearchForSingleAttributeValues() {
String param = "uid=ben,ou=people,dc=acegisecurity,dc=org"; String param = "uid=ben,ou=people,dc=acegisecurity,dc=org";
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou"); Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[] {param}, "ou");
assertEquals("Expected 3 results from search", 3, values.size()); assertEquals("Expected 3 results from search", 3, values.size());
assertTrue(values.contains("developer")); assertTrue(values.contains("developer"));

View File

@ -30,9 +30,6 @@ import javax.naming.directory.DirContext;
* @version $Id$ * @version $Id$
*/ */
public class LdapUtilsTests extends MockObjectTestCase { public class LdapUtilsTests extends MockObjectTestCase {
//~ Instance fields ================================================================================================
private final LdapDataAccessException tempCoverageBoost = new LdapDataAccessException("");
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
@ -63,6 +60,16 @@ public class LdapUtilsTests extends MockObjectTestCase {
LdapUtils.getRelativeName("cn=jane,dc=acegisecurity,dc=org", (Context) mockCtx.proxy())); LdapUtils.getRelativeName("cn=jane,dc=acegisecurity,dc=org", (Context) mockCtx.proxy()));
} }
public void testGetRelativeNameWorksWithArbitrarySpaces()
throws Exception {
Mock mockCtx = mock(DirContext.class);
mockCtx.expects(atLeastOnce()).method("getNameInNamespace").will(returnValue("dc=acegisecurity,dc = org"));
assertEquals("cn=jane smith",
LdapUtils.getRelativeName("cn=jane smith, dc = acegisecurity , dc=org", (Context) mockCtx.proxy()));
}
public void testRootDnsAreParsedFromUrlsCorrectly() { public void testRootDnsAreParsedFromUrlsCorrectly() {
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine")); assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine"));
assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/")); assertEquals("", LdapUtils.parseRootDnFromUrl("ldap://monkeymachine/"));

View File

@ -15,21 +15,21 @@
package org.acegisecurity.ldap; package org.acegisecurity.ldap;
import org.springframework.dao.DataAccessException;
import javax.naming.directory.DirContext; import javax.naming.directory.DirContext;
/** /**
* *
DOCUMENT ME!
*
* @author Luke Taylor * @author Luke Taylor
* @version $Id$ * @version $Id$
*/ */
public class MockInitialDirContextFactory implements InitialDirContextFactory { public class MockInitialDirContextFactory implements InitialDirContextFactory {
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
DirContext ctx; private DirContext ctx;
String baseDn; private String baseDn;
//~ Constructors =================================================================================================== //~ Constructors ===================================================================================================
@ -51,4 +51,12 @@ public class MockInitialDirContextFactory implements InitialDirContextFactory {
public DirContext newInitialDirContext(String username, String password) { public DirContext newInitialDirContext(String username, String password) {
return ctx; return ctx;
} }
public DirContext getReadOnlyContext() throws DataAccessException {
return ctx;
}
public DirContext getReadWriteContext() throws DataAccessException {
return ctx;
}
} }

View File

@ -106,4 +106,6 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapServerTestCase {
// assertEquals("uid=ben,ou=people,dc=acegisecurity,dc=org", ben.getDn()); // assertEquals("uid=ben,ou=people,dc=acegisecurity,dc=org", ben.getDn());
} }
// TODO: Add test with non-uid username
} }

View File

@ -27,8 +27,6 @@ import javax.naming.directory.DirContext;
/** /**
* *
DOCUMENT ME!
*
* @author Luke Taylor * @author Luke Taylor
* @version $Id$ * @version $Id$
*/ */
@ -46,14 +44,15 @@ public class PasswordComparisonAuthenticatorMockTests extends MockObjectTestCase
// 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=acegisecurity,dc=org")); mockCtx.expects(atLeastOnce()).method("getNameInNamespace").will(returnValue("dc=acegisecurity,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(new BasicAttributes())); .will(returnValue(new BasicAttributes()));
// Setup a single return value (i.e. success) // Setup a single return value (i.e. success)
Attributes searchResults = new BasicAttributes("", null); Attributes searchResults = new BasicAttributes("", null);
mockCtx.expects(once()).method("search") mockCtx.expects(once())
.with(eq("cn=Bob,ou=people"), eq("(userPassword={0})"), NOT_NULL, NOT_NULL) .method("search")
.with(eq("cn=Bob, ou=people"), eq("(userPassword={0})"), NOT_NULL, NOT_NULL)
.will(returnValue(searchResults.getAll())); .will(returnValue(searchResults.getAll()));
mockCtx.expects(atLeastOnce()).method("close"); mockCtx.expects(atLeastOnce()).method("close");
authenticator.authenticate("Bob", "bobspassword"); authenticator.authenticate("Bob", "bobspassword");