How to get all the AD groups for a particular user?

后端 未结 10 1617
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-28 04:19

I checked this post already. But it doesn\'t answer my question. I want to get all the active directory groups in which a particular user is a member.

I\'ve written

相关标签:
10条回答
  • 2020-11-28 04:52

    I would like to say that Microsoft LDAP has some special ways to search recursively for all of memberships of a user.

    1. The Matching Rule you can specify for the "member" attribute. In particular, using the Microsoft Exclusive LDAP_MATCHING_RULE_IN_CHAIN rule for "member" attribute allows recursive/nested membership searching. The rule is used when you add it after the member attribute. Ex. (member:1.2.840.113556.1.4.1941:= XXXXX )

    2. For the same Domain as the Account, The filter can use <SID=S-1-5-21-XXXXXXXXXXXXXXXXXXXXXXX> instead of an Accounts DistinguishedName attribute which is very handy to use cross domain if needed. HOWEVER it appears you need to use the ForeignSecurityPrincipal <GUID=YYYY> as it will not resolve your SID as it appears the <SID=> tag does not consider ForeignSecurityPrincipal object type. You can use the ForeignSecurityPrincipal DistinguishedName as well.

    Using this knowledge, you can LDAP query those hard to get memberships, such as the "Domain Local" groups an Account is a member of but unless you looked at the members of the group, you wouldn't know if user was a member.

    //Get Direct+Indirect Memberships of User (where SID is XXXXXX)
    
    string str = "(& (objectCategory=group)(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
    
    //Get Direct+Indirect **Domain Local** Memberships of User (where SID is XXXXXX)
    
    string str2 = "(& (objectCategory=group)(|(groupType=-2147483644)(groupType=4))(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
    
    //TAA DAA
    



    Feel free to try these LDAP queries after substituting the SID of a user you want to retrieve all group memberships of. I figure this is similiar if not the same query as what the PowerShell Command Get-ADPrincipalGroupMembership uses behind the scenes. The command states "If you want to search for local groups in another domain, use the ResourceContextServer parameter to specify the alternate server in the other domain."

    If you are familiar enough with C# and Active Directory, you should know how to perform an LDAP search using the LDAP queries provided.

    Additional Documentation:

    • <SID> Binding String
    • <GUID> Binding String
    0 讨论(0)
  • 2020-11-28 05:04

    Here is the code that worked for me:

    public ArrayList GetBBGroups(WindowsIdentity identity)
    {
        ArrayList groups = new ArrayList();
    
        try
        {
            String userName = identity.Name;
            int pos = userName.IndexOf(@"\");
            if (pos > 0) userName = userName.Substring(pos + 1);
    
            PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
            UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName);
    
            DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
            DirectorySearcher search = new DirectorySearcher(de);
            search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
            search.PropertiesToLoad.Add("samaccountname");
            search.PropertiesToLoad.Add("cn");
    
            String name;
            SearchResultCollection results = search.FindAll();
            foreach (SearchResult result in results)
            {
                name = (String)result.Properties["samaccountname"][0];
                if (String.IsNullOrEmpty(name))
                {
                    name = (String)result.Properties["cn"][0];
                }
                GetGroupsRecursive(groups, de, name);
            }
        }
        catch
        {
            // return an empty list...
        }
    
        return groups;
    }
    
    public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn)
    {
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))";
        search.PropertiesToLoad.Add("memberof");
    
        String group, name;
        SearchResult result = search.FindOne();
        if (result == null) return;
    
        group = @"RIOMC\" + dn;
        if (!groups.Contains(group))
        {
            groups.Add(group);
        }
        if (result.Properties["memberof"].Count == 0) return;
        int equalsIndex, commaIndex;
        foreach (String dn1 in result.Properties["memberof"])
        {
            equalsIndex = dn1.IndexOf("=", 1);
            if (equalsIndex > 0)
            {
                commaIndex = dn1.IndexOf(",", equalsIndex + 1);
                name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
                GetGroupsRecursive(groups, de, name);
            }
        }
    }
    

    I measured it's performance in a loop of 200 runs against the code that uses the AttributeValuesMultiString recursive method; and it worked 1.3 times faster. It might be so because of our AD settings. Both snippets gave the same result though.

    0 讨论(0)
  • 2020-11-28 05:06

    This is how I list all the groups (direct and indirect) for a specific Distinguished Name:

    The string 1.2.840.113556.1.4.1941 specifies LDAP_MATCHING_RULE_IN_CHAIN.

    This rule is limited to filters that apply to the DN. This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.

    This method is 25 times faster than the UserPrincipal.GetGroups() method in my testing.

    Note: The primary group (typically Domain Users) is not returned by this or GetGroups() method. To get the primary group name too, I've confirmed this method works.

    Additionally, I found this list of LDAP filters extremely useful.

    private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
    {
        var groups = new List<string>();
        if (!string.IsNullOrEmpty(distinguishedName))
        {
            var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName})))";
            using (var dirSearch = CreateDirectorySearcher(domainDirectoryEntry, getGroupsFilterForDn))
            {
                dirSearch.PropertiesToLoad.Add("name");
    
                using (var results = dirSearch.FindAll())
                {
                    foreach (SearchResult result in results)
                    {
                        if (result.Properties.Contains("name"))
                            groups.Add((string)result.Properties["name"][0]);
                    }
                }
            }
        }
    
        return groups;
    }
    
    0 讨论(0)
  • 2020-11-28 05:07

    This code works even faster (two 1.5 faster than my previous version):

        public List<String> GetUserGroups(WindowsIdentity identity)
        {
            List<String> groups = new List<String>();
    
            String userName = identity.Name;
            int pos = userName.IndexOf(@"\");
            if (pos > 0) userName = userName.Substring(pos + 1);
    
            PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
            UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov
    
            DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
            DirectorySearcher search = new DirectorySearcher(de);
            search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
            search.PropertiesToLoad.Add("cn");
            search.PropertiesToLoad.Add("samaccountname");
            search.PropertiesToLoad.Add("memberOf");
    
            SearchResultCollection results = search.FindAll();
            foreach (SearchResult sr in results)
            {
                GetUserGroupsRecursive(groups, sr, de);
            }
    
            return groups;
        }
    
        public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
        {
            if (sr == null) return;
    
            String group = (String)sr.Properties["cn"][0];
            if (String.IsNullOrEmpty(group))
            {
                group = (String)sr.Properties["samaccountname"][0];
            }
            if (!groups.Contains(group))
            {
                groups.Add(group);
            }
    
            DirectorySearcher search;
            SearchResult sr1;
            String name;
            int equalsIndex, commaIndex;
            foreach (String dn in sr.Properties["memberof"])
            {
                equalsIndex = dn.IndexOf("=", 1);
                if (equalsIndex > 0)
                {
                    commaIndex = dn.IndexOf(",", equalsIndex + 1);
                    name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
    
                    search = new DirectorySearcher(de);
                    search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
                    search.PropertiesToLoad.Add("cn");
                    search.PropertiesToLoad.Add("samaccountname");
                    search.PropertiesToLoad.Add("memberOf");
                    sr1 = search.FindOne();
                    GetUserGroupsRecursive(groups, sr1, de);
                }
            }
        }
    
    0 讨论(0)
  • 2020-11-28 05:10

    Use tokenGroups:

    DirectorySearcher ds = new DirectorySearcher();
    ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
    SearchResult sr = ds.FindOne();
    
    DirectoryEntry user = sr.GetDirectoryEntry();
    user.RefreshCache(new string[] { "tokenGroups" });
    
    for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
        SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
        NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
        //do something with the SID or name (nt.Value)
    }
    

    Note: this only gets security groups

    0 讨论(0)
  • 2020-11-28 05:11
    PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, "DomainName", UserAccountOU, UserName, Password);
    UserPrincipal UserPrincipalID = UserPrincipal.FindByIdentity(pc1, IdentityType.SamAccountName, UserID);
    
    searcher.Filter = "(&(ObjectClass=group)(member = " + UserPrincipalID.DistinguishedName + "));
    
    0 讨论(0)
提交回复
热议问题