Retrieving Nested Groups for a User in Active Directory
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); } } }