Retrieving Nested Groups for a User in Active Directory

Wednesday, July 24, 2013 at 4:30 PM

A short time ago I had a situation come up where I needed to determine the Active Directory groups a user had membership in, up to and including the inner-most nested group. Here's the situation:

User A belongs to Group X
Group X is in Group Y
Group Y is in Group Z

and so on...

To determine which groups User A has immediate membership in is trivial, but, from what I could find, there is not a simple, one-liner LDAP query that will return all group memberships including nested groups. A typical LDAP query will return Group X when I really need to know that User A is a member of Group Z. My solution was to recursively query Active Directory until we've parsed the user's entire group membership tree or gone X-levels deep.

/// <summary>
/// This method gets all groups that a user belongs to.
/// </summary>
/// <param name="path">The LDAP path to the Active Directory server.</param>
/// <param name="username">The username of the account for which all groups will be fetched.</param>
/// <param name="accountName">Name of the account performing the search. This should be a service account with the appropriate permissions.</param>
/// <param name="accountPassword">The password to the account performing the search.</param>
/// <param name="iterations">The the number of iterations to perform before exiting a branch.</param>
/// <returns>
/// Returns returns a list of groups to which the user belongs.
/// </returns>
public List<string> GetGroups(string path, string username, string accountName, string accountPassword, int iterations)
{
    DirectoryEntry entry = new DirectoryEntry(path, accountName, accountPassword);
    DirectorySearcher searcher = new DirectorySearcher(entry);
    searcher.PropertiesToLoad.Add("memberOf");

    List<string> groups = new List<string>();

    GetGroupsRecursively(searcher, username, groups, iterations);

    return groups;
}

/// <summary>
/// Recursively gets the groups to which a user belongs.
/// </summary>
/// <param name="searcher">The DirectorySearcher performing the search.</param>
/// <param name="filter">The username or group to search for.</param>
/// <param name="groups">The list of groups found.</param>
/// <param name="iteration">The number of iterations left before exiting.</param>
/// <exception cref="System.Exception">Error obtaining group names.</exception>
private void GetGroupsRecursively(DirectorySearcher searcher, string filter, List<string> groups, int iteration)
{
    // Exit if the max number of iterations have been performed
    if (iteration > 0)
    {
        try
        {
            // Perform an Abiguous Name Resolution search
            searcher.Filter = "(aNR=" + filter + ")";

            SearchResult result = searcher.FindOne();

            // Check for failed search
            if (result == null)
            {
                return;
            }

            for (int i = 0; i < result.Properties["memberOf"].Count; i++)
            {
                string dn = (string)result.Properties["memberOf"][i];
                int equalsIndex = dn.IndexOf("=", 1);
                int commaIndex = dn.IndexOf(",", 1);
                        
                if (equalsIndex != -1)
                {
                    string role = dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1);

                    // Don't add or iterate over a group that has already been found
                    if (!groups.Contains(role))
                    {
                        groups.Add(role);

                        // Now find all groups that this group belongs to
                        GetGroupsRecursively(searcher, role, groups, iteration - 1);
                    }
                }

            }
        }
        catch (Exception ex)
        {
            throw new Exception("Error obtaining group names.", ex);
        }
    }
}

 


Add Comment