I have an application that uses ActiveDirecotry authorisation and it has been decided that it needs to support nested AD groups, e.g.:
MAIN_AD_GROUP | |-> SUB_GROUP | |-> User
So, the user in not directly a member of MAIN_AD_GROUP
. I'd like to be able to look for the user recursively, searching the groups nested in MAIN_AD_GROUP
.
The main problem is that I'm using .NET 3.5 and there is a bug in System.DirectoryServices.AccountManagement
in .NET 3.5 whereby the method UserPrincipal.IsMemberOf()
will not work for groups with more than 1500 users. So I can't use UserPrincipal.IsMemberOf()
and no, I can't switch to .NET 4 either.
I've worked around this last problem with the following function:
private bool IsMember(Principal userPrincipal, Principal groupPrincipal) { using (var groups = userPrincipal.GetGroups()) { var isMember = groups.Any(g => g.DistinguishedName == groupPrincipal.DistinguishedName); return isMember; } }
But userPrincipal.GetGroups()
only returns the groups of which the user is a direct member.
How can I get this to work with nested groups?
We can get group members by using the Active Directory PowerShell cmdlet Get-ADGroupMember. The Get-ADGroupMember cmdlet provides the option to get all the nested group members by passing the parameter -Recursive.
You can check group membership with the Active Directory Users and Computers (ADUC) console snap-in by finding the user or group of interest and drilling down into the object's properties and clicking the “Members” or “Member Of” tab.
What is group nesting? AD group nesting, simply put, is the process of putting one group inside another group. Nested groups inherit the permissions and privileges of the group they are put under, and hence this makes privilege administration easier.
This bug is reported here at Microsoft Connect along with the following code that works around this issue by manually iterating through the PrincipalSearchResult<Principal>
returned objects, catching this exception, and continuing on:
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups(); var iterGroup = groups.GetEnumerator(); using (iterGroup) { while (iterGroup.MoveNext()) { try { Principal p = iterGroup.Current; Console.WriteLine(p.Name); } catch (NoMatchingPrincipalException pex) { continue; } } }
Another workaround found here avoids the AccountManagement
class, and uses the System.DirectoryServices
API instead:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.DirectoryServices; namespace GetGroupsForADUser { class Program { static void Main(string[] args) { String username = "Gabriel"; List<string> userNestedMembership = new List<string>(); DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain //DirectoryEntry domainConnection = new DirectoryEntry("LDAP://example.com", "username", "password"); // Use this to query a remote domain DirectorySearcher samSearcher = new DirectorySearcher(); samSearcher.SearchRoot = domainConnection; samSearcher.Filter = "(samAccountName=" + username + ")"; samSearcher.PropertiesToLoad.Add("displayName"); SearchResult samResult = samSearcher.FindOne(); if (samResult != null) { DirectoryEntry theUser = samResult.GetDirectoryEntry(); theUser.RefreshCache(new string[] { "tokenGroups" }); foreach (byte[] resultBytes in theUser.Properties["tokenGroups"]) { System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0); DirectorySearcher sidSearcher = new DirectorySearcher(); sidSearcher.SearchRoot = domainConnection; sidSearcher.Filter = "(objectSid=" + mySID.Value + ")"; sidSearcher.PropertiesToLoad.Add("distinguishedName"); SearchResult sidResult = sidSearcher.FindOne(); if (sidResult != null) { userNestedMembership.Add((string)sidResult.Properties["distinguishedName"][0]); } } foreach (string myEntry in userNestedMembership) { Console.WriteLine(myEntry); } } else { Console.WriteLine("The user doesn't exist"); } Console.ReadKey(); } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With