From 16348a93a52b1845c304054d54af0a9b2c9f616e Mon Sep 17 00:00:00 2001 From: rjung Date: Tue, 4 Nov 2008 21:42:38 +0000 Subject: [PATCH] Add ability to recursively search for roles to JNDIRealm. That way nested groups work. git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@711422 13f79535-47bb-0310-9956-ffa450edef68 --- java/org/apache/catalina/realm/JNDIRealm.java | 145 ++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 11 deletions(-) diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java index 5ea7c4b44..f47221948 100644 --- a/java/org/apache/catalina/realm/JNDIRealm.java +++ b/java/org/apache/catalina/realm/JNDIRealm.java @@ -22,9 +22,13 @@ import java.security.Principal; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; import javax.naming.Context; import javax.naming.CommunicationException; @@ -134,6 +138,14 @@ import org.apache.tomcat.util.buf.CharChunk; * in the user's element whose name is configured by the * userRoleName property. * + *
  • A default role can be assigned to each user that was successfully + * authenticated by setting the commonRole property to the + * name of this role. The role doesn't have to exist in the directory.
  • + * + *
  • If the directory server contains nested roles, you can search for roles + * recursively by setting roleRecursionLimit to some positive value. + * The default value is 0, so role searches do not recurse.
  • + * *
  • Note that the standard <security-role-ref> element in * the web application deployment descriptor allows applications to refer * to roles programmatically by names other than those used in the @@ -307,6 +319,13 @@ public class JNDIRealm extends RealmBase { /** + * The maximum recursion depth when resolving roles recursively. + * By default we don't resolve roles recursively. + */ + protected int roleRecursionLimit = 0; + + + /** * The base element for role searches. */ protected String roleBase = ""; @@ -638,6 +657,28 @@ public class JNDIRealm extends RealmBase { /** + * Return the maximum recursion depth for role searches. + */ + public int getRoleRecursionLimit() { + + return (this.roleRecursionLimit); + + } + + + /** + * Set the maximum recursion depth for role searches. + * + * @param roleRecursionLimit The new recursion limit + */ + public void setRoleRecursionLimit(int roleRecursionLimit) { + + this.roleRecursionLimit = roleRecursionLimit; + + } + + + /** * Return the base element for role searches. */ public String getRoleBase() { @@ -1405,6 +1446,69 @@ public class JNDIRealm extends RealmBase { /** + * Add roles to a user and search for other roles containing them themselves. + * We search recursively with a limited depth. + * By default the depth is 0, and we only use direct roles. + * The search needs to use the distinguished role names, + * but to return the role names. + * + * @param depth Recursion depth, starting at zero + * @param context The directory context we are searching + * @param recursiveMap The cumulative result map of role names and DNs. + * @param recursiveSet The cumulative result set of role names. + * @param groupName The role name to add to the list. + * @param groupDName The distinguished name of the role. + * + * @exception NamingException if a directory server error occurs + */ + private void getRolesRecursive(int depth, DirContext context, Map recursiveMap, Set recursiveSet, + String groupName, String groupDName) throws NamingException { + if (containerLog.isTraceEnabled()) + containerLog.trace("Recursive search depth " + depth + " for group '" + groupDName + " (" + groupName + ")'"); + // Adding the given group to the result set if not already found + if (!recursiveSet.contains(groupDName)) { + recursiveSet.add(groupDName); + recursiveMap.put(groupDName, groupName); + if (depth >= roleRecursionLimit) { + if (roleRecursionLimit > 0) + containerLog.warn("Terminating recursive role search because of recursion limit " + + roleRecursionLimit + ", results might be incomplete"); + return; + } + // Prepare the parameters for searching groups + String filter = roleFormat.format(new String[] { groupDName }); + SearchControls controls = new SearchControls(); + controls.setSearchScope(roleSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE); + controls.setReturningAttributes(new String[] { roleName }); + if (containerLog.isTraceEnabled()) { + containerLog.trace("Recursive search in role base '" + roleBase + "' for attribute '" + roleName + "'" + + " with filter expression '" + filter + "'"); + } + // Searching groups that assign the given group + NamingEnumeration results = context.search(roleBase, filter, controls); + if (results != null) { + // Iterate over the resulting groups + try { + while (results.hasMore()) { + SearchResult result = (SearchResult) results.next(); + Attributes attrs = result.getAttributes(); + if (attrs == null) + continue; + String dname = getDistinguishedName(context, roleBase, result); + String name = getAttributeValue(roleName, attrs); + if (name != null && dname != null) { + getRolesRecursive(depth+1, context, recursiveMap, recursiveSet, name, dname); + } + } + } catch (PartialResultException ex) { + if (!adCompat) + throw ex; + } + } + } + } + + /** * 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, @@ -1466,33 +1570,52 @@ public class JNDIRealm extends RealmBase { context.search(roleBase, filter, controls); if (results == null) return (list); // Should never happen, but just in case ... + + HashMap groupMap = new HashMap(); try { while (results.hasMore()) { SearchResult result = (SearchResult) results.next(); Attributes attrs = result.getAttributes(); if (attrs == null) continue; - list = addAttributeValues(roleName, attrs, list); + String dname = getDistinguishedName(context, roleBase, result); + String name = getAttributeValue(roleName, attrs); + if (name != null && dname != null) { + groupMap.put(dname, name); + } } } catch (PartialResultException ex) { if (!adCompat) throw ex; } - + Set keys = groupMap.keySet(); if (containerLog.isTraceEnabled()) { - if (list != null) { - containerLog.trace(" Returning " + list.size() + " roles"); - Iterator it = list.iterator(); - while (it.hasNext()) { - containerLog.trace( " Found role " + it.next()); - } - } else { - containerLog.trace(" getRoles about to return null "); + containerLog.trace(" Found " + keys.size() + " direct roles"); + for (Iterator i = keys.iterator(); i.hasNext();) { + Object k = i.next(); + containerLog.trace( " Found direct role " + k + " -> " + groupMap.get(k)); } } - return (list); + HashSet recursiveSet = new HashSet(); + HashMap recursiveMap = new HashMap(); + + for (Iterator i = keys.iterator(); i.hasNext();) { + String k = i.next(); + getRolesRecursive(0, context, recursiveMap, recursiveSet, groupMap.get(k), k); + } + + HashSet resultSet = new HashSet(list); + resultSet.addAll(recursiveMap.values()); + + if (containerLog.isTraceEnabled()) { + containerLog.trace(" Returning " + resultSet.size() + " roles"); + for (Iterator i = resultSet.iterator(); i.hasNext();) + containerLog.trace( " Found role " + i.next()); + } + + return new ArrayList(resultSet); } -- 2.11.0