| CODENOTIFIER | HelpYou are not signed inSign in |
Project: Acegi
Revision: 3261
Author: luke_t
Date: 26 Aug 2008 08:49:37
Changes: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.
| ... | ...@@ -15,6 +15,21 @@ | |
| 15 | 15 | |
| 16 | 16 | package org.springframework.security.ldap; |
| 17 | 17 | |
| 18 | import java.text.MessageFormat; | |
| 19 | import java.util.Arrays; | |
| 20 | import java.util.HashSet; | |
| 21 | import java.util.Set; | |
| 22 | ||
| 23 | import javax.naming.NamingEnumeration; | |
| 24 | import javax.naming.NamingException; | |
| 25 | import javax.naming.PartialResultException; | |
| 26 | import javax.naming.directory.Attributes; | |
| 27 | import javax.naming.directory.DirContext; | |
| 28 | import javax.naming.directory.SearchControls; | |
| 29 | import javax.naming.directory.SearchResult; | |
| 30 | ||
| 31 | import org.apache.commons.logging.Log; | |
| 32 | import org.apache.commons.logging.LogFactory; | |
| 18 | 33 | import org.springframework.dao.IncorrectResultSizeDataAccessException; |
| 19 | 34 | import org.springframework.ldap.core.ContextExecutor; |
| 20 | 35 | import org.springframework.ldap.core.ContextMapper; |
| ... | ...@@ -23,33 +38,18 @@ | |
| 23 | 38 | import org.springframework.ldap.core.DirContextOperations; |
| 24 | 39 | import org.springframework.ldap.core.DistinguishedName; |
| 25 | 40 | import org.springframework.ldap.core.LdapEncoder; |
| 41 | import org.springframework.ldap.core.LdapTemplate; | |
| 26 | 42 | import org.springframework.util.Assert; |
| 27 | 43 | |
| 28 | import org.apache.commons.logging.Log; | |
| 29 | import org.apache.commons.logging.LogFactory; | |
| 30 | ||
| 31 | import javax.naming.NamingEnumeration; | |
| 32 | import javax.naming.NamingException; | |
| 33 | import javax.naming.directory.Attributes; | |
| 34 | import javax.naming.directory.DirContext; | |
| 35 | import javax.naming.directory.SearchControls; | |
| 36 | import javax.naming.directory.SearchResult; | |
| 37 | import java.text.MessageFormat; | |
| 38 | import java.util.HashSet; | |
| 39 | import java.util.Set; | |
| 40 | import java.util.Arrays; | |
| 41 | ||
| 42 | 44 | |
| 43 | 45 | /** |
| 44 | * LDAP equivalent of the Spring JdbcTemplate class. | |
| 45 | * <p> | |
| 46 | * This is mainly intended to simplify Ldap access within Spring Security's LDAP-related services. | |
| 47 | * </p> | |
| 46 | * Extension of Spring LDAP's LdapTemplate class which adds extra functionality required by Spring Security. | |
| 48 | 47 | * |
| 49 | 48 | * @author Ben Alex |
| 50 | 49 | * @author Luke Taylor |
| 50 | * @since 2.0 | |
| 51 | 51 | */ |
| 52 | public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.LdapTemplate { | |
| 52 | public class SpringSecurityLdapTemplate extends LdapTemplate { | |
| 53 | 53 | //~ Static fields/initializers ===================================================================================== |
| 54 | 54 | private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class); |
| 55 | 55 | |
| ... | ...@@ -136,14 +136,14 @@ | |
| 136 | 136 | * @return the set of String values for the attribute as a union of the values found in all the matching entries. |
| 137 | 137 | */ |
| 138 | 138 | public Set searchForSingleAttributeValues(final String base, final String filter, final Object[] params, |
| 139 | final String attributeName) { | |
| 140 | // Escape the params acording to RFC2254 | |
| 141 | Object[] encodedParams = new String[params.length]; | |
| 142 | ||
| 143 | for (int i=0; i < params.length; i++) { | |
| 144 | encodedParams[i] = LdapEncoder.filterEncode(params[i].toString()); | |
| 145 | } | |
| 146 | ||
| 139 | final String attributeName) { | |
| 140 | // Escape the params acording to RFC2254 | |
| 141 | Object[] encodedParams = new String[params.length]; | |
| 142 | ||
| 143 | for (int i=0; i < params.length; i++) { | |
| 144 | encodedParams[i] = LdapEncoder.filterEncode(params[i].toString()); | |
| 145 | } | |
| 146 | ||
| 147 | 147 | String formattedFilter = MessageFormat.format(filter, encodedParams); |
| 148 | 148 | logger.debug("Using filter: " + formattedFilter); |
| 149 | 149 | |
| ... | ...@@ -175,12 +175,15 @@ | |
| 175 | 175 | /** |
| 176 | 176 | * Performs a search, with the requirement that the search shall return a single directory entry, and uses |
| 177 | 177 | * the supplied mapper to create the object from that entry. |
| 178 | * <p> | |
| 179 | * Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active Directory | |
| 180 | * (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}). | |
| 181 | * | |
| 182 | * @param base the search base, relative to the base context supplied by the context source. | |
| 183 | * @param filter the LDAP search filter | |
| 184 | * @param params parameters to be substituted in the search. | |
| 178 | 185 | * |
| 179 | * @param base | |
| 180 | * @param filter | |
| 181 | * @param params | |
| 182 | * | |
| 183 | * @return the object created by the mapper from the matching entry | |
| 186 | * @return a DirContextOperations instance created from the matching entry. | |
| 184 | 187 | * |
| 185 | 188 | * @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one |
| 186 | 189 | * result. |
| ... | ...@@ -188,32 +191,38 @@ | |
| 188 | 191 | public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) { |
| 189 | 192 | |
| 190 | 193 | return (DirContextOperations) executeReadOnly(new ContextExecutor() { |
| 191 | public Object executeWithContext(DirContext ctx) | |
| 192 | throws NamingException { | |
| 193 | ||
| 194 | NamingEnumeration results = ctx.search(base, filter, params, searchControls); | |
| 195 | ||
| 196 | if (!results.hasMore()) { | |
| 197 | throw new IncorrectResultSizeDataAccessException(1, 0); | |
| 194 | public Object executeWithContext(DirContext ctx) throws NamingException { | |
| 195 | DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace()); | |
| 196 | NamingEnumeration resultsEnum = ctx.search(base, filter, params, searchControls); | |
| 197 | Set results = new HashSet(); | |
| 198 | try { | |
| 199 | while (resultsEnum.hasMore()) { | |
| 200 | ||
| 201 | SearchResult searchResult = (SearchResult) resultsEnum.next(); | |
| 202 | // Work out the DN of the matched entry | |
| 203 | StringBuffer dn = new StringBuffer(searchResult.getName()); | |
| 204 | ||
| 205 | if (base.length() > 0) { | |
| 206 | dn.append(","); | |
| 207 | dn.append(base); | |
| 208 | } | |
| 209 | ||
| 210 | results.add(new DirContextAdapter(searchResult.getAttributes(), | |
| 211 | new DistinguishedName(dn.toString()), ctxBaseDn)); | |
| 212 | } | |
| 213 | } catch (PartialResultException e) { | |
| 214 | logger.info("Ignoring PartialResultException"); | |
| 198 | 215 | } |
| 199 | 216 | |
| 200 | SearchResult searchResult = (SearchResult) results.next(); | |
| 201 | ||
| 202 | if (results.hasMore()) { | |
| 203 | // We don't know how many results but set to 2 which is good enough | |
| 204 | throw new IncorrectResultSizeDataAccessException(1, 2); | |
| 217 | if (results.size() == 0) { | |
| 218 | throw new IncorrectResultSizeDataAccessException(1, 0); | |
| 205 | 219 | } |
| 206 | 220 | |
| 207 | // Work out the DN of the matched entry | |
| 208 | StringBuffer dn = new StringBuffer(searchResult.getName()); | |
| 209 | ||
| 210 | if (base.length() > 0) { | |
| 211 | dn.append(","); | |
| 212 | dn.append(base); | |
| 221 | if (results.size() > 1) { | |
| 222 | throw new IncorrectResultSizeDataAccessException(1, results.size()); | |
| 213 | 223 | } |
| 214 | 224 | |
| 215 | return new DirContextAdapter(searchResult.getAttributes(), | |
| 216 | new DistinguishedName(dn.toString()), new DistinguishedName(ctx.getNameInNamespace())); | |
| 225 | return results.toArray()[0]; | |
| 217 | 226 | } |
| 218 | 227 | }); |
| 219 | 228 | } |