Craig I've attached a patch to enhance the current JNDI realm implementation. This includes three features that have been requested a number of times and appear in the draft functional specification:
** the realm can authenticate by binding to the directory as the user. This mode of operation is usually to be preferred unless HTTP digest authentication is required. The realm will automatically authenticate in this way if the "userPassword" configuration attribute is not specified. The "connectionName" and "connectPassword" properties are used when searching the directory or retrieving role information. In this mode, anonymous authentication is often sufficient, in which case these need not be specified. ** the realm can search the directory for the user's entry. It will do this if the "userPattern" configuration attribute is not specified. Three new configuration parameters control the search for the user's directory entry: userSearch - a pattern specifying the LDAP search filter for selecting users. Use {0} to substitute in the username. userBase - the base element for user searches. If not specified the top level element in the directory context will be used. useSubtree - set to true if you want user searches to search subtrees of the element selected by userBase. The default value of false causes only the top level element to be searched. ** the realm can combine roles held as the values of an attribute in the user's entry with those retrieved from a search for roles. The search for roles takes place as before. If the roleSearch configuration attribute is not specified the realm will look only in the user's entry for roles. This feature uses one new configuration attribute: userRoleName - the name of an attribute in the user's entry containing the names of roles. If not specified the directory is searched for roles as before. Note that the new configuration interface is backwards compatible with the current JNDIRealm. For existing users the realm will behave just as it did before - no configuration changes are required. The existing documentation is still accurate about the subset of functionality it describes. Here is a sample realm configuration that demonstrates all three new features. It is assumed that an anonymous bind is sufficient to search the directory for the user and retrieve the role information. The user is found by searching the directory and authenticated by binding to the directory with the credentials presented by the user. Role names are contained both in the "memberOf" attribute of the user's own directory entry and in the "cn" attribute of group entries to which the user belongs <Realm className="org.apache.catalina.realm.JNDIRealm" debug="99" connectionURL="ldap://localhost:389" userBase="ou=people,dc=mycompany,dc=com" userSearch="(uid={0})" userRoleName="memberOf" roleSearch="(uniqueMember={0})" roleBase="ou=groups,dc=mycompany,dc=com" roleName="cn" roleSubtree="true" /> I've not addressed issues of connection management etc in this patch, though there is certainly room for improvement (context pooling etc). In a previous message you mentioned leveraging the "global JNDI resources" capabilities for that, and Tony Danbura has also expressed an interest in this area. However the enhancements in this patch are independent of that issue, and should not complicate it. In particular, note that the "bind mode" functionality is implemented in a way that still uses a single directory context that can be returned to the pool. This is achieved by changing the security environment of the context before and after binding, rather than creating a different context for the bind. With an LDAP v3 server this should be an efficient approach since the underlying connection to the directory server does not change. An LDAP v2 server must create a new connection for each bind operation, so context pooling will not be so effective, but that does not affect the logic. I'd be very grateful if you would take a look at this patch and hopefully submit it. If it is accepted I promise I'll update the documentation to cover the new features! Thanks, John.
Index: JNDIRealm.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java,v retrieving revision 1.5 diff -u -r1.5 JNDIRealm.java --- JNDIRealm.java 7 Sep 2001 20:45:12 -0000 1.5 +++ JNDIRealm.java 1 Feb 2002 21:17:50 -0000 @@ -68,6 +68,9 @@ import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; +import javax.naming.NameParser; +import javax.naming.Name; +import javax.naming.AuthenticationException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; @@ -87,18 +90,39 @@ * <ul> * <li>Each user that can be authenticated is represented by an individual * element in the top level <code>DirContext</code> that is accessed - * via the <code>connectionURL</code> property. This element has the - * following characteristics: + * via the <code>connectionURL</code> property.</li> + * + * <li>Each user element has a distinguished name that can be formed by + * substituting the presented username into a pattern configured by the + * <code>userPattern</code> property.</li> + * + * <li>Alternatively, if the <code>userPattern</code> property is not + * specified, a unique element can be located by searching the directory + * context. In this case + * <ul> + * <li>The <code>userSearch</code> pattern specifies the search filter + * after substitution of the username.</li> + * <li>The <code>userBase</code> property can be set to the element that + * is the base of the subtree containing users. If not specified, + * the search base is the top-level context.</li> + * <li>The <code>userSubtree</code> property can be set to + * <code>true</code> if you wish to search the entire subtree of the + * directory context. The default value of <code>false</code> + * requests a search of only the current level.</li> + * </ul> + * </li> + * + * <li>The user may be authenticated by binding to the directory with the + * username and password presented. This method is used when the + * <code>userPassword</code> property is not specified.</li> + * + * <li>The user may be authenticated by retrieving the value of an attribute + * from the directory and comparing it explicitly with the value presented + * by the user. This method is used when the <code>userPassword</code> + * property is specified, in which case * <ul> - * <li>The distinguished name (<code>dn</code>) attribute of this element - * contains the username that is being presented for authentication. - * </li> - * <li>The distinguished name can be represented by a pattern passed to - * an instance of <code>MessageFormat</code>, where the string "{0}" - * in the pattern is replaced by the username being presented.</li> - * <li>The element for this user contains an attribute named by the - * <code>userPassword</code> property. The value of this attribute - * is retrieved for use in authentication.</li> + * <li>The element for this user must contain an attribute named by the + * <code>userPassword</code> property. * <li>The value of the user password attribute is either a cleartext * String, or the result of passing a cleartext String through the * <code>RealmBase.digest()</code> method (using the standard digest @@ -108,7 +132,7 @@ * <code>RealmBase.digest()</code>) are equal to the retrieved value * for the user password attribute.</li> * </ul></li> - * <li>Each group of users that has been assigned a particular role is + * <li>Each group of users that has been assigned a particular role may be * represented by an individual element in the top level * <code>DirContext</code> that is accessed via the * <code>connectionURL</code> property. This element has the following @@ -132,6 +156,10 @@ * the <code>roleName</code> property) containing the name of the * role represented by this element.</li> * </ul></li> + * <li>In addition, roles may be represented by the values of an attribute + * in the user's element whose name is configured by the + * <code>userRoleName</code> property.</li> + * * <li>Note that the standard <code><security-role-ref></code> element in * the web application deployment descriptor allows applications to refer * to roles programmatically by names other than those used in the @@ -199,55 +227,90 @@ /** - * The base element for role searches. + * The base element for user searches. */ - protected String roleBase = ""; + protected String userBase = ""; + + + /** + * The message format used to search for a user, with "{0}" marking + * the spot where the username goes. + */ + protected String userSearch = null; /** * The MessageFormat object associated with the current - * <code>roleSearch</code>. + * <code>userSearch</code>. */ - protected MessageFormat roleFormat = null; + protected MessageFormat userSearchFormat = null; /** - * The name of the attribute containing the role name. + * Should we search the entire subtree for matching users? */ - protected String roleName[] = null; + protected boolean userSubtree = false; /** - * The message format used to select roles for a user, with "{0}" marking - * the spot where the distinguished name of the user goes. + * The attribute name used to retrieve the user password. */ - protected String roleSearch = null; + protected String userPassword = null; /** - * Should we search the entire subtree for matching memberships? + * The message format used to form the distinguished name of a + * user, with "{0}" marking the spot where the specified username + * goes. */ - protected boolean roleSubtree = false; + protected String userPattern = null; /** * The MessageFormat object associated with the current * <code>userPattern</code>. */ - protected MessageFormat userFormat = null; + protected MessageFormat userPatternFormat = null; /** - * The attribute name used to retrieve the user password. + * The base element for role searches. */ - protected String userPassword[] = null; + protected String roleBase = ""; /** - * The message format used to select a user, with "{0}" marking the - * spot where the specified username goes. + * The MessageFormat object associated with the current + * <code>roleSearch</code>. */ - protected String userPattern = null; + protected MessageFormat roleFormat = null; + + + /** + * The name of an attribute in the user's entry containing + * roles for that user + */ + protected String userRoleName = null; + + + /** + * The name of the attribute containing roles held elsewhere + */ + protected String roleName = null; + + + /** + * The message format used to select roles for a user, with "{0}" marking + * the spot where the distinguished name of the user goes. + */ + protected String roleSearch = null; + + + /** + * Should we search the entire subtree for matching memberships? + */ + protected boolean roleSubtree = false; + // ------------------------------------------------------------- Properties @@ -340,6 +403,96 @@ } + /** + * Return the base element for user searches. + */ + public String getUserBase() { + + return (this.userBase); + + } + + + /** + * Set the base element for user searches. + * + * @param userBase The new base element + */ + public void setUserBase(String userBase) { + + this.userBase = userBase; + + } + + + /** + * Return the message format pattern for selecting users in this Realm. + */ + public String getUserSearch() { + + return (this.userSearch); + + } + + + /** + * Set the message format pattern for selecting users in this Realm. + * + * @param userSearch The new user search pattern + */ + public void setUserSearch(String userSearch) { + + this.userSearch = userSearch; + if (userSearch == null) + userSearchFormat = null; + else + userSearchFormat = new MessageFormat(userSearch); + + } + + + /** + * Return the "search subtree for users" flag. + */ + public boolean getUserSubtree() { + + return (this.userSubtree); + + } + + + /** + * Set the "search subtree for users" flag. + * + * @param userSubtree The new search flag + */ + public void setUserSubtree(boolean userSubtree) { + + this.userSubtree = userSubtree; + + } + + + /** + * Return the user role name attribute name for this Realm. + */ + public String getUserRoleName() { + + return userRoleName; + } + + + /** + * Set the user role name attribute name for this Realm. + * + * @param userRoleName The new userRole name attribute name + */ + public void setUserRoleName(String userRoleName) { + + this.userRoleName = userRoleName; + + } + /** * Return the base element for role searches. @@ -368,10 +521,7 @@ */ public String getRoleName() { - if (this.roleName != null) - return (this.roleName[0]); - else - return (null); + return (this.roleName); } @@ -383,10 +533,7 @@ */ public void setRoleName(String roleName) { - if (roleName != null) - this.roleName = new String[] { roleName }; - else - this.roleName = null; + this.roleName = roleName; } @@ -444,10 +591,7 @@ */ public String getUserPassword() { - if (this.userPassword != null) - return (this.userPassword[0]); - else - return (null); + return (this.userPassword); } @@ -459,10 +603,7 @@ */ public void setUserPassword(String userPassword) { - if (userPassword != null) - this.userPassword = new String[] { userPassword }; - else - this.userPassword = null; + this.userPassword = userPassword; } @@ -486,9 +627,9 @@ this.userPattern = userPattern; if (userPattern == null) - userFormat = null; + userPatternFormat = null; else - userFormat = new MessageFormat(userPattern); + userPatternFormat = new MessageFormat(userPattern); } @@ -555,6 +696,7 @@ * Return the Principal associated with the specified username and * credentials, if there is one; otherwise return <code>null</code>. * + * @param context The directory context * @param username Username of the Principal to look up * @param credentials Password or other credentials to use in * authenticating this username @@ -566,13 +708,20 @@ String credentials) throws NamingException { - // Authenticate the specified username if possible - String dn = getUserDN(context, username, credentials); - if (dn == null) + if (username == null || credentials == null) return (null); - // Look up the associated roles - List roles = getRoles(context, username, dn); + // Retrieve user information + User user = getUser(context, username); + if (user == null) + return (null); + + // Check the user's credentials + if (!checkCredentials(context, user, credentials)) + return (null); + + // Search for additional roles + List roles = getRoles(context, user); // Create and return a suitable Principal for this user return (new GenericPrincipal(this, username, credentials, roles)); @@ -581,79 +730,376 @@ /** - * Close any open connection to the directory server for this Realm. + * Return a User object containing information about the user + * with the specified username, if found in the directory; + * otherwise return <code>null</code>. + * + * If the <code>userPassword</code> configuration attribute is + * specified, the value of that attribute is retrieved from the + * user's directory entry. If the <code>userRoleName</code> + * configuration attribute is specified, all values of that + * attribute are retrieved from the directory entry. * - * @param context The directory context to be closed + * @param context The directory context + * @param username Username to be looked up + * + * @exception NamingException if a directory server error occurs */ - protected void close(DirContext context) { + protected User getUser(DirContext context, String username) + throws NamingException { + + User user = null; - // Do nothing if there is no opened connection - if (context == null) - return; + // Get attributes to retrieve from user entry + ArrayList list = new ArrayList(); + if (userPassword != null) + list.add(userPassword); + if (userRoleName != null) + list.add(userRoleName); + String[] attrIds = new String[list.size()]; + list.toArray(attrIds); + + // Use pattern or search for user entry + if (userPatternFormat != null) { + user = getUserByPattern(context, username, attrIds); + } else { + user = getUserBySearch(context, username, attrIds); + } + + return user; + } - // Close our opened connection + + /** + * Use the <code>UserPattern</code> configuration attribute to + * locate the directory entry for the user with the specified + * username and return a User object; otherwise return + * <code>null</code>. + * + * @param context The directory context + * @param username The username + * @param attrIds String[]containing names of attributes to + * retrieve. + * + * @exception NamingException if a directory server error occurs + */ + protected User getUserByPattern(DirContext context, + String username, + String[] attrIds) + throws NamingException { + + if (debug >= 2) + log("lookupUser(" + username + ")"); + + if (username == null || userPatternFormat == null) + return (null); + + // Form the dn from the user pattern + String dn = userPatternFormat.format(new String[] { username }); + if (debug >= 3) { + log(" dn=" + dn); + } + + // Return if no attributes to retrieve + if (attrIds == null || attrIds.length == 0) + return new User(username, dn, null, null); + + // Get required attributes from user entry + Attributes attrs = null; try { - if (debug >= 1) - log("Closing directory context"); - context.close(); - } catch (NamingException e) { - log(sm.getString("jndiRealm.close"), e); + attrs = context.getAttributes(dn, attrIds); + } catch (NameNotFoundException e) { + return (null); } - this.context = null; + if (attrs == null) + return (null); + + // Retrieve value of userPassword + String password = null; + if (userPassword != null) + password = getAttributeValue(userPassword, attrs); + // Retrieve values of userRoleName attribute + ArrayList roles = null; + if (userRoleName != null) + roles = addAttributeValues(userRoleName, attrs, roles); + + return new User(username, dn, password, roles); } /** - * Return a short name for this Realm implementation. + * Search the directory to return a User object containing + * information about the user with the specified username, if + * found in the directory; otherwise return <code>null</code>. + * + * @param context The directory context + * @param username The username + * @param attrIds String[]containing names of attributes to retrieve. + * + * @exception NamingException if a directory server error occurs */ - protected String getName() { + protected User getUserBySearch(DirContext context, + String username, + String[] attrIds) + throws NamingException { - return (this.name); + if (username == null || userSearchFormat == null) + return (null); + + // Form the search filter + String filter = userSearchFormat.format(new String[] { username }); + + // Set up the search controls + SearchControls constraints = new SearchControls(); + + if (userSubtree) { + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + } + else { + constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); + } + + // Specify the attributes to be retrieved + if (attrIds == null) + attrIds = new String[0]; + constraints.setReturningAttributes(attrIds); + + if (debug > 3) { + log(" Searching for " + username); + log(" base: " + userBase + " filter: " + filter); + } + + NamingEnumeration results = + context.search(userBase, filter, constraints); + + + // Fail if no entries found + if (results == null || !results.hasMore()) { + if (debug > 2) { + log(" username not found"); + } + return (null); + } + + // Get result for the first entry found + SearchResult result = (SearchResult)results.next(); + + // Check no further entries were found + if (results.hasMore()) { + log("username " + username + " has multiple entries"); + return (null); + } + + // Get the entry's distinguished name + NameParser parser = context.getNameParser(""); + Name contextName = parser.parse(context.getNameInNamespace()); + Name baseName = parser.parse(userBase); + Name entryName = parser.parse(result.getName()); + Name name = contextName.addAll(baseName); + name = name.addAll(entryName); + String dn = name.toString(); + + if (debug > 2) + log(" entry found for " + username + " with dn " + dn); + + // Get the entry's attributes + Attributes attrs = result.getAttributes(); + if (attrs == null) + return null; + // Retrieve value of userPassword + String password = null; + if (userPassword != null) + password = getAttributeValue(userPassword, attrs); + + // Retrieve values of userRoleName attribute + ArrayList roles = null; + if (userRoleName != null) + roles = addAttributeValues(userRoleName, attrs, roles); + + return new User(username, dn, password, roles); } /** - * Return the password associated with the given principal's user name. + * Check whether the given User can be authenticated with the + * given credentials. If the <code>userPassword</code> + * configuration attribute is specified, the credentials + * previously retrieved from the directory are compared explicitly + * with those presented by the user. Otherwise the presented + * credentials are checked by binding to the directory as the + * user. + * + * @param context The directory context + * @param user The User to be authenticated + * @param credentials The credentials presented by the user + * + * @exception NamingException if a directory server error occurs */ - protected String getPassword(String username) { - - return (null); + protected boolean checkCredentials(DirContext context, + User user, + String credentials) + throws NamingException { + + boolean validated = false; + + if (userPassword == null) { + validated = bindAsUser(context, user, credentials); + } else { + validated = compareCredentials(context, user, credentials); + } + + if (debug >= 2) { + if (validated) { + log(sm.getString("jndiRealm.authenticateSuccess", + user.username)); + } else { + log(sm.getString("jndiRealm.authenticateFailure", + user.username)); + } + } + return (validated); + } - } /** - * Return the Principal associated with the given user name. + * Check whether the credentials presented by the user match those + * retrieved from the directory. + * + * @param context The directory context + * @param user The User to be authenticated + * @param credentials Authentication credentials + * + * @exception NamingException if a directory server error occurs */ - protected Principal getPrincipal(String username) { + protected boolean compareCredentials(DirContext context, + User info, + String credentials) + throws NamingException { - return (null); + if (info == null || credentials == null) + return (false); + + String password = info.password; + if (password == null) + return (false); + + // Validate the credentials specified by the user + if (debug >= 3) + log(" validating credentials"); + + boolean validated = false; + if (hasMessageDigest()) { + // Hex hashes should be compared case-insensitive + validated = (digest(credentials).equalsIgnoreCase(password)); + } else + validated = (digest(credentials).equals(password)); + return (validated); } + /** - * Return a List of roles associated with the user with the specified - * distinguished name. If no roles are associated with this user, a - * zero-length List is returned. + * Check credentials by binding to the directory as the user + * + * @param context The directory context + * @param user The User to be authenticated + * @param credentials Authentication credentials + * + * @exception NamingException if a directory server error occurs + */ + protected boolean bindAsUser(DirContext context, + User user, + String credentials) + throws NamingException { + Attributes attr; + + if (credentials == null || user == null) + return (false); + + String dn = user.dn; + if (dn == null) + return (false); + + // Validate the credentials specified by the user + if (debug >= 3) { + log(" validating credentials by binding as the user"); + } + + // Set up security environment to bind as the user + context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn); + context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials); + + // Elicit an LDAP bind operation + boolean validated = false; + try { + if (debug > 2) { + log(" binding as " + dn); + } + attr = context.getAttributes("", null); + validated = true; + } + catch (AuthenticationException e) { + if (debug > 2) { + log(" bind attempt failed"); + } + } + + // Restore the original security environment + if (connectionName != null) { + context.addToEnvironment(Context.SECURITY_PRINCIPAL, + connectionName); + } else { + context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); + } + + if (connectionPassword != null) { + context.addToEnvironment(Context.SECURITY_CREDENTIALS, + connectionPassword); + } + else { + context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); + } + + return (validated); + } + + + /** + * Return a List of roles associated with the given User. Any + * roles present in the user's directory entry are supplemented by + * a directory search. If no roles are associated with this user, + * a zero-length List is returned. * * @param context The directory context we are searching - * @param username The username of the user to be checked - * @param dn Distinguished name of the user to be checked + * @param user The User to be checked * * @exception NamingException if a directory server error occurs */ - protected List getRoles(DirContext context, - String username, String dn) + protected List getRoles(DirContext context, User user) throws NamingException { + if (user == null) + return (null); + + String dn = user.dn; + String username = user.username; + + if (dn == null || username == null) + return (null); + if (debug >= 2) - log("getRoles(" + dn + ")"); + log(" getRoles(" + dn + ")"); + + // Start with roles retrieved from the user entry + ArrayList list = user.roles; + if (list == null) { + list = new ArrayList(); + } // Are we configured to do role searches? - ArrayList list = new ArrayList(); if ((roleFormat == null) || (roleName == null)) return (list); @@ -664,12 +1110,12 @@ controls.setSearchScope(SearchControls.SUBTREE_SCOPE); else controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); - controls.setReturningAttributes(roleName); + controls.setReturningAttributes(new String[] {roleName}); // Perform the configured search and process the results if (debug >= 3) { log(" Searching role base '" + roleBase + "' for attribute '" + - roleName[0] + "'"); + roleName + "'"); log(" With filter expression '" + filter + "'"); } NamingEnumeration results = @@ -680,64 +1126,41 @@ SearchResult result = (SearchResult) results.next(); Attributes attrs = result.getAttributes(); if (attrs == null) - continue; - Attribute attr = attrs.get(roleName[0]); - if (attr != null) { - String role = (String) attr.get(); - if (debug >= 3) - log(" Found role '" + role + "'"); - list.add(role); - } + continue; + list = addAttributeValues(roleName, attrs, list); } - // Return the completed list of roles - if (debug >= 2) + // Return the augmented list of roles + if (debug >= 2) { log(" Returning " + list.size() + " roles"); - return (list); + for (int i=0; i<list.size(); i++) + log( " Found role " + list.get(i)); + } + return (list); } /** - * Return the distinguished name of an authenticated user (if successful) - * or <code>null</code> if authentication is unsuccessful. + * Return a String representing the value of the specified attribute. * - * @param context The directory context we are accessing - * @param username Username to be authenticated - * @param credentials Authentication credentials + * @param attrId Attribute name + * @param attrs Attributes containing the required value * * @exception NamingException if a directory server error occurs */ - protected String getUserDN(DirContext context, - String username, String credentials) + private String getAttributeValue(String attrId, Attributes attrs) throws NamingException { - if (debug >= 2) - log("getUserDN(" + username + ")"); - if (username == null) - return (null); - if ((userFormat == null) || (userPassword == null)) - return (null); - - // Retrieve the user password attribute for this user - String dn = userFormat.format(new String[] { username }); - if (debug >= 3) - log(" dn=" + dn); - Attributes attrs = null; - try { - attrs = context.getAttributes(dn, userPassword); - } catch (NameNotFoundException e) { - return (null); - } - if (attrs == null) - return (null); if (debug >= 3) - log(" retrieving attribute " + userPassword[0]); - Attribute attr = attrs.get(userPassword[0]); + log(" retrieving attribute " + attrId); + + if (attrId == null || attrs == null) + return null; + + Attribute attr = attrs.get(attrId); if (attr == null) return (null); - if (debug >= 3) - log(" retrieving value"); Object value = attr.get(); if (value == null) return (null); @@ -746,34 +1169,100 @@ valueString = new String((byte[]) value); else valueString = value.toString(); + + return valueString; + } - // Validate the credentials specified by the user - if (debug >= 3) - log(" validating credentials"); - boolean validated = false; - if (hasMessageDigest()) { - // Hex hashes should be compared case-insensitive - validated = (digest(credentials).equalsIgnoreCase(valueString)); - } else - validated = (digest(credentials).equals(valueString)); - if (validated) { - if (debug >= 2) - log(sm.getString("jndiRealm.authenticateSuccess", - username)); - } else { - if (debug >= 2) - log(sm.getString("jndiRealm.authenticateFailure", - username)); + /** + * Add values of a specified attribute to a list + * + * @param attrId Attribute name + * @param attrs Attributes containing the new values + * @param values ArrayList containing values found so far + * + * @exception NamingException if a directory server error occurs + */ + private ArrayList addAttributeValues(String attrId, + Attributes attrs, + ArrayList values) + throws NamingException{ + + if (debug >= 3) + log(" retrieving values for attribute " + attrId); + if (attrId == null || attrs == null) + return null; + if (values == null) + values = new ArrayList(); + Attribute attr = attrs.get(attrId); + if (attr == null) return (null); + NamingEnumeration e = attr.getAll(); + while(e.hasMore()) { + String value = (String)e.next(); + values.add(value); + } + return values; + } + + + /** + * Close any open connection to the directory server for this Realm. + * + * @param context The directory context to be closed + */ + protected void close(DirContext context) { + + // Do nothing if there is no opened connection + if (context == null) + return; + + // Close our opened connection + try { + if (debug >= 1) + log("Closing directory context"); + context.close(); + } catch (NamingException e) { + log(sm.getString("jndiRealm.close"), e); } - return (dn); + this.context = null; + + } + + + /** + * Return a short name for this Realm implementation. + */ + protected String getName() { + + return (this.name); } /** + * Return the password associated with the given principal's user name. + */ + protected String getPassword(String username) { + + return (null); + + } + + + /** + * Return the Principal associated with the given user name. + */ + protected Principal getPrincipal(String username) { + + return (null); + + } + + + + /** * Open (if necessary) and return a connection to the configured * directory server for this Realm. * @@ -859,5 +1348,29 @@ } +} + +// ------------------------------------------------------ Private Classes + +/** + * A private class representing a User + */ +class User { + String username = null; + String dn = null; + String password = null; + ArrayList roles = null; + + + User(String username, + String dn, + String password, + ArrayList roles) { + this.username = username; + this.dn = dn; + this.password = password; + this.roles = roles; + } } +
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>