Не удалось успешно выполнить итерацию по объекту AccountManagement.GroupPrincipal GetMembers. - PullRequest
0 голосов
/ 01 декабря 2018

Я использую System.DirectoryServices.AccountManagement.GroupPrincipal FindByIdentity в C # для создания объекта, содержащего членов группы (идентификаторы и имена пользователей) для целевой группы.Моя цель состоит в том, чтобы перебрать итоговый список UserPrincipals и распечатать SamAccountName и DisplayName для каждого.Для некоторых целевых групп это работает нормально;для других происходит сбой пользователя (или, возможно, более одного), который выдает следующую ошибку:

System.DirectoryServices.AccountManagement.PrincipalOperationException HResult = 0x80131501 Сообщение = указанный атрибут службы каталогов или значение несуществовать.

Когда я использую Get-ADGroup PowerShell, чтобы получить объект группы для одной из неудачных целей и выполнить итерацию по ней, проблем не возникает.

Я изучилчленство в AD Group, и я считаю, что проблема заключается в том, что в некоторых группах (которые терпят неудачу) некоторые участники могут быть отключены или могут быть частью междоменного доверия.Однако их статус не имеет для меня никакого значения;Я просто хочу перечислить все, чтобы владелец группы мог решить, какие члены переносятся в новые группы.

Я использую следующий метод:

private static ArrayList EnumerateGroupMembers()
{
    ArrayList gmObjects = new ArrayList();
    string ldapVal = "DC=dc1,DC=dc2,DC=dcMain,DC=dcSecondary";
    string ldapDom = "dc1.dc2.dcMain.dcSecondary:389";

    PrincipalContext ctx = new PrincipalContext(ContextType.Domain, ldapDom, ldapVal);

    GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "AD-GROUPNAME");

    if (group != null)
    {
        var users = group.GetMembers(true);

        //*** PrincipalOperationException occurs here ***
        foreach (UserPrincipal p in users)
        {
            Console.WriteLine(p.SamAccountName + ", " + p.DisplayName);
        }
        Console.WriteLine("Done");
        Console.ReadKey();
    }
    //*** Please note: I know I am returning an empty list here. I'm writing to Console during development
    return gmObjects;
}

Может кто-нибудь подсказать, как я могу выполнить итерациюсписок UserPrincipals, не выбрасывая исключение PrincipalOperationException?Или, по крайней мере, способ обойти вхождения UserPrincipal, которые вызывают эти ошибки?Даже если я не смогу перечислить отказавших пользователей, я выживу.

1 Ответ

0 голосов
/ 05 декабря 2018

К сожалению, пространство имен System.DirectoryServices.AccountManagement не очень хорошо работает с Foreign Security Principals, как вы уже нашли.

Вы можете сделать это, используя пространство имен System.DirectoryServices, которое AccountManagement использует в своей основе.сцены.Вы, вероятно, обнаружите, что в любом случае он работает лучше, хотя это немного сложнее.

Я хотел написать что-то вроде этого для моего сайта , так что вотметод, который найдет всех членов группы и перечислит их в формате DOMAIN\username.Он также может расширять вложенные группы.

public static List<string> GetGroupMemberList(DirectoryEntry group, bool recurse = false, Dictionary<string, string> domainSidMapping = null) {
    var members = new List<string>();

    group.RefreshCache(new[] { "member", "canonicalName" });

    if (domainSidMapping == null) {
        //Find all the trusted domains and create a dictionary that maps the domain's SID to its DNS name
        var groupCn = (string) group.Properties["canonicalName"].Value;
        var domainDns = groupCn.Substring(0, groupCn.IndexOf("/", StringComparison.Ordinal));

        var domain = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, domainDns));
        var trusts = domain.GetAllTrustRelationships();

        domainSidMapping = new Dictionary<string, string>();

        foreach (TrustRelationshipInformation trust in trusts) {
            using (var trustedDomain = new DirectoryEntry($"LDAP://{trust.TargetName}")) {
                try {
                    trustedDomain.RefreshCache(new [] {"objectSid"});
                    var domainSid = new SecurityIdentifier((byte[]) trustedDomain.Properties["objectSid"].Value, 0).ToString();
                    domainSidMapping.Add(domainSid, trust.TargetName);
                } catch (Exception e) {
                    //This can happen if you're running this with credentials
                    //that aren't trusted on the other domain or if the domain
                   //can't be contacted
                   Console.WriteLine($"Can't connect to domain {trust.TargetName}: {e.Message}");
                }
            }
        }
    }

    while (true) {
        var memberDns = group.Properties["member"];
        foreach (string member in memberDns) {
            using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
                memberDe.RefreshCache(new[] { "objectClass", "msDS-PrincipalName", "cn" });

                if (recurse && memberDe.Properties["objectClass"].Contains("group")) {
                    members.AddRange(GetGroupMemberList(memberDe, true, domainSidMapping));
                } else if (memberDe.Properties["objectClass"].Contains("foreignSecurityPrincipal")) {
                    //User is on a trusted domain
                    var foreignUserSid = memberDe.Properties["cn"].Value.ToString();
                    //The SID of the domain is the SID of the user minus the last block of numbers
                    var foreignDomainSid = foreignUserSid.Substring(0, foreignUserSid.LastIndexOf("-"));
                    if (domainSidMapping.TryGetValue(foreignDomainSid, out var foreignDomainDns)) {
                        using (var foreignUser = new DirectoryEntry($"LDAP://{foreignDomainDns}/<SID={foreignUserSid}>")) {
                            foreignUser.RefreshCache(new[] { "msDS-PrincipalName" });
                            members.Add(foreignUser.Properties["msDS-PrincipalName"].Value.ToString());
                        }
                    } else {
                        //unknown domain
                        members.Add(foreignUserSid);
                    }
                } else {
                    var username = memberDe.Properties["msDS-PrincipalName"].Value.ToString();
                    if (!string.IsNullOrEmpty(username)) {
                        members.Add(username);
                    }
                }
            }
        }

        if (memberDns.Count == 0) break;

        try {
            group.RefreshCache(new[] {$"member;range={members.Count}-*"});
        } catch (COMException e) {
            if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
                break;
            }
            throw;
        }
    }
    return members;
}

Есть несколько вещей, которые нужно сделать:

  1. Атрибут member даст вам только 1500 аккаунтов ввремя, так что вам нужно запрашивать больше, пока не останется ни одного.
  2. Foreign Security Principals имеют SID учетной записи в чужом домене, но вам нужно использовать DNS-имя домена для подключения к нему (т.е. $"LDAP://{foreignDomainDns}/<SID={foreignUserSid}>").Таким образом, этот метод будет искать все доверительные отношения для домена и создавать сопоставление между SID домена и его DNS-именем.

Вы используете его так:

var group = new DirectoryEntry($"LDAP://{distinguishedNameOfGroup}");
var members = GetGroupMemberList(group);

Иливы можете использовать GetGroupMemberList(group, true), если вы хотите найти пользователей и во вложенных группах.

Имейте в виду, что при этом пользователи не будут находить эту группу в качестве своей основной группы, поскольку основная группа не использует member атрибут.Я описываю это в моей Что делает члена членом статьи.В большинстве случаев вам все равно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...