在多森林环境中获取Domain Local Group的所有成员

时间:2013-12-18 22:38:37

标签: c# .net active-directory

我有一个域本地组,它是林A中域A的一部分。

我正在尝试迭代这个组中的所有成员。它迭代遍历林A的所有域,但不会迭代域B中域B中的任何成员。

是从不同的林开始迭代相同代码的唯一方法吗?

我们已尝试使用System.DirectoryServices.AccountManagement类,但它们和Windows Server 2012域控制器似乎存在问题。

private List<User> getUsersInGroup(string groupDN)
    {
        var users = new List<User>();

        using (DirectoryEntry de = new DirectoryEntry("GC://rootDSE"))
        {
            var rootName = de.Properties["rootDomainNamingContext"].Value.ToString();
            using (var userBinding = new DirectoryEntry("GC://" + rootName))
            {
                using (DirectorySearcher adSearch = new DirectorySearcher(userBinding))
                {
                    adSearch.ReferralChasing = ReferralChasingOption.All;
                    adSearch.Filter = String.Format("(&(memberOf={0})(objectClass=person))", groupDN);
                    adSearch.PropertiesToLoad.Add("distinguishedName");
                    adSearch.PropertiesToLoad.Add("givenname");
                    adSearch.PropertiesToLoad.Add("samaccountname");
                    adSearch.PropertiesToLoad.Add("sn");
                    adSearch.PropertiesToLoad.Add("title");
                    adSearch.PropertiesToLoad.Add("displayName");
                    adSearch.PropertiesToLoad.Add("department");

                    using (var searchResults = adSearch.FindAll())
                    {
                        foreach (SearchResult result in searchResults)
                        {
                            User u = new User();

                            u.UserName = result.Properties["samaccountname"][0].ToString();
                            u.DistinguishedName = result.Properties["distinguishedName"][0].ToString();
                            if (result.Properties.Contains("title"))
                            {
                                u.Title = result.Properties["title"][0].ToString();
                            }

                            if (result.Properties.Contains("department"))
                            {
                                u.Department = result.Properties["department"][0].ToString();
                            }

                            if (result.Properties.Contains("displayName"))
                            {
                                u.DisplayName = result.Properties["displayName"][0].ToString();
                            }
                            u.DomainName = getDomainFromDN(u.DistinguishedName);


                            users.Add(u);
                        }
                    }
                }


            }
        }


        return users;
    }

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

您无法使用 memberOf 属性搜索其他林中的群组成员,因为当您将用户添加到本地域时,它无法设置属于另一个森林的群体。

相反,AD会在具有目标用户的SID作为其 CN 的组的域中创建类型为 ForeignSecurityPrincipal 的对象。然后,该对象的 DN 会添加到该群组的成员属性中。

不幸的是,与用户对象不同, foreingSecurityPrincipal 对象永远不会获得 memberOf 属性,因此即使您的搜索也无法找到它们删除 objectType 条件。

因此,您应该按照rufanov的建议反转搜索并枚举群组的成员属性。

但您还应该扩展代码以处理这些外部安全主体。要检测 DirectoryEntry 是否表示外部主体,您可以检查其对象类是否包含 foreignSecurityPricipal 。如果是, CN 属性将包含您可以用来搜索 objectSid 属性的SID

if (de.Properties["objectClass"].Contains("foreignSecurityPrincipal"))
{
    // use this value in a search condition for objectSid
    var sidString = de.Properties["cn"].Cast<string>().First();

    IdentityReference id = new SecurityIdentifier(sid);

    var account = id.Translate(typeof(NTAccount)).ToString().Split('\\');

    var userName = account[1];
    var domainName = account[0];
}

答案 1 :(得分:2)

为什么您不能只枚举目标组的成员,而不是搜索具有目标组作为“memberof”属性值之一的用户的整个林?它比使用DirectorySearcher执行此任务要快得多/简单得多。组“成员”属性是林范围的,因此它将包括来自林中任何域的成员。

获取用户集合的代码可能如下所示:

using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;

namespace AdTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            var users = p.GetMembersOf("LDAP://CN=SQL Adminsistrators,OU=_System accounts and groups,OU=Domain Users,DC=test,DC=net");
            // do something with users
        }

        private List<User> GetMembersOf(string groupdn)
        {
            var context = new DirectoryContext(DirectoryContextType.Forest);
            var Result = new List<User>();
            var GroupEntity = new DirectoryEntry(groupdn);
            var Members = (IEnumerable)GroupEntity.Invoke("Members", null);

            foreach (var member in Members)
            {
                var UserEntry = new DirectoryEntry(member);
                var User = GetUser(UserEntry);

                if(User != null)
                    Result.Add(User);
            }


            return Result;

        }

        private User GetUser(DirectoryEntry UserEntry)
        {
            var Result = new User();

            foreach (PropertyValueCollection UserProperty in UserEntry.Properties)
            {
                switch (UserProperty.PropertyName)
                {
                    case "sAMAccountName":
                        Result.UserName = (string)UserProperty.Value;
                        break;
                    case "distinguishedName":
                        Result.DistinguishedName = (string)UserProperty.Value;
                        Result.DomainName = getDomainFromDN((string)UserProperty.Value);
                        break;
                    case "title":
                        Result.Title = (string)UserProperty.Value;
                        break;
                    case "department":
                        Result.Department = (string)UserProperty.Value;
                        break;
                    case "displayName":
                        Result.DisplayName = (string)UserProperty.Value;
                        break;
                    case "objectClass":
                        var UserClasses = (object[])UserProperty.Value;

                        if (UserClasses.Contains("user"))
                            break;
                        else
                            return null;

                    default:
                        break;
                }
            }
            return Result;
        }

        private string getDomainFromDN(string p)
        {
            return string.Empty;
        }

        public string groupDN { get; set; }
    }
}