diff --git a/sandbox/src/main/java/org/acegisecurity/providers/dao/ldap/LdapPasswordAuthenticationDao.java b/sandbox/src/main/java/org/acegisecurity/providers/dao/ldap/LdapPasswordAuthenticationDao.java
index e092b3e056..a2dee1b30f 100644
--- a/sandbox/src/main/java/org/acegisecurity/providers/dao/ldap/LdapPasswordAuthenticationDao.java
+++ b/sandbox/src/main/java/org/acegisecurity/providers/dao/ldap/LdapPasswordAuthenticationDao.java
@@ -1,17 +1,17 @@
/* 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.
- */
+*
+* 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 net.sf.acegisecurity.providers.dao.ldap;
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
@@ -44,365 +45,857 @@ import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/**
- * This is an example PasswordAuthenticationDao
implementation
- * using LDAP service for user authentication.
- *
-*
Example use:
-* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao">
-* <property name="host"><value>sydney.ipov.info</value></property>
-* <property name="rootContext"><value>dc=ipov,dc=info</value></property>
-* <property name="userContext"><alue>ou=Users</value></property>
-* <property name="userAttribute"><value>uid</value></property>
-* </bean>
-* ...
-* <bean id="authenticationProvider" class="net.sf.acegisecurity.providers.dao.PasswordDaoAuthenticationProvider">
-* <property name="passwordAuthenticationDao"><ref local="ldapDaoImpl"/></property>
-* </bean>
+*
+* LdapPasswordAuthenticationDao allows you to authenticate user's against LDAP Directories via JNDI. +* LDAP administrators have a wide variety of options available to them when configuring a server, +* so the LdapPasswordAuthenticationDao has a wide variety of ways that it can be configured.
+* +* +*+* Currently LdapPasswordAuthenticationDao authenticates a username/password pair by +* 'logging in to' the LDAP server via a JNDI bind() operation. +* There is some flexibility in that multiple userContexts can be set; the +* LdapPasswordAuthenticationDao will attempt to bind() against each until either a bind() +* operation succeeds or all userContexts have been tried.
+* +*+* LdapPasswordAuthenticationDao offers 3 modes for determining the roles assigned to a user +* (these can be used in combination).
+*+* If the both the userRolesAttributes method and the roleContexts search method are used, +* and if both return results, then the final list of roles will be determined by combining the two results. *
- * - * @author Karel Miarka - * @author Daniel Miller - * @author Robert Sanders - */ +*+* One final operation is performed before returning the list of GrantedAuthority +* objects associated with the user: if the the upperCaseRoleNames property is set to +* true the user's role names are capitalized; then the values of the rolePrefix and roleSuffix +* are used to wrap any role names. +*
+*+* At this point a few examples will probably help clear up the confusion +* that the abstract description above may have created. +* Unless otherwise noted, all examples will use the following base set of assumptions: +* An LDAP server reachable at the url ldap://ldap.mycompany.com:389/ +* and a rootContext of dc=mycompany,dc=com. The following would be you AuthenticationProvider: +*
+* <bean id="authenticationProvider" class="net.sf.acegisecurity.providers.dao.PasswordDaoAuthenticationProvider"> +* <property name="passwordAuthenticationDao"><ref local="ldapDaoImpl"/></property> +* </bean> +*+* +* +*
+* First example: your users are stored under the rootContext as cn=USERNAME,ou=Users; +* user objects have the attribute memberOf which contains the names of any roles they +* have been granted. You would use the following bean configuration: +*
+* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao"> +* <property name="url"><value>ldap://ldap.mycompany.com:389/</value></property> +* <property name="rootContext"><value>dc=mycompany,dc=com</value></property> +* <property name="userContext"><alue>cn={0},ou=Users,dc=mycompany,dc=com</value></property> +* <property name="userRolesAttribute"><value>memberOf</value></property> +* </bean> +*+* +* +*
+* Second example: users are stored under the rootContext as uid=USERNAME,ou=Users; +* user object have no role information. Groups (aka roles) are stored as objects +* under the context ou=Groups and have an attribute memberUid which contains the +* full distinguished name of the user. You would use the following bean configuration: +*
+* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao"> +* <property name="url"><value>ldap://ldap.mycompany.com:389/</value></property> +* <property name="rootContext"><value>dc=mycompany,dc=com</value></property> +* <!-- here {0} is the username --> +* <property name="userContext"><value>uid={0},ou=Users,dc=mycompany,dc=com</value></property> +* <property name="roleContext"><value>ou=Groups</value></property> +* <!-- here {0} is the distinguished name (which would be uid=USERNAME,ou=Users,dc=mycompany,cd=com +* and {1} is the username. --> +* <property name="roleAttributesSearchFilter"><value>(memberUid={0})</value></property> +* <property name="roleNameAttribute"><value>memberUid</value></property> +* </bean> +*+* +* +*
+* Third example: under the rootContext your users are stored as uid=USERNAME,ou=Users. +* You don't care about the roles stored in the LDAP, all you want to know is if the user +* can login via LDAP. You would use the following bean configuration: +*
+* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao"> +* <property name="url"><value>ldap://ldap.mycompany.com:389/</value></property> +* <property name="rootContext"><value>dc=mycompany,dc=com</value></property> +* <property name="userContext"><alue>cn={0},ou=Users,dc=mycompany,dc=com</value></property> +* <property name="defaultRolename"><value>USER</value></property> +* </bean> +*+* +* +*
+* Forth example (something more complex): under the rootContext your users are stored in to seperate subContexts. +* Your internal users are under uid=USERNAME,ou=Users; you also have client logins stored +* under the context uid=USERNAME,ou=Clients. For internal users role information is stored +* under the context ou=Groups and have an attribute memberUid which contains the +* full distinguished name of the user. For clients, role information is stored as an attribute +* memberOf as part of their user object. You could split the definitions up into two separate +* LdapPasswordAuthenticationDao beans, but you could also use: +*
+* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao"> +* <property name="url"><value>ldap://ldap.mycompany.com:389/</value></property> +* <property name="rootContext"><value>dc=mycompany,dc=com</value></property> +* <!-- here {0} is the username --> +* <property name="userContexts"> +* <list> +* <value>uid={0},ou=Users,dc=mycompany,dc=com</value> +* <value>uid={0},ou=Clients,dc=mycompany,dc=com</value> +* </list> +* </property> +* <property name="userRolesAttribute"><value>memberOf</value></property> +* <property name="roleContext"><value>ou=Groups</value></property> +* <!-- here {0} is the distinguished name (which would be uid=USERNAME,ou=Users,dc=mycompany,cd=com +* and {1} is the username. --> +* <property name="roleAttributesSearchFilter"><value>(memberUid={0})</value></property> +* <property name="roleNameAttributes"><value>memberUid</value></property> +* </bean> +*+* +* +* @author Karel Miarka +* @author Daniel Miller +* @author Robert Sanders +*/ public class LdapPasswordAuthenticationDao implements PasswordAuthenticationDao { - //~ Static fields/initializers ============================================= + + /** InnerClass used to keep context variable together. */ + private class UserContext { + public DirContext dirContext; + public String userPrincipal; + + /** + * Get the attribute(s) to match when searching for the user object. This + * implementation returns a "distinguishedName" attribute with the value + * returned by
getUserPrincipal(username)
. A subclass may
+ * customize this behavior by overriding getUserPrincipal
+ * and/or getUsernameAttributes
.
+ *
+ * @param username
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Attributes getUsernameAttributes() {
+ Attributes matchAttrs = new BasicAttributes(true); // ignore case
+ matchAttrs.put(new BasicAttribute("distinguishedName", userPrincipal));
+ return matchAttrs;
+ }
+ }
+
- public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
- private static final transient Log log = LogFactory.getLog(LdapPasswordAuthenticationDao.class);
+ public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
- //~ Instance fields ========================================================
+ private static final transient Log log = LogFactory
+ .getLog(LdapPasswordAuthenticationDao.class);
- private String host;
+ /** Type of authentication within LDAP; default is simple. */
+ private String authenticationType = "simple";
- /** The INITIAL_CONTEXT_FACTORY for use with JNDI. */
- private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
- private String rootContext;
- private String userAttribute = "CN"; // ??? is this the right code??
- private String userContext = "CN=Users";
- private String[] rolesAttributes = {"memberOf"};
- private int port = 389;
+ /** If set to a non-null value, and a user can be bound to a LDAP Conext,
+ * but no role information is found then this role is automatically added.
+ * If null (the default) then a BadCredentialsException is thrown
+ *
+ * For example; if you have an LDAP directory with no role information + * stored, you might simply want to give any user who can login a role of "USER".
+ */ + private String defaultRole = null; - //~ Methods ================================================================ + /** 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"; + + /** Internal variable, concatenation */ + private String providerUrl; - /** - * Set hostname or IP address of the host running LDAP server. - * - * @param hostname DOCUMENT ME! - */ - public void setHost(String hostname) { - this.host = hostname; - } + /** Used to build LDAP Search Filter for finding roles (in the roleContexts) + * pointing to a user. Uses MessageFormat like tokens; {0} is the + * user's DistiguishedName, {1} is the user's username. + * For more information on syntax see + * javax.naming.directory.DirContext.search(), or RFC 2254. + * + *Example: if each group has an attribute 'memberUid' with values being + * the usernames of the user's in that group, then the value of this property + * would be (memberUid={1})
+ **/ + private String roleAttributesSearchFilter; - /** - * DOCUMENT ME! - * - * @return Returns the host. - */ - public String getHost() { - return host; - } + /** Contexts to search for role's (which point to the user id). + *Example, if you have a Groups object containing Groups of users then + * the expression: ou=Groups,dc=mycompany,dc=com might be used; + * alternatively, if rootContext="dc=mycompany,dc=com" then simply use "ou=Groups" here. + **/ + private String[] roleContexts; + + /** Attribute(s) of any role object returned from the roleContexts to use as role-names. + * Warning: if you do role lookups using the roleContexts and + * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes + * will be returned. + * + **/ + private String[] roleNameAttributes; + + /** Prefix to be associated with any roles found for a user, + * defaults to an empty string. + * Older versions of this class used "ROLE_" for this value. */ + private String rolePrefix = ""; + + /** Suffix to be associated with any roles found for a user, + * defaults to an empty string. */ + private String roleSuffix = ""; - /** - * DOCUMENT ME! - * - * @param initialContextFactory The initialContextFactory to set. - */ - public void setInitialContextFactory(String initialContextFactory) { - this.initialContextFactory = initialContextFactory; - } + /** Root context of the LDAP Connection, if any is needed. + *
Example: dc=mycompany,dc=com
+ *Note: It is usually preferable to add this data as part of the + * userContexts and/or roleContexts attributes.
+ **/ + private String rootContext = ""; + + /** If true then all role name values returned from the directory + * will be converted to uppercase. + */ + private boolean upperCaseRoleNames = false; + + /** + * LDAP URL (without the port) of the LDAP server to connect to; example + * ldap://dir.mycompany.com:389/ (port 389 is the standard LDAP port). + */ + private String url; + + /** One or more LDAP Contexts which contain user account information, use the + * MessageFormat key {0} to denote location where the user's username should + * be inserted into the expression to create a DistiguishedName. + *Example:
cn={0},ou=Users,dc=mycompnay,dc=com
+ *Alternatively, if you had set rootContext="dc=mycompany,dc=com" then + * the first example would be rewritten as cn={0},ou=Users.
+ **/ + private MessageFormat[] userContexts; - /** - * DOCUMENT ME! - * - * @return Returns the initialContextFactory. - */ - public String getInitialContextFactory() { - return initialContextFactory; - } + /** Name(s) of the attribute(s) for a user account object + * contaning role names assigned to the user. Leave unset if there are none. + * Consult your LDAP server administrator to determine these value(s). + * + **/ + private String[] userRolesAttributes; + + /** + * + * @param results Result of searching on of the roleContexts for matches against the current user. + * @param roles List of roles the user has already been assigned. + * @throws NamingException + */ + protected void addAnyRolesFound(NamingEnumeration results, Collection roles) throws NamingException { + while (results.hasMore()) { + SearchResult result = (SearchResult)results.next(); + Attributes attrs = result.getAttributes(); + if (attrs == null) { + continue; + } + // Here we loop over the attributes returned in the SearchResult + // TODO replace with Utility method call: + NamingEnumeration e = attrs.getAll(); + while (e.hasMore()) { + Attribute a = (Attribute)e.next(); + for (int i = 0; i < a.size(); i++) { + roles.add( (String)a.get(i) ); + } + } + } + } - /** - * Set the port on which is running the LDAP server.GrantedAuthorities
given the list of roles
+ * obtained from the LDAP context. Delegates to
+ * getGrantedAuthority(String ldapRole)
. This function may
+ * be overridden in a subclass.
+ *
+ * @param ldapRoles
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected GrantedAuthority[] getGrantedAuthorities(String[] ldapRoles) {
+ GrantedAuthority[] grantedAuthorities = new GrantedAuthority[ldapRoles.length];
- public String getProviderURL() {
- StringBuffer providerUrl = new StringBuffer();
- providerUrl.append("ldap://");
- providerUrl.append(this.host);
- providerUrl.append(":");
- providerUrl.append(this.port);
- providerUrl.append("/");
- providerUrl.append(this.rootContext);
+ for (int i = 0; i < ldapRoles.length; i++) {
+ grantedAuthorities[i] = getGrantedAuthority(ldapRoles[i]);
+ }
- return providerUrl.toString();
- }
+ return grantedAuthorities;
+ }
- /**
- * Set the name of user object's attribute(s) which contains the list of
- * user's role names. The role is converted to upper case and a "ROLE_"
- * prefix is added when GrantedAuthority
is created. Default
- * value: { "memberOf" }.
- *
- * @param rolesAttributes DOCUMENT ME!
- */
- public void setRolesAttributes(String[] rolesAttributes) {
- this.rolesAttributes = rolesAttributes;
- }
+ /**
+ * Get a GrantedAuthority
given a role obtained from the LDAP
+ * context. If found in the LDAP role, the following characters are
+ * converted to underscore: ',' (comma), '=' (equals), ' ' (space) This
+ * function may be overridden in a subclass.
+ *
+ * @param ldapRole
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected GrantedAuthority getGrantedAuthority(String ldapRole) {
+ String roleName = rolePrefix + ldapRole.toUpperCase() + roleSuffix;
+ if (upperCaseRoleNames) {
+ roleName = roleName.toUpperCase();
+ }
+ GrantedAuthority ga = new GrantedAuthorityImpl(roleName);
- /**
- * Set the root context to which you attempt to log in. GrantedAuthorities
given the list of roles
- * obtained from the LDAP context. Delegates to
- * getGrantedAuthority(String ldapRole)
. This function may be
- * overridden in a subclass.
- *
- * @param ldapRoles DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- protected GrantedAuthority[] getGrantedAuthorities(String[] ldapRoles) {
- GrantedAuthority[] grantedAuthorities = new GrantedAuthority[ldapRoles.length];
+ Collection roles = getRolesFromUserContext(userContext, getUserRolesAttributes());
+ Collection roles2 = getRolesFromRoleSearch(userContext, username, getRoleNameAttributes());
+ if (null != roles2) {
+ roles.addAll(roles2);
+ }
+
+ userContext.dirContext.close();
- for (int i = 0; i < ldapRoles.length; i++) {
- grantedAuthorities[i] = getGrantedAuthority(ldapRoles[i]);
- }
+
+ if (roles.isEmpty()) {
+ if (null == defaultRole) {
+ throw new BadCredentialsException("The user has no granted "
+ + "authorities or the rolesAttribute is invalid");
+ } else {
+ roles.add(defaultRole);
+ }
+ }
- return grantedAuthorities;
- }
+ String[] ldapRoles = (String[]) roles.toArray(new String[] {});
+
+ return new User(username, password, true, true, true,
+ getGrantedAuthorities(ldapRoles));
+ } catch (AuthenticationException ex) {
+ throw new BadCredentialsException(
+ BAD_CREDENTIALS_EXCEPTION_MESSAGE, ex);
+ } catch (CommunicationException ex) {
+ throw new DataAccessResourceFailureException(ex.getRootCause()
+ .getMessage(), ex);
+ } catch (NamingException ex) {
+ throw new DataAccessResourceFailureException(ex.getMessage(), ex);
+ }
+ }
+
+ /** If set to a non-null value, and a user can be bound to a LDAP Conext,
+ * but no role information is found then this role is automatically added.
+ * If null (the default) then a BadCredentialsException is thrown
+ *
+ * For example; if you have an LDAP directory with no role information + * stored, you might simply want to give any user who can login a role of "USER".
+ * + * @param defaultRole The defaultRole to set. + */ + public void setDefaultRole(String defaultRole) { + this.defaultRole = defaultRole; + } - /** - * Get aGrantedAuthority
given a role obtained from the LDAP
- * context. If found in the LDAP role, the following characters are
- * converted to underscore: ',' (comma), '=' (equals), ' ' (space) This
- * function may be overridden in a subclass.
- *
- * @param ldapRole DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- protected GrantedAuthority getGrantedAuthority(String ldapRole) {
- GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_"
- + ldapRole.toUpperCase());
+ /** 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.
+ *
+ * @param initialContextFactory The InitialContextFactory for creating the root JNDI context;
+ * defaults to "com.sun.jndi.ldap.LdapCtxFactory"
+ */
+ public void setInitialContextFactory(String initialContextFactory) {
+ this.initialContextFactory = initialContextFactory;
+ }
- if (log.isDebugEnabled()) {
- log.debug("GrantedAuthority: " + ga);
- }
+ /** Name(s) of the attribute(s) for a user account object
+ * contaning role names assigned to the user. Leave unset if there are none.
+ * Consult your LDAP server administrator to determine these value(s).
+ *
+ * @param roleUserAttributes
+ * The roleUserAttributes to set.
+ */
+ public void setRoleAttributesSearchFilter(String roleAttributesSearchArgs) {
+ this.roleAttributesSearchFilter = roleAttributesSearchArgs;
+ }
+
+ /** Shortcut for setRoleContexts( new String[]{roleContext} ); */
+ public void setRoleContext(String roleContext) {
+ setRoleContexts( new String[]{roleContext} );
+ }
- return ga;
- }
+ /** Contexts to search for role's (which point to the user id).
+ * Example, if you have a Groups object containing Groups of users then + * the expression: ou=Groups,dc=mycompany,dc=com might be used; + * alternatively, if rootContext="dc=mycompany,dc=com" then simply use "ou=Groups" here. + * + * @param roleContexts Array of MessageFormat String's for Contexts that store role information for users. + */ + public void setRoleContexts(String[] roleContexts) { + this.roleContexts = roleContexts; + } + + /** Used to build LDAP Search Filter for finding roles (in the roleContexts) + * pointing to a user. Uses MessageFormat like tokens; {0} is the + * user's DistiguishedName, {1} is the user's username. + * For more information on syntax see + * javax.naming.directory.DirContext.search(), or RFC 2254. + * + *
Example: if each group has an attribute 'memberUid' with values being + * the usernames of the user's in that group, then the value of this property + * would be (memberUid={1})
+ * + * @param roleNameAttributes The roleNameAttributes to set. + */ + public void setRoleNameAttribute(String roleNameAttribute) { + setRoleNameAttributes( new String[] {roleNameAttribute} ); + } + + /** Attribute(s) of any role object returned from the roleContexts to use as role-names. + * Warning: if you do role lookups using the roleContexts and + * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes + * will be returned. + * + * @param roleNameAttributes The roleNameAttributes to set. + */ + public void setRoleNameAttributes(String[] roleNameAttributes) { + this.roleNameAttributes = roleNameAttributes; + } + + /** Prefix to be associated with any roles found for a user, + * defaults to an empty string. + * Older versions of this class used "ROLE_" for this value. + * + * @param rolePrefix The rolePrefix to set. + */ + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + + /** Suffix to be associated with any roles found for a user, + * defaults to an empty string. + * + * @param roleSuffix The roleSuffix to set. + */ + public void setRoleSuffix(String roleSuffix) { + this.roleSuffix = roleSuffix; + } + + /** Root context of the LDAP Connection, if any is needed. + *Example: dc=mycompany,dc=com
+ *Note: It is usually preferable to add this data as part of the + * userContexts and/or roleContexts attributes.
+ * + * @param rootContext The rootContext which to connect to; + * typically it could look something like: dc=mycompany,dc=com. + */ + public void setRootContext(String rootContext) { + this.rootContext = rootContext; + } + + /** If true then all role name values returned from the directory + * will be converted to uppercase. + * + * @param upperCaseRoleNames The upperCaseRoleNames to set. + */ + public void setUpperCaseRoleNames(boolean upperCaseRoleNames) { + this.upperCaseRoleNames = upperCaseRoleNames; + } - /** - * DOCUMENT ME! - * - * @param name DOCUMENT ME! - * - * @return Return true if the given name is a role attribute. - */ - protected boolean isRoleAttribute(String name) { - if (name != null) { - for (int i = 0; i < rolesAttributes.length; i++) { - if (name.equals(rolesAttributes[i])) { - return true; - } - } - } + /** + * @param host The LDAP URL to conntect to; example: ldap://ldap.mycompany.com:389/ + */ + public void setURL(String url) { + this.url = url; + } + + /** Shortcut for setUserContexts( new String[]{userContext} ); */ + public void setUserContext(String userContext) { + setUserContexts( new String[]{userContext} ); + } - return false; - } + /** One or more LDAP Contexts which contain user account information, use the + * MessageFormat key {0} to denote location where the user's username should + * be inserted into the expression to create a DistiguishedName. + *Example:
cn={0},ou=Users,dc=mycompnay,dc=com
+ *Alternatively, if you had set rootContext="dc=mycompany,dc=com" then + * the first example would be rewritten as cn={0},ou=Users.
+ * + * @param userContexts + * The userContexts to set. + */ + public void setUserContexts(String[] userContexts) { + this.userContexts = new MessageFormat[userContexts.length]; + for (int i = 0; i < userContexts.length; i++) { + this.userContexts[i] = new MessageFormat(userContexts[i]); + } + } + + /** Shortcut for setUserRolesAttributes(new String[]{userRolesAttribute}); */ + public void setUserRolesAttribute(String userRolesAttribute) { + this.userRolesAttributes = new String[]{userRolesAttribute}; + } - /** - * Get the attributes to that contain role information. This function may - * be overridden in a subclass. - * - * @return DOCUMENT ME! - */ - protected String[] getRolesAttributeNames() { - return rolesAttributes; - } + /** Attribute(s) of any role object returned from the roleContexts to use as role-names. + * Warning: if you do role lookups using the roleContexts and + * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes + * will be returned. + * + * @param userRolesAttributes + * The userRolesAttributes to set. + */ + public void setUserRolesAttributes(String[] userRolesAttributes) { + this.userRolesAttributes = userRolesAttributes; + } - protected Collection getRolesFromContext(DirContext ctx, - String userContext, String username, String[] roleAttributes) - throws NamingException { - List roles = new ArrayList(); - - if (log.isDebugEnabled()) { - String rolesString = ""; - - for (int i = 0; i < roleAttributes.length; i++) { - rolesString += (", " + roleAttributes[i]); - } - - log.debug("Searching user context '" + userContext + "' for roles " - + "attributes: " + rolesString.substring(1)); - } - - NamingEnumeration answer = ctx.search(userContext, - getUsernameAttributes(username), roleAttributes); - - while (answer.hasMore()) { - SearchResult sr = (SearchResult) answer.next(); - NamingEnumeration attrs = sr.getAttributes().getAll(); - - while (attrs.hasMore()) { - Attribute attr = (Attribute) attrs.next(); - - if (isRoleAttribute(attr.getID())) { - NamingEnumeration rolesAttr = attr.getAll(); - - while (rolesAttr.hasMore()) { - String role = (String) rolesAttr.next(); - roles.add(role); - - if (log.isDebugEnabled()) { - log.debug("Role read: " + attr.getID() + "=" + role); - } - } - } - } - } - - return roles; - } - - /** - * Get theContext.SECURITY_PRINCIPAL
for the given username
- * string. This implementation returns the userBase for JNDI / LDAP
- * lookup.
- *
- * @param username DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- protected String getUserPrincipal(String username) {
- StringBuffer principal = new StringBuffer(userAttribute);
- principal.append("=");
- principal.append(username);
- principal.append(",");
- principal.append(this.userContext);
- principal.append(",");
- principal.append(this.rootContext);
-
- return principal.toString();
- }
-
- /**
- * Get the attribute(s) to match when searching for the user object. This
- * implementation returns a "distinguishedName" attribute with the value
- * returned by getUserPrincipal(username)
. A subclass may
- * customize this behavior by overriding getUserPrincipal
- * and/or getUsernameAttributes
.
- *
- * @param username DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- */
- protected Attributes getUsernameAttributes(String username) {
- Attributes matchAttrs = new BasicAttributes(true); // ignore case
- matchAttrs.put(new BasicAttribute("distinguishedName",
- getUserPrincipal(username)));
-
- return matchAttrs;
- }
}