Как определить все группы, к которым принадлежит пользователь (включая вложенные группы) в ActiveDirectory и .NET 3.5 - PullRequest
21 голосов
/ 15 марта 2011

У меня есть приложение, использующее авторизацию ActiveDirecotry, и было решено, что оно должно поддерживать вложенные группы AD, например:

MAIN_AD_GROUP
     |
     |-> SUB_GROUP
              | 
              |-> User

Итак, пользователь не напрямую aчлен MAIN_AD_GROUP.Я хотел бы иметь возможность искать пользователя рекурсивно, ища группы, вложенные в MAIN_AD_GROUP.

Основная проблема заключается в том, что я использую .NET 3.5, и в System.DirectoryServices.AccountManagement есть ошибкав .NET 3.5, при этом метод UserPrincipal.IsMemberOf() не будет работать для групп с более 1500 пользователей .Поэтому я не могу использовать UserPrincipal.IsMemberOf() и нет, я также не могу переключиться на .NET 4.

Я решил эту последнюю проблему с помощью следующей функции:

private bool IsMember(Principal userPrincipal, Principal groupPrincipal)
{
    using (var groups = userPrincipal.GetGroups())
    {
        var isMember = groups.Any(g => 
            g.DistinguishedName == groupPrincipal.DistinguishedName);
        return isMember;
    }
}

Но userPrincipal.GetGroups() возвращает только те группы, непосредственным участником которых является пользователь.

Как заставить это работать с вложенными группами?

Ответы [ 4 ]

33 голосов
/ 02 декабря 2011

Обходной путь # 1

Об этой ошибке сообщается здесь, в Microsoft Connect вместе со следующим кодом, который решает эту проблему, вручную перебирая возвращенные объекты PrincipalSearchResult<Principal>, перехватывая это исключение и продолжая:

PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
var iterGroup = groups.GetEnumerator();
using (iterGroup)
{
    while (iterGroup.MoveNext())
    {
        try
        {
            Principal p = iterGroup.Current;
            Console.WriteLine(p.Name);
        }
        catch (NoMatchingPrincipalException pex)
        {
            continue;
        }
    }
}

Обходной путь # 2

Другой обходной путь , найденный здесь , избегает использования класса AccountManagement и использует вместо него System.DirectoryServices API:

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

namespace GetGroupsForADUser  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            String username = "Gabriel";  

            List<string> userNestedMembership = new List<string>();  

            DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain
            //DirectoryEntry domainConnection = new DirectoryEntry("LDAP://example.com", "username", "password"); // Use this to query a remote domain

            DirectorySearcher samSearcher = new DirectorySearcher();  

            samSearcher.SearchRoot = domainConnection;  
            samSearcher.Filter = "(samAccountName=" + username + ")";  
            samSearcher.PropertiesToLoad.Add("displayName");  

            SearchResult samResult = samSearcher.FindOne();  

            if (samResult != null)  
            {  
                DirectoryEntry theUser = samResult.GetDirectoryEntry();  
                theUser.RefreshCache(new string[] { "tokenGroups" });  

                foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])  
                {  
                    System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0);  

                    DirectorySearcher sidSearcher = new DirectorySearcher();  

                    sidSearcher.SearchRoot = domainConnection;  
                    sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";  
                    sidSearcher.PropertiesToLoad.Add("distinguishedName");  

                    SearchResult sidResult = sidSearcher.FindOne();  

                    if (sidResult != null)  
                    {  
                        userNestedMembership.Add((string)sidResult.Properties["distinguishedName"][0]);  
                    }  
                }  

                foreach (string myEntry in userNestedMembership)  
                {  
                    Console.WriteLine(myEntry);  
                }  

            }  
            else 
            {  
                Console.WriteLine("The user doesn't exist");  
            }  

            Console.ReadKey();  

        }  
    }  
}  
13 голосов
/ 15 марта 2011

Вместо этого используйте UserPrincipal.GetAuthorizationGroups() - из документов MSDN :

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

Группы, которые возвращаются этим метод может включать группы из другой объем и магазин, чем главный. Например, если принципалом является объект AD DS, который имеет DN «CN = SpecialGroups, DC = Fabrikam, DC = ком, возвращенный набор может содержать группы которые принадлежат «CN = NormalGroups, DC = Fabrikam, DC = COM.

6 голосов
/ 17 февраля 2016

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

public static class AccountManagementExtensions
{
    public static bool IsNestedMemberOf(this Principal principal, GroupPrincipal group)
    {
        // LDAP Query for memberOf Nested 
        var filter = String.Format("(&(sAMAccountName={0})(memberOf:1.2.840.113556.1.4.1941:={1}))",
                principal.SamAccountName,
                group.DistinguishedName
            );

        var searcher = new DirectorySearcher(filter);

        var result = searcher.FindOne();

        return result != null;
    }
}
1 голос
/ 30 июля 2015

Эффективный способ - выполнить один запрос AD, используя правильный фильтр DirectorySearcher, например, для

public bool CheckMemberShip(string userName)
    {

        bool membership = false;
        string connection = "LDAP://"+YOURDOMAIN;
        DirectoryEntry entry = new DirectoryEntry(connection);
        DirectorySearcher mySearcher = new DirectorySearcher(entry);
        mySearcher.Filter = "(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=cn=GROUPNAME,OU=Groups,OU=ABC,OU=ABC,OU=IND,DC=ad,DC=COMPANY,DC=com)(|(sAMAccountName=" + userName + ")))";
        SearchResult result = mySearcher.FindOne();

        // No search result, hence no membership
        if (result == null)
        {
            membership = false;
        }

        entry.Close();
        entry.Dispose();
        mySearcher.Dispose();

        membership = true;
        return membership;
    }

Вам необходимо заменить YOURDOMAIN и GROUPNAME на правильные значения из вашей AD.

Источник: Как рекурсивно получить членство пользователя в группе в Active Directory с использованием .NET / C # и LDAP (без двух обращений к Active Directory)включить using System.DirectoryServices;

...