开发者

Get members of an Active Directory group recursively, i.e. including subgroups

开发者 https://www.devze.com 2023-01-15 00:55 出处:网络
Given a group like this in Active Directory: MainGroup GroupA User1 User2 GroupB User3 User4 I can easily determine if User3 is member of MainGroup or any of its subgroups with code like this:

Given a group like this in Active Directory:

MainGroup
  GroupA
    User1
    User2
  GroupB
    User3
  User4

I can easily determine if User3 is member of MainGroup or any of its subgroups with code like this:

using System;
using System.DirectoryServices;

static class Program {
    static void Main() {
        DirectoryEntry user = new DirectoryEntry("LDAP://CN=User3,DC=X,DC=y");
        string filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=MainGroup,DC=X,DC=y)";
        DirectorySearcher searcher = new DirectorySearcher(user, filter);
        searcher.SearchScope = SearchScope.Subtree;
        var r = searcher.FindOne();
        bool isMember = (r != null);
    }
}

I would like to know if there is a similar way to get all the users that are member of a group or any of its subgroups, i.e. in the example for MainGroup get User1, User2, User3 and User4.

The obvious way of getting all the users is to recursively query each subgro开发者_开发百科up, but I was wondering if there is an easier way to do it.

Using the same approach with the memberOf:1.2.840.113556.1.4.1941: filter, but using the domain root instead of the user as a search base is not feasible, as the query takes too long (probably it computes all the group memberships recursively for all users in the domain and checks if they are member of the given group).

Which is the best way to get all members of a group, including its subgroups?


Just in case this might benefit someone else: here is the solution I ended up with. It is just a recursive search, with some extra checks to avoid checking the same group or user twice, e.g. if groupA is member of groupB and groupB is member of groupA or a user is member of more than one group.

using System;
using System.DirectoryServices;
using System.Collections.Generic;

static class Program {

    static IEnumerable<SearchResult> GetMembers(DirectoryEntry searchRoot, string groupDn, string objectClass) {
        using (DirectorySearcher searcher = new DirectorySearcher(searchRoot)) {
            searcher.Filter = "(&(objectClass=" + objectClass + ")(memberOf=" + groupDn + "))";
            searcher.PropertiesToLoad.Clear();
            searcher.PropertiesToLoad.AddRange(new string[] { 
                "objectGUID",
                "sAMAccountName",
                "distinguishedName"});
            searcher.Sort = new SortOption("sAMAccountName", SortDirection.Ascending);
            searcher.PageSize = 1000;
            searcher.SizeLimit = 0;
            foreach (SearchResult result in searcher.FindAll()) {
                yield return result;
            }
        }
    }

    static IEnumerable<SearchResult> GetUsersRecursively(DirectoryEntry searchRoot, string groupDn) {
        List<string> searchedGroups = new List<string>();
        List<string> searchedUsers = new List<string>();
        return GetUsersRecursively(searchRoot, groupDn, searchedGroups, searchedUsers);
    }

    static IEnumerable<SearchResult> GetUsersRecursively(
        DirectoryEntry searchRoot,
        string groupDn,
        List<string> searchedGroups,
        List<string> searchedUsers) {
        foreach (var subGroup in GetMembers(searchRoot, groupDn, "group")) {
            string subGroupName = ((string)subGroup.Properties["sAMAccountName"][0]).ToUpperInvariant();
            if (searchedGroups.Contains(subGroupName)) {
                continue;
            }
            searchedGroups.Add(subGroupName);
            string subGroupDn = ((string)subGroup.Properties["distinguishedName"][0]);
            foreach (var user in GetUsersRecursively(searchRoot, subGroupDn, searchedGroups, searchedUsers)) {
                yield return user;
            }
        }
        foreach (var user in GetMembers(searchRoot, groupDn, "user")) {
            string userName = ((string)user.Properties["sAMAccountName"][0]).ToUpperInvariant();
            if (searchedUsers.Contains(userName)) {
                continue;
            }
            searchedUsers.Add(userName);
            yield return user;
        }
    }

    static void Main(string[] args) {
        using (DirectoryEntry searchRoot = new DirectoryEntry("LDAP://DC=x,DC=y")) {
            foreach (var user in GetUsersRecursively(searchRoot, "CN=MainGroup,DC=x,DC=y")) {
                Console.WriteLine((string)user.Properties["sAMAccountName"][0]);
            }
        }
    }

}


    static List<SearchResult> ad_find_all_members(string a_sSearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
    {
        using (DirectoryEntry de = new DirectoryEntry(a_sSearchRoot))
            return ad_find_all_members(de, a_sGroupDN, a_asPropsToLoad);
    }

    static List<SearchResult> ad_find_all_members(DirectoryEntry a_SearchRoot, string a_sGroupDN, string[] a_asPropsToLoad)
    {
        string sDN = "distinguishedName";
        string sOC = "objectClass";
        string sOC_GROUP = "group";
        string[] asPropsToLoad = a_asPropsToLoad;
        Array.Sort<string>(asPropsToLoad);
        if (Array.BinarySearch<string>(asPropsToLoad, sDN) < 0)
        {
            Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
            asPropsToLoad[asPropsToLoad.Length-1] = sDN;
        }
        if (Array.BinarySearch<string>(asPropsToLoad, sOC) < 0)
        {
            Array.Resize<string>(ref asPropsToLoad, asPropsToLoad.Length+1);
            asPropsToLoad[asPropsToLoad.Length-1] = sOC;
        }

        List<SearchResult> lsr = new List<SearchResult>();

        using (DirectorySearcher ds = new DirectorySearcher(a_SearchRoot))
        {
            ds.Filter = "(&(|(objectClass=group)(objectClass=user))(memberOf=" + a_sGroupDN + "))";
            //ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.AddRange(asPropsToLoad);
            //ds.PageSize = 1000;
            //ds.SizeLimit = 0;
            foreach (SearchResult sr in ds.FindAll())
                lsr.Add(sr);
        }

        for(int i=0;i<lsr.Count;i++)
            if (lsr[i].Properties.Contains(sOC) && lsr[i].Properties[sOC].Contains(sOC_GROUP))
                lsr.AddRange(ad_find_all_members(a_SearchRoot, (string)lsr[i].Properties[sDN][0], asPropsToLoad));

        return lsr;
    }

    static void Main(string[] args)
    {
    foreach (var sr in ad_find_all_members("LDAP://DC=your-domain,DC=com", "CN=your-group-name,OU=your-group-ou,DC=your-domain,DC=com", new string[] { "sAMAccountName" }))
        Console.WriteLine((string)sr.Properties["distinguishedName"][0] + " : " + (string)sr.Properties["sAMAccountName"][0]);
    }


To get the members recursively take this (the trick is GetMembers(true) instead of false which is the default value):

    private List<string> GetGroupMembers(string groupName)
    {
        var members = new List<string>();

        try
        {
            using (var pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
            {
                var gp = GroupPrincipal.FindByIdentity(pc, groupName);

                if (gp == null) return members;

                foreach (Principal p in gp.GetMembers(true))
                    members.Add(p.Name);

                members.Sort();
            }
        }
        catch (Exception)
        {
            return new List<string>();
        }

        return members;
    }

I also wanted to know if a user or a computer is in an ActiveDirectory group. And this worked for me also with nested groups (it is part of a WebService but you can use the code also in a standalone application):

    using System.DirectoryServices;
    using System.DirectoryServices.AccountManagement;

    [HttpGet]
    [Route("IsUserInGroup")]
    public HttpResponseMessage IsUserInGroup(string userName, string groupName)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);

        try
        {
            using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
            {
                var gp = GroupPrincipal.FindByIdentity(pc, groupName);
                var up = UserPrincipal.FindByIdentity(pc, userName);

                if (gp == null)
                {
                    response.Content = Common.ConvertToJsonContent($"Group '{groupName}' not found in Active Directory");
                    response.StatusCode = HttpStatusCode.NotFound;
                    return response;
                }

                if (up == null)
                {
                    response.Content = Common.ConvertToJsonContent($"User '{userName}' not found in Active Directory");
                    response.StatusCode = HttpStatusCode.NotFound;
                    return response;
                }

                DirectoryEntry user = new DirectoryEntry($"LDAP://{up.DistinguishedName}");
                DirectorySearcher mySearcher = new DirectorySearcher(user)
                {
                    SearchScope = System.DirectoryServices.SearchScope.Subtree,
                    Filter = $"(memberOf:1.2.840.113556.1.4.1941:={gp.DistinguishedName})"
                };

                SearchResult result = mySearcher.FindOne();

                response.StatusCode = HttpStatusCode.OK;
                response.Content = Common.ConvertToJsonContent(result != null);
            }
        }
        catch (Exception ex)
        {
            response.StatusCode = HttpStatusCode.BadRequest;
            response.Content = Common.ConvertToJsonContent($"{MethodBase.GetCurrentMethod().Name}: {ex.Message}");
        }

        return response;
    }

    [HttpGet]
    [Route("IsComputerInGroup")]
    public HttpResponseMessage IsComputerInGroup(string computerName, string groupName)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);

        try
        {
            using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Common.THE_DOMAIN))
            {
                var gp = GroupPrincipal.FindByIdentity(pc, groupName);
                var cp = ComputerPrincipal.FindByIdentity(pc, computerName);

                if (gp == null)
                {
                    response.Content = Common.ConvertToJsonContent($"Group '{groupName}' not found in Active Directory");
                    response.StatusCode = HttpStatusCode.NotFound;
                    return response;
                }

                if (cp == null)
                {
                    response.Content = Common.ConvertToJsonContent($"Computer '{computerName}' not found in Active Directory");
                    response.StatusCode = HttpStatusCode.NotFound;
                    return response;
                }

                DirectoryEntry computer = new DirectoryEntry($"LDAP://{cp.DistinguishedName}");
                DirectorySearcher mySearcher = new DirectorySearcher(computer)
                {
                    SearchScope = System.DirectoryServices.SearchScope.Subtree,
                    Filter = $"(memberOf:1.2.840.113556.1.4.1941:={gp.DistinguishedName})"
                };

                SearchResult result = mySearcher.FindOne();

                response.StatusCode = HttpStatusCode.OK;
                response.Content = Common.ConvertToJsonContent(result != null);
            }
        }
        catch (Exception ex)
        {
            response.StatusCode = HttpStatusCode.BadRequest;
            response.Content = Common.ConvertToJsonContent($"{MethodBase.GetCurrentMethod().Name}: {ex.Message}");
        }

        return response;
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消