2
votes

I need to find all users that are members of two groups (GroupA and GroupB). I also need to take into account nested groups. What is the best way to do this?

I know that doing an ldap search using memberOf does not take into account nested groups. I could also locate the two groups specifically, get a list of members, and iterate through them, matching up ones that are members of both lists, but the members collection of a group doesn't take into account nested groups either. Are there any methods that do work with nested groups, or do I need to write my own recursive logic?

Edit Nested group: If I have a security group called GroupA. GroupA can have members which are either users or other groups. GroupB is what I am calling a 'nested group' if it is a member of GroupA.

3
Please define 'nested groups'.Till
Sorry, 'Nested group' is probably my own term, so I've added a description of what that is...Jeremy
This is very similar to my unanswered question stackoverflow.com/questions/4430567/…Peter

3 Answers

2
votes

Here is something working in an ActiveDirectory 2003 ans 2008 R2. I use Microsoft LDAP_MATCHING_RULE_IN_CHAIN to :

1) Search recursively (but in one query) all the users from the first group (be careful it return users from security and distributions group)

2) For each user from the first query, I again search recursively (but in one query) if the user belongs to the second group.

static void Main(string[] args)
{
  //Connection to Active Directory
  string sFromWhere = "LDAP://SRVENTR2:389/dc=societe,dc=fr";
  DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "societe\\administrateur", "test.2011");

  // To find all the users member of groups "Grp1"  :
  // Set the base to the groups container DN; for example root DN (dc=societe,dc=fr) 
  // Set the scope to subtree
  // Use the following filter :
  // (member:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=X)
  //
  DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
  dsLookFor.Filter = "(&(memberof:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=societe,DC=fr)(objectCategory=user))";
  dsLookFor.SearchScope = SearchScope.Subtree;
  dsLookFor.PropertiesToLoad.Add("cn");

  SearchResultCollection srcUsers = dsLookFor.FindAll();

  // Just to know if user is present in an other group
  foreach (SearchResult srcUser in srcUsers)
  {
    Console.WriteLine("{0}", srcUser.Path);

    // To check if a user "user1" is a member of group "group1".
    // Set the base to the user DN (cn=user1, cn=users, dc=x)
    // Set the scope to base
    // Use the following filter :
    // (memberof:1.2.840.113556.1.4.1941:=(cn=Group1,OU=groupsOU,DC=x))
    DirectoryEntry deBaseUsr = new DirectoryEntry(srcUser.Path, "societe\\administrateur", "test.2011");
    DirectorySearcher dsVerify = new DirectorySearcher(deBaseUsr);
    dsVerify.Filter = "(memberof:1.2.840.113556.1.4.1941:=CN=Grp3,OU=MonOu,DC=societe,DC=fr)";
    dsVerify.SearchScope = SearchScope.Base;
    dsVerify.PropertiesToLoad.Add("cn");

    SearchResult srcTheUser = dsVerify.FindOne();

    if (srcTheUser != null)
    {
      Console.WriteLine("Bingo {0}", srcTheUser.Path);
    }
  }
  Console.ReadLine();
}
0
votes

I don't know of any way to do this except via recursion. Get the group membership for group a. Loop through the list, if item is a user add to second list, if item is a group then perform recursion.

0
votes

Is there a requirement that you use ldap search to do this? The WindowsPrincipal.IsInRole() method will test for membership both directly and via a nested group - at least it did for the test I ran.

This code tests the current thread's identity against GroupA and GroupB but you could use a similar approach to enumerate the members of GroupA and then test each of those against GroupB by calling IsInRole...

AppDomain myDomain = Thread.GetDomain();

myDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

WindowsPrincipal myPrincipal = (WindowsPrincipal)Thread.CurrentPrincipal;

NTAccount groupA = new NTAccount("Domain\\GroupA");

SecurityIdentifier sidGroupA = (SecurityIdentifier)groupA.Translate(typeof(SecurityIdentifier));

bool inGroupA = myPrincipal.IsInRole(sidGroupA);

NTAccount groupB = new NTAccount("Domain\\GroupB");

SecurityIdentifier sidGroupB = (SecurityIdentifier)groupB.Translate(typeof(SecurityIdentifier));

bool inGroupB = myPrincipal.IsInRole(sidGroupB);

Console.WriteLine("{0}, {1}", groupA, inGroupA);

Console.WriteLine("{0}, {1}", groupB, inGroupB);

Console.ReadLine();