SEC-832: NamingEnumeration.hasMore fails on MS AD with PartialResultException

http://jira.springframework.org/browse/SEC-832. Changed searchForSingleEntry method to ignore PartialResultException, similar to Spring LDAP's approach.
This commit is contained in:
Luke Taylor 2008-08-26 12:49:37 +00:00
parent 7f28a8bc5d
commit 150f3d97d0

View File

@ -15,6 +15,21 @@
package org.springframework.security.ldap; package org.springframework.security.ldap;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.ContextExecutor; import org.springframework.ldap.core.ContextExecutor;
import org.springframework.ldap.core.ContextMapper; import org.springframework.ldap.core.ContextMapper;
@ -23,33 +38,18 @@ import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEncoder; import org.springframework.ldap.core.LdapEncoder;
import org.springframework.ldap.core.LdapTemplate;
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.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;
/** /**
* LDAP equivalent of the Spring JdbcTemplate class. * Extension of Spring LDAP's LdapTemplate class which adds extra functionality required by Spring Security.
* <p>
* This is mainly intended to simplify Ldap access within Spring Security's LDAP-related services.
* </p>
* *
* @author Ben Alex * @author Ben Alex
* @author Luke Taylor * @author Luke Taylor
* @since 2.0
*/ */
public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.LdapTemplate { public class SpringSecurityLdapTemplate extends LdapTemplate {
//~ Static fields/initializers ===================================================================================== //~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class); private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);
@ -175,12 +175,15 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
/** /**
* Performs a search, with the requirement that the search shall return a single directory entry, and uses * Performs a search, with the requirement that the search shall return a single directory entry, and uses
* the supplied mapper to create the object from that entry. * the supplied mapper to create the object from that entry.
* <p>
* Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active Directory
* (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).
* *
* @param base * @param base the search base, relative to the base context supplied by the context source.
* @param filter * @param filter the LDAP search filter
* @param params * @param params parameters to be substituted in the search.
* *
* @return the object created by the mapper from the matching entry * @return a DirContextOperations instance created from the matching entry.
* *
* @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one * @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one
* result. * result.
@ -188,22 +191,14 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) { public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
return (DirContextOperations) executeReadOnly(new ContextExecutor() { return (DirContextOperations) executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx) public Object executeWithContext(DirContext ctx) throws NamingException {
throws NamingException { DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
NamingEnumeration resultsEnum = ctx.search(base, filter, params, searchControls);
NamingEnumeration results = ctx.search(base, filter, params, searchControls); Set results = new HashSet();
try {
if (!results.hasMore()) { while (resultsEnum.hasMore()) {
throw new IncorrectResultSizeDataAccessException(1, 0);
}
SearchResult searchResult = (SearchResult) results.next();
if (results.hasMore()) {
// We don't know how many results but set to 2 which is good enough
throw new IncorrectResultSizeDataAccessException(1, 2);
}
SearchResult searchResult = (SearchResult) resultsEnum.next();
// Work out the DN of the matched entry // Work out the DN of the matched entry
StringBuffer dn = new StringBuffer(searchResult.getName()); StringBuffer dn = new StringBuffer(searchResult.getName());
@ -212,8 +207,22 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
dn.append(base); dn.append(base);
} }
return new DirContextAdapter(searchResult.getAttributes(), results.add(new DirContextAdapter(searchResult.getAttributes(),
new DistinguishedName(dn.toString()), new DistinguishedName(ctx.getNameInNamespace())); new DistinguishedName(dn.toString()), ctxBaseDn));
}
} catch (PartialResultException e) {
logger.info("Ignoring PartialResultException");
}
if (results.size() == 0) {
throw new IncorrectResultSizeDataAccessException(1, 0);
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
}
return results.toArray()[0];
} }
}); });
} }