Получить членов группы Active Directory рекурсивно, т. е. включая подгруппы

Учитывая такую ​​​​группу в Active Directory:

MainGroup
  GroupA
    User1
    User2
  GroupB
    User3
  User4

Я могу легко определить, является ли User3 членом MainGroup или какой-либо из ее подгрупп, с помощью такого кода:

using System;
using System.DirectoryServices;

static class Program {
    static void Main() {
        DirectoryEntry user = new DirectoryEntry("LDAP://CN=User3,DC=X,DC=y");
        string filter = "(memberOf:1.2.840.113556.1.4.1941:=CN=MainGroup,DC=X,DC=y)";
        DirectorySearcher searcher = new DirectorySearcher(user, filter);
        searcher.SearchScope = SearchScope.Subtree;
        var r = searcher.FindOne();
        bool isMember = (r != null);
    }
}

Я хотел бы знать, есть ли аналогичный способ получить всех пользователей, которые являются членами группы или любой из ее подгрупп, т.е. в примере для MainGroup получить User1, User2, User3 и User4.

Очевидным способом получения всех пользователей является рекурсивный запрос каждой подгруппы, но мне было интересно, есть ли более простой способ сделать это.

Использование того же подхода с фильтром memberOf:1.2.840.113556.1.4.1941:, но использование корня домена вместо пользователя в качестве базы поиска невозможно, так как запрос занимает слишком много времени (вероятно, он рекурсивно вычисляет членство в группах для всех пользователей в домене и проверяет, они входят в данную группу).

Как лучше всего получить всех членов группы, включая ее подгруппы?


person Paolo Tedesco    schedule 08.09.2010    source источник


Ответы (1)


На всякий случай это может принести пользу кому-то другому: вот решение, с которым я столкнулся. Это просто рекурсивный поиск с некоторыми дополнительными проверками, чтобы избежать повторной проверки одной и той же группы или пользователя, например. если группа A является членом группы B, а группа B является членом группы A или пользователь является членом более чем одной группы.

using System;
using System.DirectoryServices;
using System.Collections.Generic;

static class Program {

    static IEnumerable<SearchResult> GetMembers(DirectoryEntry searchRoot, string groupDn, string objectClass) {
        using (DirectorySearcher searcher = new DirectorySearcher(searchRoot)) {
            searcher.Filter = "(&(objectClass=" + objectClass + ")(memberOf=" + groupDn + "))";
            searcher.PropertiesToLoad.Clear();
            searcher.PropertiesToLoad.AddRange(new string[] { 
                "objectGUID",
                "sAMAccountName",
                "distinguishedName"});
            searcher.Sort = new SortOption("sAMAccountName", SortDirection.Ascending);
            searcher.PageSize = 1000;
            searcher.SizeLimit = 0;
            foreach (SearchResult result in searcher.FindAll()) {
                yield return result;
            }
        }
    }

    static IEnumerable<SearchResult> GetUsersRecursively(DirectoryEntry searchRoot, string groupDn) {
        List<string> searchedGroups = new List<string>();
        List<string> searchedUsers = new List<string>();
        return GetUsersRecursively(searchRoot, groupDn, searchedGroups, searchedUsers);
    }

    static IEnumerable<SearchResult> GetUsersRecursively(
        DirectoryEntry searchRoot,
        string groupDn,
        List<string> searchedGroups,
        List<string> searchedUsers) {
        foreach (var subGroup in GetMembers(searchRoot, groupDn, "group")) {
            string subGroupName = ((string)subGroup.Properties["sAMAccountName"][0]).ToUpperInvariant();
            if (searchedGroups.Contains(subGroupName)) {
                continue;
            }
            searchedGroups.Add(subGroupName);
            string subGroupDn = ((string)subGroup.Properties["distinguishedName"][0]);
            foreach (var user in GetUsersRecursively(searchRoot, subGroupDn, searchedGroups, searchedUsers)) {
                yield return user;
            }
        }
        foreach (var user in GetMembers(searchRoot, groupDn, "user")) {
            string userName = ((string)user.Properties["sAMAccountName"][0]).ToUpperInvariant();
            if (searchedUsers.Contains(userName)) {
                continue;
            }
            searchedUsers.Add(userName);
            yield return user;
        }
    }

    static void Main(string[] args) {
        using (DirectoryEntry searchRoot = new DirectoryEntry("LDAP://DC=x,DC=y")) {
            foreach (var user in GetUsersRecursively(searchRoot, "CN=MainGroup,DC=x,DC=y")) {
                Console.WriteLine((string)user.Properties["sAMAccountName"][0]);
            }
        }
    }

}
person Paolo Tedesco    schedule 08.09.2010
comment
Этот пост был здесь некоторое время, но я хотел бы поблагодарить за публикацию вашего ответа. Я только что столкнулся с той же проблемой, и я собираюсь попробовать это. - person Steven; 22.10.2010
comment
Я не знаю, увидите ли вы это (или даже если это самый подходящий способ задать подобный вопрос, но у меня возникли некоторые проблемы с тем, чтобы заставить это работать, и я думаю, что это, вероятно, из-за моего отсутствия AD Когда я получаю уникальное имя (DN) своей группы, она возвращает что-то вроде этого: «CN=‹УДАЛЕНО›, OU=‹УДАЛЕНО›, OU=Distribution List,OU=Exchange Services,OU=Core Directory Services, DC=‹ОТРЕДАКТИРОВАНО›,DC=com' Но когда я попытался выполнить поиск по этому (или этому без начального CN=), я получаю Unspecified Error в точке, где поисковик перебирает FindAll() - person Steven; 23.10.2010
comment
@Steven: я думаю, что лучше всего для вас опубликовать новый вопрос (со ссылкой на этот, если это связанная проблема). Прямо сейчас я не могу вам помочь, так как мне не хватает домена для тестирования, извините :) - person Paolo Tedesco; 23.10.2010