Получить пользователей, которые являются "членами" группы

Я получил рабочее решение, однако я почти уверен, что есть менее ресурсоемкий метод, потому что текущее решение включает в себя выполнение запроса для получения члена группы, а затем запроса для получения информации о каждом пользователе.

Вот код, который у меня есть:

DirectoryEntry root = new DirectoryEntry( "LDAP://server:port" );
DirectorySearcher searcher = new DirectorySearcher( root );
searcher.Filter = "(&(ObjectClass=Group)(CN=foo-group))";

var members = (IEnumerable)searcher.FindOne()
              .GetDirectoryEntry()
              .Invoke( "members" );

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

foreach( object member in members ) {
   DirectoryEntry de = new DirectoryEntry( member );
   results.Add( de.Properties[ "SAMAccountname" ][ 0 ].ToString(), de.Properties[ "cn" ][ 0 ].ToString() );
}

В идеале я хотел бы иметь возможность выполнить один запрос, чтобы каждый пользователь, являющийся членом группы, фильтровал загружаемые свойства и затем отображал их. Так как то так

DirectoryEntry root = new DirectoryEntry( "LDAP://server:port" );
DirectorySearcher searcher = new DirectorySearcher( root );
searcher.PropertiesToLoad.Add( "cn" );
searcher.PropertiesToLoad.Add( "SAMAccountname" );
searcher.Filter = "(&(ObjectClass=user)(memberof=foo-group))";

foreach( var user in searcher.FindAll() ) {
    //do whatever...
}

К сожалению, это не работает по какой-то причине.

Ответ 1

Если вы можете использовать System.DirectoryServices.AccountManagement:

var context = new PrincipalContext(ContextType.Domain, "YOUR_DOMAIN_NAME");
using (var searcher = new PrincipalSearcher())
{
    var groupName = "YourGroup";
    var sp = new GroupPrincipal(context, groupName);
    searcher.QueryFilter = sp;
    var group = searcher.FindOne() as GroupPrincipal;

    if (group == null)
        Console.WriteLine("Invalid Group Name: {0}", groupName);

    foreach (var f in group.GetMembers())
    {
        var principal = f as UserPrincipal;

        if (principal == null || string.IsNullOrEmpty(principal.Name))
            continue;

        Console.WriteLine("{0}", principal.Name);
    }
}

У меня есть код VB, который также будет работать по-старому, но это определенно проще с AccountManagement.


Здесь код VB, на который я ссылался (опять-таки не очень, но функциональный):

Public Function GetUsersByGroup(de As DirectoryEntry, groupName As String) As IEnumerable(Of DirectoryEntry)
    Dim userList As New List(Of DirectoryEntry)
    Dim group As DirectoryEntry = GetGroup(de, groupName)

    If group Is Nothing Then Return Nothing

    For Each user In GetUsers(de)
        If IsUserInGroup(user, group) Then
            userList.Add(user)
        End If
    Next

    Return userList
End Function

Public Function GetGroup(de As DirectoryEntry, groupName As String) As DirectoryEntry
    Dim deSearch As New DirectorySearcher(de)

    deSearch.Filter = "(&(objectClass=group)(SAMAccountName=" & groupName & "))"

    Dim result As SearchResult = deSearch.FindOne()

    If result Is Nothing Then
        Return Nothing
    End If

    Return result.GetDirectoryEntry()
End Function

Public Function GetUsers(de As DirectoryEntry) As IEnumerable(Of DirectoryEntry)
    Dim deSearch As New DirectorySearcher(de)
    Dim userList As New List(Of DirectoryEntry)

    deSearch.Filter = "(&(objectClass=person))"

    For Each user In deSearch.FindAll()
        userList.Add(user.GetDirectoryEntry())
    Next

    Return userList
End Function

Public Function IsUserInGroup(user As DirectoryEntry, group As DirectoryEntry) As Boolean
    Dim memberValues = user.Properties("memberOf")

    If memberValues Is Nothing OrElse memberValues.Count = 0 Then Return False

    For Each g In memberValues.Value
        If g = group.Properties("distinguishedName").Value.ToString() Then
            Return True
        End If
    Next

    Return False
End Function

И использование:

Dim entries = New DirectoryEntry("LDAP://...")
Dim userList As IEnumerable(Of DirectoryEntry) = GetUsersByGroup(entries, "GroupName")

Ответ 2

using System.DirectoryServices;

DirectoryEntry objEntry = DirectoryEntry(Ldapserver, userid, password);
DirectorySearcher personSearcher = new DirectorySearcher(objEntry);
personSearcher.Filter = string.Format("(SAMAccountName={0}", username);
SearchResult result = personSearcher.FindOne();

if(result != null)
{
    DirectoryEntry personEntry = result.GetDirectoryEntry();
    PropertyValueCollection groups = personEntry.Properties["memberOf"];
    foreach(string g in groups)
    {
        Console.WriteLine(g); // will write group name
    }
}

Я изначально использовал метод, аналогичный тому, что вы опубликовали, и потребовалось около 12 минут, чтобы пробежать всю мою компанию AD и получить результаты. После переключения на этот метод требуется около 2 минут. Вам нужно будет использовать адрес ldapserver, где я написал ldapserver и userid и пароль, а username - имя SAMAccountName для человека, которого вы ищете.

Ответ 3

Если вы проверите ЗДЕСЬ, вы можете сделать следующее:

DirectoryEntry group = new DirectoryEntry("LDAP://CN=foo-group,DC=Cmp,DC=COM");
foreach(object dn in group.Properties["member"] )
    //do whatever

Ответ 4

Он короче, используя GroupPrincipal метод FindByIdentity, который дает также несколько способов определить группу с IdentityType:

        using (var context = new PrincipalContext(ContextType.Domain, "YOUR_DOMAIN_NAME")
        {
            var userPrincipals = GroupPrincipal
                .FindByIdentity(context, IdentityType.SamAccountName, "GROUP_ACCOUNT")
                .GetMembers(true) // recursive
                .OfType<UserPrincipal>();
            ...
        }