Как получить все группы AD для конкретного пользователя? - PullRequest
35 голосов
/ 16 декабря 2010

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

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

class Program
{
    static void Main(string[] args)
    {
        DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com");
        DirectorySearcher searcher = new DirectorySearcher(de);
        searcher.Filter = "(&(ObjectClass=group))";
        searcher.PropertiesToLoad.Add("distinguishedName");
        searcher.PropertiesToLoad.Add("sAMAccountName");
        searcher.PropertiesToLoad.Add("name");
        searcher.PropertiesToLoad.Add("objectSid");
        SearchResultCollection results = searcher.FindAll();
        int i = 1;
        foreach (SearchResult res in results)
        {
            Console.WriteLine("Result" + Convert.ToString(i++));
            DisplayProperties("distinguishedName", res);
            DisplayProperties("sAMAccouontName", res);
            DisplayProperties("name", res);
            DisplayProperties("objectSid", res);
            Console.WriteLine();
        }

        Console.ReadKey();
    }

    private static void DisplayProperties(string property, SearchResult res)
    {
        Console.WriteLine("\t" + property);
        ResultPropertyValueCollection col = res.Properties[property];
        foreach (object o in col)
        {
            Console.WriteLine("\t\t" + o.ToString());
        }
    }
}

Есть идеи?

Ответы [ 10 ]

35 голосов
/ 07 января 2011

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

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

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
    Console.Out.WriteLine(group);
}
25 голосов
/ 16 декабря 2010

Просто запросите свойство "memberOf" и повторите итерацию, например:

            search.PropertiesToLoad.Add("memberOf");
            StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited

                SearchResult result = search.FindOne();
                int propertyCount = result.Properties["memberOf"].Count;
                String dn;
                int equalsIndex, commaIndex;

                for (int propertyCounter = 0; propertyCounter < propertyCount;
                    propertyCounter++)
                {
                    dn = (String)result.Properties["memberOf"][propertyCounter];

                    equalsIndex = dn.IndexOf("=", 1);
                    commaIndex = dn.IndexOf(",", 1);
                    if (-1 == equalsIndex)
                    {
                        return null;
                    }
                    groupNames.Append(dn.Substring((equalsIndex + 1),
                                (commaIndex - equalsIndex) - 1));
                    groupNames.Append("|");
                }

            return groupNames.ToString();

Это просто вставляет имена групп в строку groupNames с разделителем канала, но когда вы прокручиваете их, вы можете делать с ними что угодно

25 голосов
/ 16 декабря 2010

Использовать tokenGroups :

DirectorySearcher ds = new DirectorySearcher();
ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
SearchResult sr = ds.FindOne();

DirectoryEntry user = sr.GetDirectoryEntry();
user.RefreshCache(new string[] { "tokenGroups" });

for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
    SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
    NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
    //do something with the SID or name (nt.Value)
}

Примечание. При этом получаются только группы безопасности

3 голосов
/ 30 октября 2015

Этот код работает даже быстрее (в два раза быстрее, чем моя предыдущая версия):

    public List<String> GetUserGroups(WindowsIdentity identity)
    {
        List<String> groups = new List<String>();

        String userName = identity.Name;
        int pos = userName.IndexOf(@"\");
        if (pos > 0) userName = userName.Substring(pos + 1);

        PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
        UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov

        DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
        search.PropertiesToLoad.Add("cn");
        search.PropertiesToLoad.Add("samaccountname");
        search.PropertiesToLoad.Add("memberOf");

        SearchResultCollection results = search.FindAll();
        foreach (SearchResult sr in results)
        {
            GetUserGroupsRecursive(groups, sr, de);
        }

        return groups;
    }

    public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
    {
        if (sr == null) return;

        String group = (String)sr.Properties["cn"][0];
        if (String.IsNullOrEmpty(group))
        {
            group = (String)sr.Properties["samaccountname"][0];
        }
        if (!groups.Contains(group))
        {
            groups.Add(group);
        }

        DirectorySearcher search;
        SearchResult sr1;
        String name;
        int equalsIndex, commaIndex;
        foreach (String dn in sr.Properties["memberof"])
        {
            equalsIndex = dn.IndexOf("=", 1);
            if (equalsIndex > 0)
            {
                commaIndex = dn.IndexOf(",", equalsIndex + 1);
                name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);

                search = new DirectorySearcher(de);
                search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
                search.PropertiesToLoad.Add("cn");
                search.PropertiesToLoad.Add("samaccountname");
                search.PropertiesToLoad.Add("memberOf");
                sr1 = search.FindOne();
                GetUserGroupsRecursive(groups, sr1, de);
            }
        }
    }
2 голосов
/ 05 февраля 2019

Вот как я перечисляю все группы (прямые и косвенные) для определенного Отличительного имени:

Строка 1.2.840.113556.1.4.1941 определяет LDAP_MATCHING_RULE_IN_CHAIN.

Это правило ограничено фильтрами, которые применяются к DN.Это специальный «расширенный» оператор сопоставления, который проходит цепочку предков в объектах вплоть до корня, пока не найдет совпадение.

Этот метод в 25 раз быстрее , чем Метод UserPrincipal.GetGroups () в моем тестировании.

Примечание. Основная группа (обычно пользователи домена) не возвращается этим методом или методом GetGroups ().Чтобы получить и имя основной группы, я подтвердил, , этот метод работает.

Кроме того, я нашел этот список фильтров LDAP чрезвычайно полезным.

private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
    var groups = new List<string>();
    if (!string.IsNullOrEmpty(distinguishedName))
    {
        var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName})))";
        using (var dirSearch = CreateDirectorySearcher(domainDirectoryEntry, getGroupsFilterForDn))
        {
            dirSearch.PropertiesToLoad.Add("name");

            using (var results = dirSearch.FindAll())
            {
                foreach (SearchResult result in results)
                {
                    if (result.Properties.Contains("name"))
                        groups.Add((string)result.Properties["name"][0]);
                }
            }
        }
    }

    return groups;
}
2 голосов
/ 16 декабря 2010

Следующий пример взят из статьи проекта кода: (Почти) все в Active Directory через C # :

// userDn is a Distinguished Name such as:
// "LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com"
public ArrayList Groups(string userDn, bool recursive)
{
    ArrayList groupMemberships = new ArrayList();
    return AttributeValuesMultiString("memberOf", userDn,
        groupMemberships, recursive);
}

public ArrayList AttributeValuesMultiString(string attributeName,
     string objectDn, ArrayList valuesCollection, bool recursive)
{
    DirectoryEntry ent = new DirectoryEntry(objectDn);
    PropertyValueCollection ValueCollection = ent.Properties[attributeName];
    IEnumerator en = ValueCollection.GetEnumerator();

    while (en.MoveNext())
    {
        if (en.Current != null)
        {
            if (!valuesCollection.Contains(en.Current.ToString()))
            {
                valuesCollection.Add(en.Current.ToString());
                if (recursive)
                {
                    AttributeValuesMultiString(attributeName, "LDAP://" +
                    en.Current.ToString(), valuesCollection, true);
                }
            }
        }
    }
    ent.Close();
    ent.Dispose();
    return valuesCollection;
}

Просто вызовите метод Groups с Отличительным именем для пользователя и передайте флаг bool, чтобы указать, хотите ли вы включить членство в вложенных / дочерних группах в результирующий ArrayList :

ArrayList groups = Groups("LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com", true);
foreach (string groupName in groups)
{
    Console.WriteLine(groupName);
}

Если вам нужен серьезный уровень программирования на Active Directory в .NET, я настоятельно рекомендую добавить в закладки и просмотреть статью Code Project, о которой я упоминал выше.

1 голос
/ 10 января 2019

Я хотел бы сказать, что в Microsoft LDAP есть несколько специальных способов рекурсивного поиска всех членств пользователя.

  1. Правило соответствия, которое вы можете указать для атрибута "member",В частности, использование правила Microsoft Exclusive LDAP_MATCHING_RULE_IN_CHAIN ​​ для атрибута "member" позволяет осуществлять рекурсивный / вложенный поиск членства.Правило используется, когда вы добавляете его после атрибута участника.Ex.(член: 1.2.840.113556.1.4.1941: = XXXXX)

  2. Для того же домена, что и учетная запись, фильтр может использоватьвместо атрибута Accounts DistinguishedName, который очень удобен для использования междоменного домена при необходимости.ОДНАКО кажется, что вам нужно использовать ForeignSecurityPrincipalтак как он не разрешит ваш SID, так кактег не учитывает тип объекта ForeignSecurityPrincipal.Вы также можете использовать ForeignSecurityPrincipal DistinguishedName.

Используя эти знания, вы можете LDAP запрашивать тех, кто трудно получить членство, таких как группы «Local Local», членом которых является учетная запись.если вы не посмотрите на участников группы, вы не узнаете, был ли пользователь участником.

//Get Direct+Indirect Memberships of User (where SID is XXXXXX)

string str = "(& (objectCategory=group)(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";

//Get Direct+Indirect **Domain Local** Memberships of User (where SID is XXXXXX)

string str2 = "(& (objectCategory=group)(|(groupType=-2147483644)(groupType=4))(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";

//TAA DAA



Не стесняйтесь пробовать эти запросы LDAP после замены SID пользователя.Вы хотите получить все членства в группах.Я полагаю, что это похоже, если не на тот же запрос, который используется PowerShell Command Get-ADPrincipalGroupMembership за кулисами.Команда заявляет: «Если вы хотите искать локальные группы в другом домене, используйте параметр ResourceContextServer, чтобы указать альтернативный сервер в другом домене».

Если вы достаточно знакомы с C # и Active Directory, вам следуетзнать, как выполнить поиск LDAP с использованием предоставленных запросов LDAP.

Дополнительная документация:

1 голос
/ 29 октября 2015

Вот код, который работал для меня:

public ArrayList GetBBGroups(WindowsIdentity identity)
{
    ArrayList groups = new ArrayList();

    try
    {
        String userName = identity.Name;
        int pos = userName.IndexOf(@"\");
        if (pos > 0) userName = userName.Substring(pos + 1);

        PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
        UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName);

        DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
        search.PropertiesToLoad.Add("samaccountname");
        search.PropertiesToLoad.Add("cn");

        String name;
        SearchResultCollection results = search.FindAll();
        foreach (SearchResult result in results)
        {
            name = (String)result.Properties["samaccountname"][0];
            if (String.IsNullOrEmpty(name))
            {
                name = (String)result.Properties["cn"][0];
            }
            GetGroupsRecursive(groups, de, name);
        }
    }
    catch
    {
        // return an empty list...
    }

    return groups;
}

public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn)
{
    DirectorySearcher search = new DirectorySearcher(de);
    search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))";
    search.PropertiesToLoad.Add("memberof");

    String group, name;
    SearchResult result = search.FindOne();
    if (result == null) return;

    group = @"RIOMC\" + dn;
    if (!groups.Contains(group))
    {
        groups.Add(group);
    }
    if (result.Properties["memberof"].Count == 0) return;
    int equalsIndex, commaIndex;
    foreach (String dn1 in result.Properties["memberof"])
    {
        equalsIndex = dn1.IndexOf("=", 1);
        if (equalsIndex > 0)
        {
            commaIndex = dn1.IndexOf(",", equalsIndex + 1);
            name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
            GetGroupsRecursive(groups, de, name);
        }
    }
}

Я измерил его производительность в цикле из 200 запусков с кодом, который использует рекурсивный метод AttributeValuesMultiString; и он работал в 1,3 раза быстрее. Это может быть так из-за наших настроек AD. Оба фрагмента дали один и тот же результат.

0 голосов
/ 21 августа 2015

Если у вас есть соединение LDAP с именем пользователя и паролем для подключения к Active Directory, вот код, который я использовал для правильного подключения:

using System.DirectoryServices.AccountManagement;

// ...

// Connection information
var connectionString = "LDAP://domain.com/DC=domain,DC=com";
var connectionUsername = "your_ad_username";
var connectionPassword = "your_ad_password";

// Get groups for this user
var username = "myusername";

// Split the LDAP Uri
var uri = new Uri(connectionString);
var host = uri.Host;
var container = uri.Segments.Count() >=1 ? uri.Segments[1] : "";

// Create context to connect to AD
var princContext = new PrincipalContext(ContextType.Domain, host, container, connectionUsername, connectionPassword);

// Get User
UserPrincipal user = UserPrincipal.FindByIdentity(princContext, IdentityType.SamAccountName, username);

// Browse user's groups
foreach (GroupPrincipal group in user.GetGroups())
{
    Console.Out.WriteLine(group.Name);
}
0 голосов
/ 15 апреля 2015
PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, "DomainName", UserAccountOU, UserName, Password);
UserPrincipal UserPrincipalID = UserPrincipal.FindByIdentity(pc1, IdentityType.SamAccountName, UserID);

searcher.Filter = "(&(ObjectClass=group)(member = " + UserPrincipalID.DistinguishedName + "));
...