diff --git a/core/src/main/java/org/acegisecurity/ldap/DefaultInitialDirContextFactory.java b/core/src/main/java/org/acegisecurity/ldap/DefaultInitialDirContextFactory.java new file mode 100644 index 0000000000..66b35a7911 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/DefaultInitialDirContextFactory.java @@ -0,0 +1,285 @@ +package org.acegisecurity.ldap; + +import org.acegisecurity.AcegiMessageSource; +import org.acegisecurity.BadCredentialsException; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.MessageSource; +import org.springframework.context.support.MessageSourceAccessor; +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.InitialDirContext; +import javax.naming.Context; +import javax.naming.CommunicationException; +import javax.naming.NamingException; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.Hashtable; + +/** + * Encapsulates the information for connecting to an LDAP server and provides an + * access point for obtaining DirContext references. + *

+ * The directory location is configured using by setting the constructor argument + * providerUrl. This should be in the form + * ldap://monkeymachine.co.uk:389/dc=acegisecurity,dc=org. The Sun JNDI + * provider also supports lists of space-separated URLs, each of which will be tried + * in turn until a connection is obtained. + *

+ *

+ * To obtain an initial context, the client calls the newInitialDirContext + * method. There are two signatures - one with no arguments and one which allows + * binding with a specific username and password. + *

+ *

+ * The no-args version will bind anonymously unless a manager login has been configured + * using the properties managerDn and managerPassword, in which case + * it will bind as the manager user. + *

+ *

+ * Connection pooling is enabled by default for anonymous or manager connections, but + * not when binding as a specific user. + *

+ * + * @see The Java + * tutorial's guide to LDAP connection pooling + * + * @author Robert Sanders + * @author Luke Taylor + * @version $Id$ + * + */ +public class DefaultInitialDirContextFactory implements InitialDirContextFactory, + MessageSourceAware { + + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(org.acegisecurity.ldap.DefaultInitialDirContextFactory.class); + + private static final String CONNECTION_POOL_KEY = "com.sun.jndi.ldap.connect.pool"; + + private static final String AUTH_TYPE_NONE = "none"; + + //~ Instance fields ======================================================== + + protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); + + /** + * The LDAP url of the server (and root context) to connect to. + */ + private String providerUrl; + + /** + * The root DN. This is worked out from the url. + * It is used by client classes when forming a full DN for + * bind authentication (for example). + */ + private String rootDn = null; + + /** + * If your LDAP server does not allow anonymous searches then + * you will need to provide a "manager" user's DN to log in with. + */ + private String managerDn = null; + + /** + * The manager user's password. + */ + private String managerPassword = "manager_password_not_set"; + + /** Type of authentication within LDAP; default is simple. */ + private String authenticationType = "simple"; + + /** + * The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory. + * Default is "com.sun.jndi.ldap.LdapCtxFactory"; you should not + * need to set this unless you have unusual needs. + */ + private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; + + /** Allows extra environment variables to be added at config time. */ + private Map extraEnvVars = null; + + /** + * Use the LDAP Connection pool; if true, then the + * LDAP environment property "com.sun.jndi.ldap.connect.pool" is added + * to any other JNDI properties. + */ + private boolean useConnectionPool = true; + + //~ Constructors =========================================================== + + public DefaultInitialDirContextFactory(String providerUrl) { + this.providerUrl = providerUrl; + + Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied."); + + StringTokenizer st = new StringTokenizer(providerUrl); + + // 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); + + org.acegisecurity.ldap.DefaultInitialDirContextFactory.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"); + } + } + + // This doesn't necessarily hold for embedded servers. + //Assert.isTrue(uri.getScheme().equals("ldap"), "Ldap URL must start with 'ldap://'"); + } + + + //~ Methods ================================================================ + + /** + * Connects anonymously unless a manager user has been specified, in which case + * it will bind as the manager. + * + * @return the resulting context object. + */ + public DirContext newInitialDirContext() { + + if (managerDn != null) { + return newInitialDirContext(managerDn, managerPassword); + } + + Hashtable env = getEnvironment(); + env.put(Context.SECURITY_AUTHENTICATION, org.acegisecurity.ldap.DefaultInitialDirContextFactory.AUTH_TYPE_NONE); + + return connect(env); + } + + public DirContext newInitialDirContext(String username, String password) { + Hashtable env = getEnvironment(); + + // Don't pool connections for individual users + if (!username.equals(managerDn)) { + env.remove(org.acegisecurity.ldap.DefaultInitialDirContextFactory.CONNECTION_POOL_KEY); + } + + env.put(Context.SECURITY_PRINCIPAL, username); + env.put(Context.SECURITY_CREDENTIALS, password); + + return connect(env); + } + + /** + * @return the Hashtable describing the base DirContext that will be created, + * minus the username/password if any. + */ + protected Hashtable getEnvironment() { + Hashtable env = new Hashtable(); + + env.put(Context.SECURITY_AUTHENTICATION, authenticationType); + env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory); + env.put(Context.PROVIDER_URL, providerUrl); + + if (useConnectionPool) { + env.put(org.acegisecurity.ldap.DefaultInitialDirContextFactory.CONNECTION_POOL_KEY, "true"); + } + + if ((extraEnvVars != null) && (extraEnvVars.size() > 0)) { + env.putAll(extraEnvVars); + } + + return env; + } + + private InitialDirContext connect(Hashtable env) { + + if (org.acegisecurity.ldap.DefaultInitialDirContextFactory.logger.isDebugEnabled()) { + Hashtable envClone = (Hashtable)env.clone(); + + if (envClone.containsKey(Context.SECURITY_CREDENTIALS)) { + envClone.put(Context.SECURITY_CREDENTIALS, "******"); + } + + org.acegisecurity.ldap.DefaultInitialDirContextFactory.logger.debug("Creating InitialDirContext with environment " + envClone); + } + + try { + return new InitialDirContext(env); + + } catch(CommunicationException ce) { + throw new LdapDataAccessException(messages.getMessage( + "DefaultIntitalDirContextFactory.communicationFailure", + "Unable to connect to LDAP server"), ce); + } catch(javax.naming.AuthenticationException ae) { + throw new BadCredentialsException(messages.getMessage( + "DefaultIntitalDirContextFactory.badCredentials", + "Bad credentials"), ae); + } catch (NamingException nx) { + throw new LdapDataAccessException(messages.getMessage( + "DefaultIntitalDirContextFactory.unexpectedException", + "Failed to obtain InitialDirContext due to unexpected exception"), nx); + } + } + + /** + * Returns the root DN of the configured provider URL. For example, + * if the URL is ldap://monkeymachine.co.uk:389/dc=acegisecurity,dc=org + * the value will be dc=acegisecurity,dc=org. + * + * @return the root DN calculated from the path of the LDAP url. + */ + public String getRootDn() { + return rootDn; + } + + public void setAuthenticationType(String authenticationType) { + Assert.hasLength(authenticationType, "LDAP Authentication type must not be empty or null"); + this.authenticationType = authenticationType; + } + + public void setInitialContextFactory(String initialContextFactory) { + Assert.hasLength(initialContextFactory, "Initial context factory name cannot be empty or null"); + this.initialContextFactory = initialContextFactory; + } + + /** + * @param managerDn The name of the "manager" user for default authentication. + */ + public void setManagerDn(String managerDn) { + Assert.hasLength(managerDn, "Manager user name cannot be empty or null."); + this.managerDn = managerDn; + } + + /** + * @param managerPassword The "manager" user's password. + */ + public void setManagerPassword(String managerPassword) { + Assert.hasLength(managerPassword, "Manager password must not be empty or null."); + this.managerPassword = managerPassword; + } + + /** + * @param extraEnvVars extra environment variables to be added at config time. + */ + public void setExtraEnvVars(Map extraEnvVars) { + Assert.notNull(extraEnvVars, "Extra environment map cannot be null."); + this.extraEnvVars = extraEnvVars; + } + + public void setMessageSource(MessageSource messageSource) { + this.messages = new MessageSourceAccessor(messageSource); + } + + /** + * Connection pooling is enabled by default for anonymous or "manager" + * connections when using the default Sun provider. To disable all + * connection pooling, set this property to false. + * + * @param useConnectionPool whether to pool connections for non-specific users. + */ + public void setUseConnectionPool(boolean useConnectionPool) { + this.useConnectionPool = useConnectionPool; + } +} diff --git a/core/src/main/java/org/acegisecurity/ldap/InitialDirContextFactory.java b/core/src/main/java/org/acegisecurity/ldap/InitialDirContextFactory.java new file mode 100644 index 0000000000..c8a0949b1f --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/InitialDirContextFactory.java @@ -0,0 +1,29 @@ +package org.acegisecurity.ldap; + +import javax.naming.directory.DirContext; + +/** + * Access point for obtaining LDAP contexts. + * + * @see org.acegisecurity.ldap.DefaultInitialDirContextFactory + * + * @author Luke Taylor + * @version $Id$ + */ +public interface InitialDirContextFactory { + + /** + * Provides an initial context without specific user information. + */ + DirContext newInitialDirContext(); + + /** + * Provides an initial context by binding as a specific user. + */ + DirContext newInitialDirContext(String userDn, String password); + + /** + * @return The DN of the contexts returned by this factory. + */ + String getRootDn(); +} diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapDataAccessException.java b/core/src/main/java/org/acegisecurity/ldap/LdapDataAccessException.java new file mode 100644 index 0000000000..657a4d7d63 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/LdapDataAccessException.java @@ -0,0 +1,36 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ldap; + +import org.acegisecurity.AuthenticationServiceException; + +/** + * Used to wrap unexpected NamingExceptions while accessing the LDAP server + * or for other LDAP-related data problems such as data we can't handle. + * + * @author Luke Taylor + * @version $Id$ + */ +public class LdapDataAccessException extends AuthenticationServiceException { + + public LdapDataAccessException(String msg) { + super(msg); + } + + public LdapDataAccessException(String msg, Throwable ex) { + super(msg, ex); + } +} diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapUserInfo.java b/core/src/main/java/org/acegisecurity/ldap/LdapUserInfo.java new file mode 100644 index 0000000000..54b64d1653 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/LdapUserInfo.java @@ -0,0 +1,71 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ldap; + +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.NamingException; + +/** + * A user representation which is used internally by the Ldap provider. + * + * It contains the user's distinguished name and a set of attributes that + * have been retrieved from the Ldap server. + *

+ * An instance may be created as the result of a search, or when user information + * is retrieved during authentication. + *

+ *

+ * An instance of this class will be used by the LdapAuthenticationProvider + * to construct the final user details object that it returns. + *

+ * + * @author Luke Taylor + * @version $Id$ + */ +public class LdapUserInfo { + + //~ Instance fields ======================================================== + + private String dn; + private Attributes attributes; + + //~ Constructors =========================================================== + + /** + * + * @param dn the full DN of the user + * @param attributes any attributes loaded from the user's directory entry. + */ + public LdapUserInfo(String dn, Attributes attributes) { + this.dn = dn; + this.attributes = attributes; + } + + //~ Methods ================================================================ + + public String getDn() { + return dn; + } + + public String getRelativeName(DirContext ctx) throws NamingException { + return LdapUtils.getRelativeName(dn, ctx); + } + + public Attributes getAttributes() { + return (Attributes)attributes.clone(); + } +} diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java b/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java new file mode 100644 index 0000000000..4768404ea1 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java @@ -0,0 +1,41 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ldap; + +/** + * Obtains a user's information from the LDAP directory given a login name. + *

+ * May be optionally used to configure the LDAP authentication implementation when + * a more sophisticated approach is required than just using a simple username->DN + * mapping. + *

+ * + * @author Luke Taylor + * @version $Id$ + */ +public interface LdapUserSearch { + + /** + * Locates a single user in the directory and returns the LDAP information + * for that user. + * + * @param username the login name supplied to the authentication service. + * @return an LdapUserInfo object containing the user's full DN and requested attributes. + * TODO: Need to optionally supply required attributes here for the search. + */ + LdapUserInfo searchForUser(String username); + +} diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java b/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java new file mode 100644 index 0000000000..419b14ce5a --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java @@ -0,0 +1,149 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ldap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; + +import javax.naming.Context; +import javax.naming.NamingException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * LDAP Utility methods. + * + * @author Luke Taylor + * @version $Id$ + */ +public class LdapUtils { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(LdapUtils.class); + + //~ Methods ================================================================ + + public static void closeContext(Context ctx) { + try { + if (ctx != null) { + ctx.close(); + } + } catch (NamingException e) { + logger.error("Failed to close context.", e); + } + } + + /** + * Parses the supplied LDAP URL. + * @param url the URL (e.g. ldap://monkeymachine:11389/dc=acegisecurity,dc=org). + * @return the URI object created from the URL + * @throws IllegalArgumentException if the URL is null, empty or the URI syntax is invalid. + */ + public static URI parseLdapUrl(String url) { + Assert.hasLength(url); + + try { + return new URI(url); + } catch (URISyntaxException e) { + IllegalArgumentException iae = new IllegalArgumentException("Unable to parse url: " + url); + iae.initCause(e); + throw iae; + } + } + + public static byte[] getUtf8Bytes(String s) { + try { + return s.getBytes("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 escapeNameForFilter(String name) { + // TODO: Implement escaping as defined in RFC 2254 + // Think this is probably not needed as filter args should be escaped automatically + // by the search methods. + + return name; + } + + /** + * Obtains the part of a DN relative to a supplied base context. + *

+ * If the DN is "cn=bob,ou=people,dc=acegisecurity,dc=org" and the base context + * name is "ou=people,dc=acegisecurity,dc=org" it would return "cn=bob". + *

+ * + * @param fullDn the DN + * @param baseCtx the context to work out the name relative to. + * @return the + * @throws NamingException any exceptions thrown by the context are propagated. + */ + public static String getRelativeName(String fullDn, Context baseCtx) throws NamingException { + String baseDn = baseCtx.getNameInNamespace(); + + if (baseDn.length() == 0) { + return fullDn; + } + + if (baseDn.equals(fullDn)) { + return ""; + } + + int index = fullDn.lastIndexOf(baseDn); + + Assert.isTrue(index > 0, "Context base DN is not contained in the full DN"); + + // remove the base name and preceding comma. + return fullDn.substring(0, index - 1); + } + + /** + * Works out the root DN for an LDAP URL. + *

+ * For example, the URL ldap://monkeymachine:11389/dc=acegisecurity,dc=org + * has the root DN "dc=acegisecurity,dc=org". + * + * + * @param url the LDAP URL + * @return the root DN + */ + public static String parseRootDnFromUrl(String url) { + Assert.hasLength(url); + + String urlRootDn = null; + + if (url.startsWith("ldap:") || url.startsWith("ldaps:")) { + + URI uri = parseLdapUrl(url); + + urlRootDn = uri.getPath(); + + } else { + // Assume it's an embedded server + urlRootDn = url; + } + + if (urlRootDn.startsWith("/")) { + urlRootDn = urlRootDn.substring(1); + } + + return urlRootDn; + } +} diff --git a/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java b/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java new file mode 100644 index 0000000000..8b9363bea9 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java @@ -0,0 +1,181 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.ldap.search; + +import org.acegisecurity.userdetails.UsernameNotFoundException; +import org.acegisecurity.BadCredentialsException; +import org.acegisecurity.ldap.LdapUserSearch; +import org.acegisecurity.ldap.LdapUtils; +import org.acegisecurity.ldap.InitialDirContextFactory; +import org.acegisecurity.ldap.LdapUserInfo; +import org.acegisecurity.ldap.LdapDataAccessException; +import org.springframework.util.Assert; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.directory.DirContext; +import javax.naming.NamingException; +import javax.naming.NamingEnumeration; + +/** + * LdapUserSearch implementation which uses an Ldap filter to locate the user. + * + * @author Robert Sanders + * @author Luke Taylor + * @version $Id$ + */ +public class FilterBasedLdapUserSearch implements LdapUserSearch { + //~ Static fields/initializers ============================================= + + private static final Log logger = LogFactory.getLog(FilterBasedLdapUserSearch.class); + + //~ Instance fields ======================================================== + + /** + * Context name to search in, relative to the root DN of the configured + * InitialDirContextFactory. + */ + private String searchBase = ""; + + /** + * If true then searches the entire subtree as identified by context, + * if false (the default) then only searches the level identified by the context. + */ +// private boolean searchSubtree = false; + + private int searchScope = SearchControls.ONELEVEL_SCOPE; + + /** + * 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 search methods in {@link javax.naming.directory.DirContext DirContext} + * for more information. + *

+ * In this case, the username is the only parameter. + *

+ * Possible examples are: + * + * TODO: more examples. + * + */ + private String searchFilter; + + /** + * The time (in milliseconds) which to wait before the search fails; + * the default is zero, meaning forever. + */ + private int searchTimeLimit = 0; + + private InitialDirContextFactory initialDirContextFactory; + + //~ Methods ================================================================ + + public FilterBasedLdapUserSearch(String searchBase, + String searchFilter, + InitialDirContextFactory initialDirContextFactory) { + Assert.notNull(initialDirContextFactory, "initialDirContextFactory must not be null"); + Assert.notNull(searchFilter, "searchFilter must not be null."); + Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable)."); + + this.searchFilter = searchFilter; + this.initialDirContextFactory = initialDirContextFactory; + this.searchBase = searchBase; + + if(searchBase.length() == 0) { + logger.info("SearchBase not set. Searches will be performed from the root: " + + initialDirContextFactory.getRootDn()); + } + } + + //~ Methods ================================================================ + + /** + * Return the LdapUserInfo containing the user's information, or null if + * no SearchResult is found. + * + * @param username the username to search for. + */ + public LdapUserInfo searchForUser(String username) { + DirContext ctx = initialDirContextFactory.newInitialDirContext(); + SearchControls ctls = new SearchControls(); + ctls.setTimeLimit( searchTimeLimit ); + ctls.setSearchScope( searchScope ); + + if (logger.isDebugEnabled()) { + logger.debug("Searching for user '" + username + "', in context " + ctx + + ", with user search " + this.toString()); + } + + try { + String[] args = new String[] { LdapUtils.escapeNameForFilter(username) }; + + NamingEnumeration results = ctx.search(searchBase, searchFilter, args, ctls); + + if (!results.hasMore()) { + throw new UsernameNotFoundException("User " + username + " not found in directory."); + } + + SearchResult searchResult = (SearchResult)results.next(); + + if (results.hasMore()) { + throw new BadCredentialsException("Expected a single user but search returned multiple results"); + } + + StringBuffer userDn = new StringBuffer(searchResult.getName()); + + if (searchBase.length() > 0) { + userDn.append(","); + userDn.append(searchBase); + } + + userDn.append(","); + userDn.append(ctx.getNameInNamespace()); + + return new LdapUserInfo(userDn.toString(), searchResult.getAttributes()); + + } catch(NamingException ne) { + throw new LdapDataAccessException("User Couldn't be found due to exception", ne); + } finally { + LdapUtils.closeContext(ctx); + } + } + + public void setSearchSubtree(boolean searchSubtree) { +// this.searchSubtree = searchSubtree; + this.searchScope = searchSubtree ? + SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE; + } + + public void setSearchTimeLimit(int searchTimeLimit) { + this.searchTimeLimit = searchTimeLimit; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("[ searchFilter: '").append(searchFilter).append("', "); + sb.append("searchBase: '").append(searchBase).append("'"); + sb.append(", scope: ").append(searchScope == + SearchControls.SUBTREE_SCOPE ? "subtree" : "single-level, "); + sb.append("searchTimeLimit: ").append(searchTimeLimit).append(" ]"); + + return sb.toString(); + } +} diff --git a/core/src/main/java/org/acegisecurity/ldap/search/package.html b/core/src/main/java/org/acegisecurity/ldap/search/package.html new file mode 100644 index 0000000000..4543b9ef20 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/ldap/search/package.html @@ -0,0 +1,5 @@ + + +LdapUserSearch implementations. These may be used to locate the user in the directory. + +