【问题标题】:Recursively querying LDAP group membership递归查询 LDAP 组成员资格
【发布时间】:2011-10-19 19:24:08
【问题描述】:

我正在编写一个基于 MVC (.NET 4.0) 的网站,该网站需要来自公司 LDAP 服务器的登录凭据。我的代码需要的是只允许属于某个组的用户。例如,我可能正在寻找属于“企业 IT”组的用户。我的凭据可能是“系统管理员”组的一部分,该组是“公司 IT”的子组。我正在使用表单身份验证。

我如何递归检查用户登录时属于哪个组?

【问题讨论】:

    标签: c# asp.net-mvc-3 ldap


    【解决方案1】:

    对于通过搜索此类查询来到这里的其他人,这是我在应用程序中的做法:

    关键是1.2.840.113556.1.4.1941扩展搜索过滤器。由于此特定过滤器仅适用于 DN,因此我首先获取要检查的用户的 DN,然后查询组以查看此特定用户是否是链中任何组的成员。

    internal const string UserNameSearchFilter = "(&(objectCategory=user)(objectClass=user)(|(userPrincipalName={0})(samAccountName={0})))";
    internal const string MembershipFilter = "(&(objectCategory=group)(objectClass=group)(cn=MyGroup)(member:1.2.840.113556.1.4.1941:={0}))";
    
    using (var de = new DirectoryEntry(AppSettings.LDAPRootContainer, AppSettings.AdminUser, AppSettings.AdminPassword, AuthenticationTypes.FastBind))
    using (var ds = new DirectorySearcher(de) { Filter = string.Format(UserNameSearchFilter, username) })
    {
    
        ds.PropertiesToLoad.AddRange(new[] { "distinguishedName" });
    
        var user = ds.FindOne();
    
        if (user != null)
            using (var gds = new DirectorySearcher(de) { PropertyNamesOnly = true, Filter = string.Format(MembershipFilter, user.Properties["distinguishedName"][0] as string) })
            {
                 gds.PropertiesToLoad.AddRange(new[] { "objectGuid" });
                 return gds.FindOne() != null;
            }
    }
    

    【讨论】:

      【解决方案2】:

      我发现使用带有递归标志的GroupPrincipal.GetMembers 既快速又有效。

      public bool IsMember(string groupName, string samAccountName)
      {
          using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
          using (UserPrincipal user = UserPrincipal.FindByIdentity(context, samAccountName))
          using(GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName))
          {
              return group.GetMembers(true).OfType<Principal>().Any(u => u.SamAccountName.Equals(user.SamAccountName, StringComparison.InvariantCultureIgnoreCase));
          }
      
      }
      

      【讨论】:

        【解决方案3】:

        如果要检查特定用户的成员资格,请绑定到相关 AD 对象并检索 tokenGroups 属性。它包含二进制形式的所有直接和间接组成员身份 - 它是一个字节数组的数组。每个字节数组都可以传递给 SecurityIdentifier 类的构造函数,然后转换为 NTAccount,其中包含明文形式的组名称。

        var sids = new IdentityReferenceCollection();
        foreach (byte[] group in tokenGroups)
        {
            sids.Add(new SecurityIdentifier(group, 0));
        }
        var accounts = sids.Translate(typeof(NTAccount));
        

        【讨论】:

        • 此技术有效,但不包括分发列表,仅包括安全组。如果有人也知道如何获取分发列表,那就太好了。
        • 确实,我正在寻找组枚举。
        • 包含通讯组的组枚举?
        【解决方案4】:

        这是一个完全不同的解决方案。经过测试并在我的域上工作。一些注意事项:您必须正确设置 DirectorySearcher.Filter。为您的 AD 层次结构添加多个 OU(以相反的顺序,自下而上)。另请注意,为了安全起见,我在“使用”语句中处理了一些对象,因为它们实现了 System.ComponentModel.Component,而后者又实现了 IDisposable……所以,安全总比抱歉好。

        public bool IsUserMemberOfGroup(string groupName)
            {
                // CN is your distro group name. OU is the object(s) in your AD hierarchy. DC is for your domain and domain suffix (e.g., yourDomain.local)
                string searchFilter = String.Format(@"(&(objectcategory=user)(sAMAccountName=markp)(memberof=CN={0},OU=System Admins,OU=USA,DC=yourDomain,DC=local))", groupName);
                SearchResultCollection searchResult;
        
                using (var dirEntry = new DirectoryEntry("LDAP://dc=yourDomain,dc=local"))
                {
                    using (var dirSearch = new DirectorySearcher(dirEntry))
                    {
                        dirSearch.SearchScope = SearchScope.Subtree;
                        dirSearch.Filter = searchFilter;
                        searchResult = dirSearch.FindAll();
                    }
                }
                if (searchResult.Count <= 0 || searchResult == null) {
                    return false; // not in group
                }
                else {
                    return true; // in group
                }
            }
        

        【讨论】:

        • 根据 MSDN,GetGroups 方法不执行组成员身份的递归查找,因此它不会返回间接组成员身份。 GetAuthorizationGroups 可以进行递归查找,但仅限于安全组,就像我的方法一样。
        • 你是对的亨宁。接得好。我认为这个问题可能有些模糊。 @MarkP 再想一想,您实际上不需要查看/列出所有组,对吗?您只需要验证用户是否在某个(AD 发行版)组中。那是对的吗?如果是这样,那是可行的。否则,您正在查看 recursion-he*l,甚至可能调用 PowerShell。
        • 是的,我只需要验证某个用户是否属于更大的组。
        • @MarkP 完全改变了答案。解决方案现在应该适合您。
        • @PatrickPitre - 这个新解决方案只会获得直接的组成员资格,因为您正在测试 memberOf 属性。