View | Details | Raw Unified | Return to bug 46925
Collapse All | Expand All

(-)java/org/apache/catalina/realm/JNDIRealm.java (-117 / +80 lines)
Lines 317-331 Link Here
317
     */
317
     */
318
    protected MessageFormat[] userPatternFormatArray = null;
318
    protected MessageFormat[] userPatternFormatArray = null;
319
319
320
321
    /**
320
    /**
322
     * The maximum recursion depth when resolving roles recursively.
323
     * By default we don't resolve roles recursively.
324
     */
325
    protected int roleRecursionLimit = 0;
326
327
328
    /**
329
     * The base element for role searches.
321
     * The base element for role searches.
330
     */
322
     */
331
    protected String roleBase = "";
323
    protected String roleBase = "";
Lines 362-368 Link Here
362
     * Should we search the entire subtree for matching memberships?
354
     * Should we search the entire subtree for matching memberships?
363
     */
355
     */
364
    protected boolean roleSubtree = false;
356
    protected boolean roleSubtree = false;
357
    
358
    /**
359
     * Should we look for nested group in order to determine roles?
360
     */
361
    protected boolean roleNested = false;
365
362
363
366
    /**
364
    /**
367
     * An alternate URL, to which, we should connect if connectionURL fails.
365
     * An alternate URL, to which, we should connect if connectionURL fails.
368
     */
366
     */
Lines 652-679 Link Here
652
650
653
651
654
    /**
652
    /**
655
     * Return the maximum recursion depth for role searches.
656
     */
657
    public int getRoleRecursionLimit() {
658
659
        return (this.roleRecursionLimit);
660
661
    }
662
663
664
    /**
665
     * Set the maximum recursion depth for role searches.
666
     *
667
     * @param roleRecursionLimit The new recursion limit
668
     */
669
    public void setRoleRecursionLimit(int roleRecursionLimit) {
670
671
        this.roleRecursionLimit = roleRecursionLimit;
672
673
    }
674
675
676
    /**
677
     * Return the base element for role searches.
653
     * Return the base element for role searches.
678
     */
654
     */
679
    public String getRoleBase() {
655
    public String getRoleBase() {
Lines 763-771 Link Here
763
        this.roleSubtree = roleSubtree;
739
        this.roleSubtree = roleSubtree;
764
740
765
    }
741
    }
742
    
743
    /**
744
     * Return the "The nested group search flag" flag.
745
     */
746
    public boolean getRoleNested() {
766
747
748
        return (this.roleNested);
767
749
750
    }
751
752
768
    /**
753
    /**
754
     * Set the "search subtree for roles" flag.
755
     *
756
     * @param roleNested The nested group search flag
757
     */
758
    public void setRoleNested(boolean roleNested) {
759
760
        this.roleNested = roleNested;
761
762
    }
763
     
764
765
766
    /**
769
     * Return the password attribute used to retrieve the user password.
767
     * Return the password attribute used to retrieve the user password.
770
     */
768
     */
771
    public String getUserPassword() {
769
    public String getUserPassword() {
Lines 1546-1617 Link Here
1546
        return (validated);
1544
        return (validated);
1547
     }
1545
     }
1548
1546
1549
1550
    /**
1547
    /**
1551
     * Add roles to a user and search for other roles containing them themselves.
1552
     * We search recursively with a limited depth.
1553
     * By default the depth is 0, and we only use direct roles.
1554
     * The search needs to use the distinguished role names,
1555
     * but to return the role names.
1556
     *
1557
     * @param depth Recursion depth, starting at zero
1558
     * @param context The directory context we are searching
1559
     * @param recursiveMap The cumulative result map of role names and DNs.
1560
     * @param recursiveSet The cumulative result set of role names.
1561
     * @param groupName The role name to add to the list.
1562
     * @param groupDName The distinguished name of the role.
1563
     *
1564
     * @exception NamingException if a directory server error occurs
1565
     */
1566
    private void getRolesRecursive(int depth, DirContext context, Map<String, String> recursiveMap, Set<String> recursiveSet,
1567
                                     String groupName, String groupDName) throws NamingException {
1568
        if (containerLog.isTraceEnabled())
1569
            containerLog.trace("Recursive search depth " + depth + " for group '" + groupDName + " (" + groupName + ")'");
1570
        // Adding the given group to the result set if not already found
1571
        if (!recursiveSet.contains(groupDName)) {
1572
            recursiveSet.add(groupDName);
1573
            recursiveMap.put(groupDName, groupName);
1574
            if (depth >= roleRecursionLimit) {
1575
                if (roleRecursionLimit > 0)
1576
                    containerLog.warn("Terminating recursive role search because of recursion limit " +
1577
                                      roleRecursionLimit + ", results might be incomplete");
1578
                return;
1579
            }
1580
            // Prepare the parameters for searching groups
1581
            String filter = roleFormat.format(new String[] { groupDName });
1582
            SearchControls controls = new SearchControls();
1583
            controls.setSearchScope(roleSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
1584
            controls.setReturningAttributes(new String[] { roleName });
1585
            if (containerLog.isTraceEnabled()) {
1586
                containerLog.trace("Recursive search in role base '" + roleBase + "' for attribute '" + roleName + "'" +
1587
                                   " with filter expression '" + filter + "'");
1588
            }
1589
            // Searching groups that assign the given group
1590
            NamingEnumeration<SearchResult> results =
1591
                context.search(roleBase, filter, controls);
1592
            if (results != null) {
1593
                // Iterate over the resulting groups
1594
                try {
1595
                    while (results.hasMore()) {
1596
                        SearchResult result = results.next();
1597
                        Attributes attrs = result.getAttributes();
1598
                        if (attrs == null)
1599
                            continue;
1600
                        String dname = getDistinguishedName(context, roleBase, result);
1601
                        String name = getAttributeValue(roleName, attrs);
1602
                        if (name != null && dname != null) {
1603
                           getRolesRecursive(depth+1, context, recursiveMap, recursiveSet, name, dname);
1604
                        }
1605
                    }
1606
                } catch (PartialResultException ex) {
1607
                    if (!adCompat)
1608
                        throw ex;
1609
                }
1610
            }
1611
        }
1612
    }
1613
1614
    /**
1615
     * Return a List of roles associated with the given User.  Any
1548
     * Return a List of roles associated with the given User.  Any
1616
     * roles present in the user's directory entry are supplemented by
1549
     * roles present in the user's directory entry are supplemented by
1617
     * a directory search. If no roles are associated with this user,
1550
     * a directory search. If no roles are associated with this user,
Lines 1654-1660 Link Here
1654
        // Are we configured to do role searches?
1587
        // Are we configured to do role searches?
1655
        if ((roleFormat == null) || (roleName == null))
1588
        if ((roleFormat == null) || (roleName == null))
1656
            return (list);
1589
            return (list);
1657
1590
        
1658
        // Set up parameters for an appropriate search
1591
        // Set up parameters for an appropriate search
1659
        String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username });
1592
        String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username });
1660
        SearchControls controls = new SearchControls();
1593
        SearchControls controls = new SearchControls();
Lines 1691-1720 Link Here
1691
        Set<String> keys = groupMap.keySet();
1624
        Set<String> keys = groupMap.keySet();
1692
        if (containerLog.isTraceEnabled()) {
1625
        if (containerLog.isTraceEnabled()) {
1693
            containerLog.trace("  Found " + keys.size() + " direct roles");
1626
            containerLog.trace("  Found " + keys.size() + " direct roles");
1694
            for (Iterator<String> i = keys.iterator(); i.hasNext();) {
1627
            for (String key: keys) {
1695
                Object k = i.next();
1628
            	containerLog.trace(  "  Found direct role " + key + " -> " + groupMap.get(key));
1696
                containerLog.trace(  "  Found direct role " + k + " -> " + groupMap.get(k));
1697
            }
1629
            }
1698
        }
1630
        }
1631
        
1632
        // if nested group search is enabled, perform searches for nested groups until no new group is found
1633
        if (getRoleNested()) {
1634
        	
1635
        	// The following efficient algorithm is known as memberOf Algorithm, as described in "Practices in 
1636
        	// Directory Groups". It avoids group slurping and handles cyclic group memberships as well.
1637
        	// See http://middleware.internet2.edu/dir/ for details
1638
        	
1639
        	Set<String> newGroupDNs = new HashSet<String>(groupMap.keySet());
1640
        	while (!newGroupDNs.isEmpty()) {
1641
        		Set<String> newThisRound = new HashSet<String>(); // Stores the groups we find in this iteration
1642
        		        		
1643
        		for (String groupDN : newGroupDNs) {
1644
        			filter = roleFormat.format(new String[] { groupDN });
1645
        			
1646
        			if (containerLog.isTraceEnabled()) {
1647
        				containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
1648
        			}
1649
        			
1650
        			results = context.search(roleBase, filter, controls);
1651
        			
1652
        			try {
1653
        			    while (results.hasMore()) {
1654
        	                SearchResult result = results.next();
1655
	                        Attributes attrs = result.getAttributes();
1656
	                        if (attrs == null)
1657
	                            continue;
1658
	                        String dname = getDistinguishedName(context, roleBase, result);
1659
	                        String name = getAttributeValue(roleName, attrs);
1660
	                        if (name != null && dname != null && !groupMap.keySet().contains(dname)) {
1661
	                            groupMap.put(dname, name);
1662
	                            newThisRound.add(dname);
1663
	                            
1664
	                            if (containerLog.isTraceEnabled()) {
1665
	                				containerLog.trace("  Found nested role " + dname + " -> " + name);
1666
	                			}
1667
	                            
1668
	                        }
1669
	        	         }
1670
        			} catch (PartialResultException ex) {
1671
        	            if (!adCompat)
1672
        	                throw ex;
1673
        	        }
1674
        		}
1675
        		
1676
        		newGroupDNs = newThisRound;
1677
        	}
1678
        } 
1699
1679
1700
        HashSet<String> recursiveSet = new HashSet<String>();
1680
        return new ArrayList<String>(groupMap.values());
1701
        HashMap<String, String> recursiveMap = new HashMap<String, String>();
1702
1703
        for (Iterator<String> i = keys.iterator(); i.hasNext();) {
1704
            String k = i.next();
1705
            getRolesRecursive(0, context, recursiveMap, recursiveSet, groupMap.get(k), k);
1706
        }
1707
1708
        HashSet<String> resultSet = new HashSet<String>(list);
1709
        resultSet.addAll(recursiveMap.values());
1710
1711
        if (containerLog.isTraceEnabled()) {
1712
            containerLog.trace("  Returning " + resultSet.size() + " roles");
1713
            for (Iterator<String> i = resultSet.iterator(); i.hasNext();)
1714
                containerLog.trace(  "  Found role " + i.next());
1715
        }
1716
1717
        return new ArrayList<String>(resultSet);
1718
    }
1681
    }
1719
1682
1720
1683

Return to bug 46925