首页 > 解决方案 > 检查 AD 组是否是另一个组的成员(递归)

问题描述

想象一下我有结构

RootGroup <- Group{x} .... <- Group{x+n} <- Group100 

我怎么能检查那Group100是成员RootGroup

我有这个,它总是返回false

private bool IsMemberOfInternal(string userOrGroupDistinguishedName, string groupMembershipDistinguishedName)
        {
            GroupPrincipal principal = null;
            GroupPrincipal target = null;
            try
            {
                principal = _getUserGroupPrincipalFunc(principalContext, userOrGroupDistinguishedName);
                target = _getUserGroupPrincipalFunc(principalContext, groupMembershipDistinguishedName);

                if (principal != default(GroupPrincipal)
                    && target != default(GroupPrincipal))
                {
                    return principal.IsMemberOf(target);
                }
            }
            catch
            {
            }

            return false;
        }

标签: c#active-directoryactive-directory-groupactivedirectorymembership

解决方案


你最好不要使用GroupPrincipal这个。AD 实际上有一个内置的方法来做这种搜索,它比任何事情GroupPrincipal都快得多。您可以通过使用DirectoryEntryandDirectorySearcher直接使用它(无论如何,这就是GroupPrincipal幕后PrincipalSearcher使用的内容)。

我写了一篇关于确定用户是否是特定组的成员的文章,但它同样适用于组。我有一个示例方法,您可以使用它:

private static bool IsUserInGroup(DirectoryEntry user, DirectoryEntry group, bool recursive) {

    //fetch the attributes we're going to need
    user.RefreshCache(new [] {"distinguishedName", "objectSid"});
    group.RefreshCache(new [] {"distinguishedName", "groupType"});

    //This magic number tells AD to look for the user recursively through any nested groups
    var recursiveFilter = recursive ? ":1.2.840.113556.1.4.1941:" : "";

    var userDn = (string) user.Properties["distinguishedName"].Value;
    var groupDn = (string) group.Properties["distinguishedName"].Value;

    var filter = $"(member{recursiveFilter}={userDn})";

    if (((int) group.Properties["groupType"].Value & 8) == 0) {
        var groupDomainDn = groupDn.Substring(
            groupDn.IndexOf(",DC=", StringComparison.Ordinal));
        var userDomainDn = userDn.Substring(
            userDn.IndexOf(",DC=", StringComparison.Ordinal));
        if (groupDomainDn != userDomainDn) {
            //It's a Domain Local group, and the user and group are on
            //different domains, so the account might show up as a Foreign
            //Security Principal. So construct a list of SID's that could
            //appear in the group for this user
            var fspFilters = new StringBuilder();

            var userSid =
                new SecurityIdentifier((byte[]) user.Properties["objectSid"].Value, 0);
            fspFilters.Append(
                $"(member{recursiveFilter}=CN={userSid},CN=ForeignSecurityPrincipals{groupDomainDn})");

            if (recursive) {
                //Any of the groups the user is in could show up as an FSP,
                //so we need to check for them all
                user.RefreshCache(new [] {"tokenGroupsGlobalAndUniversal"});
                var tokenGroups = user.Properties["tokenGroupsGlobalAndUniversal"];
                foreach (byte[] token in tokenGroups) {
                    var groupSid = new SecurityIdentifier(token, 0);
                    fspFilters.Append(
                        $"(member{recursiveFilter}=CN={groupSid},CN=ForeignSecurityPrincipals{groupDomainDn})");
                }
            }
            filter = $"(|{filter}{fspFilters})";
        }
    }

    var searcher = new DirectorySearcher {
        Filter = filter,
        SearchRoot = group,
        PageSize = 1, //we're only looking for one object
        SearchScope = SearchScope.Base
    };

    searcher.PropertiesToLoad.Add("cn"); //just so it doesn't load every property

    return searcher.FindOne() != null;
}

此方法还处理user(或您的子组)位于根组的外部受信任域上的情况。这可能是也可能不是您必须担心的事情。

只需将 a作为参数传递DirectoryEntry给您。像这样的东西:Group100user

var isMemberOf = IsUserInGroup(
    new DirectoryEntry($"LDAP://{userOrGroupDistinguishedName}"),
    new DirectoryEntry($"LDAP://{groupMembershipDistinguishedName}"),
    true);

true对于递归搜索(当您传递recursive参数时),它使用LDAP_MATCHING_RULE_IN_CHAIN“匹配规则 OID”(如此所述):

此规则仅限于适用于 DN 的过滤器。这是一个特殊的“扩展”匹配运算符,它将对象中的祖先链一直遍历到根,直到找到匹配项。


推荐阅读