web-dev-qa-db-fra.com

Comment obtenir tous les groupes AD pour un utilisateur particulier?

J'ai vérifié cela poste déjà. Mais cela ne répond pas à ma question. Je souhaite obtenir tous les groupes Active Directory dans lesquels un utilisateur particulier est membre.

J'ai écrit le code suivant. Mais je ne peux pas continuer plus longtemps car je ne sais pas comment donner le filtre et comment accéder aux propriétés.

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());
        }
    }
}

Des idées?

33
NLV

Il suffit d'interroger la propriété "memberOf" et d'itérer le retour, exemple:

            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();

Cela ne fait que fourrer les noms de groupe dans la chaîne groupNames, délimitée par un tuyau, mais lorsque vous effectuez une rotation complète, vous pouvez faire ce que vous voulez

25
curtisk

Vous devez utiliser System.DirectoryServices.AccountManagement . C'est beaucoup plus facile. Voici un projet de code Nice article vous donnant un aperçu de toutes les classes de cette DLL.

Comme vous l'avez souligné, votre approche actuelle ne permet pas d'identifier le groupe principal. En fait, c'est bien pire que vous ne le pensiez. Dans certains cas, cela ne fonctionne pas, comme le groupe de domaine local d'un autre domaine. Vous pouvez vérifier ici pour plus de détails. Voici à quoi ressemble le code si vous passez à System.DirectoryServices.AccountManagement. Le code suivant permet de rechercher les groupes immédiats attribués à cet utilisateur, notamment le groupe principal.

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
    Console.Out.WriteLine(group);
}
35
Harvey Kwok

Utilisez 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)
}

Note: cela n'obtient que des groupes de sécurité

23
Shurdoof

L'exemple suivant provient de l'article Code Project, (Presque) tout dans Active Directory via 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;
}

Appelez simplement la méthode Groups avec le nom distinctif pour l'utilisateur et transmettez l'indicateur bool pour indiquer si vous souhaitez inclure des appartenances à des groupes imbriqués/enfants dans votre ArrayList résultant:

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

Si vous avez besoin de faire un niveau sérieux de programmation Active Directory dans .NET, je vous recommande vivement de mettre en signet et de réviser l'article Code Project que j'ai mentionné ci-dessus.

2
Saul Dolgin

Voici comment je liste tous les groupes (directs et indirects) pour un nom distinctif spécifique:

La chaîne 1.2.840.113556.1.4.1941 spécifie LDAP_MATCHING_RULE_IN_CHAIN. 

Cette règle est limitée aux filtres qui s'appliquent au nom distinctif. Il s'agit d'un opérateur de correspondance "étendu" spécial qui parcourt la chaîne d'ascendance dans les objets jusqu'à la racine jusqu'à ce qu'il trouve une correspondance.

Cette méthode est 25 fois plus rapide que la méthode UserPrincipal.GetGroups () dans mes tests.

Remarque: le groupe principal (généralement les utilisateurs du domaine) n'est pas renvoyé par cette méthode ou par la méthode GetGroups (). Pour obtenir le nom du groupe principal aussi, j'ai confirmé que cette méthode fonctionne. 

De plus, j'ai trouvé ce liste de LDAP filtres extrêmement utile.

private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
    var groups = new List<string>();
    if (!string.IsNullOrEmpty(distinguishedName))
    {
        var getGroupsFilterForDn = $"(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
noctural

Ce code fonctionne encore plus vite (deux fois plus rapide que ma version précédente):

    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
Nuren Geodakov

Voici le code qui a fonctionné pour moi:

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);
        }
    }
}

J'ai mesuré ses performances dans une boucle de 200 exécutions par rapport au code qui utilise la méthode AttributeValuesMultiString récursive; et cela a fonctionné 1,3 fois plus vite ... Il pourrait en être ainsi à cause de nos paramètres AD. Les deux extraits ont cependant donné le même résultat.

1
Nuren Geodakov

Je voudrais dire que Microsoft LDAP dispose de méthodes spéciales pour rechercher de manière récursive toutes les appartenances d’un utilisateur.

  1. La règle de correspondance que vous pouvez spécifier pour l'attribut "membre". En particulier, l'utilisation de l'attribut Microsoft Exclusive LDAP_MATCHING_RULE_IN_CHAIN ​​ / pour l'attribut "membre" permet une recherche d'appartenance récursive/imbriquée. La règle est utilisée lorsque vous l'ajoutez après l'attribut member. Ex. (membre: 1.2.840.113556.1.4.1941: = XXXXX)

  2. Pour le même domaine que le compte, le filtre peut utiliser <SID = S-1-5-21-XXXXXXXXXXXXXXXXXXXXXXX> au lieu d'un attribut Accounts DistinguishedName qui est très pratique pour utiliser interdomaine si nécessaire. TOUTEFOIS, il semble que vous deviez utiliser le <GUID = YYYY> de ForeignSecurityPrincipal car il ne résoudra pas votre SID car il apparaît que la balise <SID => ne considère pas le type d'objet ForeignSecurityPrincipal. Vous pouvez également utiliser ForeignSecurityPrincipal DistinguishedName.

Grâce à ces connaissances, vous pouvez interroger les utilisateurs difficiles à obtenir avec LDAP, tels que les groupes "Domaine local" dont un compte est membre, mais vous ne sauriez pas si l'utilisateur est membre de ce groupe.

//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



N'hésitez pas à essayer ces requêtes LDAP après avoir substitué le SID d'un utilisateur pour lequel vous souhaitez extraire toutes les appartenances à un groupe. Je suppose que cette requête est similaire, sinon identique, à ce que/ la commande PowerShell Get-ADPrincipalGroupMembership utilise en arrière-plan. La commande indique "Si vous souhaitez rechercher des groupes locaux dans un autre domaine, utilisez le paramètre ResourceContextServer pour spécifier le serveur de remplacement dans l'autre domaine".

Si vous connaissez suffisamment C # et Active Directory, vous devez savoir comment effectuer une recherche LDAP à l'aide des requêtes LDAP fournies.

Documentation supplémentaire: 

1
C Sharp Conner

Si vous disposez d'une connexion LDAP avec un nom d'utilisateur et un mot de passe pour vous connecter à Active Directory, voici le code que j'ai utilisé pour vous connecter correctement:

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
Maxime