114 votes

Comment obtenir les groupes d'un utilisateur dans Active Directory ? (c#, asp.net)

J'utilise ce code pour obtenir les groupes de l'utilisateur actuel. Mais je veux donner manuellement l'utilisateur et ensuite obtenir ses groupes. Comment puis-je le faire ?

using System.Security.Principal;

public ArrayList Groups()
{
    ArrayList groups = new ArrayList();

    foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
    {
        groups.Add(group.Translate(typeof(NTAccount)).ToString());
    }

    return groups;
}

172voto

marc_s Points 321990

Si vous utilisez .NET 3.5 ou une version ultérieure, vous pouvez utiliser la nouvelle fonction System.DirectoryServices.AccountManagement (S.DS.AM), ce qui rend les choses beaucoup plus faciles qu'auparavant.

Lisez tout à ce sujet ici : Gérer les principes de sécurité de l'annuaire dans le .NET Framework 3.5

Mise à jour : Les anciens articles du magazine MSDN ne sont malheureusement plus en ligne. télécharger le CHM du magazine MSDN de janvier 2008 de Microsoft et lisez l'article qui s'y trouve.

Fondamentalement, vous devez avoir un "contexte principal" (typiquement votre domaine), un principal utilisateur, et ensuite vous obtenez ses groupes très facilement :

public List<GroupPrincipal> GetGroups(string userName)
{
   List<GroupPrincipal> result = new List<GroupPrincipal>();

   // establish domain context
   PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

   // find your user
   UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);

   // if found - grab its groups
   if(user != null)
   {
      PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();

      // iterate over all groups
      foreach(Principal p in groups)
      {
         // make sure to add only group principals
         if(p is GroupPrincipal)
         {
             result.Add((GroupPrincipal)p);
         }
      }
   }

   return result;
}

et c'est tout ce qu'il y a ! Vous avez maintenant un résultat (une liste) des groupes d'autorisation auxquels l'utilisateur appartient - itérez sur eux, imprimez leurs noms ou tout ce dont vous avez besoin.

Mise à jour : Afin d'accéder à certaines propriétés, qui ne sont pas en surface sur le site de l UserPrincipal vous devez creuser dans l'objet sous-jacent DirectoryEntry :

public string GetDepartment(Principal principal)
{
    string result = string.Empty;

    DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);

    if (de != null)
    {
       if (de.Properties.Contains("department"))
       {
          result = de.Properties["department"][0].ToString();
       }
    }

    return result;
}

Mise à jour n°2 : Il ne devrait pas être trop difficile d'assembler ces deux bouts de code.... mais ok - c'est parti :

public string GetDepartment(string username)
{
    string result = string.Empty;

    // if you do repeated domain access, you might want to do this *once* outside this method, 
    // and pass it in as a second parameter!
    PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

    // find the user
    UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);

    // if user is found
    if(user != null)
    {
       // get DirectoryEntry underlying it
       DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);

       if (de != null)
       {
          if (de.Properties.Contains("department"))
          {
             result = de.Properties["department"][0].ToString();
          }
       }
    }

    return result;
}

0 votes

@Tassisto : malheureusement, cette propriété n'est pas disponible directement sur l'interface utilisateur. UserPrincipal - voir ma réponse mise à jour pour savoir comment l'obtenir.

0 votes

J'ai besoin de donner le nom d'utilisateur pour obtenir la valeur de son champ département.

0 votes

@Tassito : alors 1) créez un contexte de domaine, 2) trouvez cet utilisateur par son nom, et 3) utilisez mon extrait de code pour obtenir son département.

60voto

Mickey Mouse Points 81

GetAuthorizationGroups() ne trouve pas les groupes imbriqués. Pour obtenir réellement tous les groupes dont un utilisateur donné est membre (y compris les groupes imbriqués), essayez ceci :

using System.Security.Principal

private List<string> GetGroups(string userName)
{
    List<string> result = new List<string>();
    WindowsIdentity wi = new WindowsIdentity(userName);

    foreach (IdentityReference group in wi.Groups)
    {
        try
        {
            result.Add(group.Translate(typeof(NTAccount)).ToString());
        }
        catch (Exception ex) { }
    }
    result.Sort();
    return result;
}

J'utilise try/catch parce que j'ai eu quelques exceptions avec 2 groupes sur 200 dans un très grand AD parce que certains SIDs n'étaient plus disponibles. (Le Translate() effectue une conversion SID -> Nom).

3 votes

Les performances ont été améliorées en utilisant cette technique au lieu de passer par AD. merci !

0 votes

GetAuthorisationGroups() est super lent pour moi : 26 et tous les autres codes que j'ai trouvés jusqu'à présent n'incluaient pas d'identifiants bien connus comme Everyone, Domain Users, etc... Le code que vous avez fourni est littéralement instantané et inclut tous les sids, oui seulement les sids mais c'est ce dont j'ai besoin, y compris les identifiants connus et personnalisés !

20voto

Bigjim Points 406

Tout d'abord, GetAuthorizationGroups() est une excellente fonction mais elle présente malheureusement deux inconvénients :

  1. Les performances sont médiocres, surtout dans les grandes entreprises avec de nombreux utilisateurs et groupes. Il récupère beaucoup plus de données que ce dont vous avez réellement besoin et fait un appel au serveur pour chaque itération de boucle dans le résultat.
  2. Il contient des bogues qui peuvent faire en sorte que votre application cesse de fonctionner "un jour" lorsque les groupes et les utilisateurs évoluent. Microsoft a reconnu le problème et il est lié à certains SID. L'erreur que vous obtiendrez est "Une erreur s'est produite lors de l'énumération des groupes".

Par conséquent, j'ai écrit une petite fonction pour remplacer GetAuthorizationGroups() avec de meilleures performances et sans risque d'erreur. Elle ne fait qu'un seul appel LDAP avec une requête utilisant des champs indexés. Elle peut être facilement étendue si vous avez besoin de plus de propriétés que les seuls noms de groupes (propriété "cn").

// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain")
public static List<string> GetAdGroupsForUser2(string userName, string domainName = null)
{
    var result = new List<string>();

    if (userName.Contains('\\') || userName.Contains('/'))
    {
        domainName = userName.Split(new char[] { '\\', '/' })[0];
        userName = userName.Split(new char[] { '\\', '/' })[1];
    }

    using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName))
        using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName))
            using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name)))
            {
                searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName);
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PropertiesToLoad.Add("cn");

                foreach (SearchResult entry in searcher.FindAll())
                    if (entry.Properties.Contains("cn"))
                        result.Add(entry.Properties["cn"][0].ToString());
            }

    return result;
}

0 votes

Génial ! Merci. J'ai commencé à écrire du code et j'utilisais GetAuthorizationGroups et j'étais horrifié de voir que cela prenait 300ms-2.5s pour obtenir tous les groupes. Votre méthode se fait en 20-30 ms.

4 votes

Cela semblait prometteur, mais cela ne résout pas les groupes imbriqués, par exemple un utilisateur est membre du groupe a, qui est lui-même membre du groupe x. Le code ci-dessus ne montrera que le groupe a, mais pas le groupe x. J'ai utilisé cette méthode via tokenGroups : stackoverflow.com/a/4460658/602449

0 votes

Jetez un coup d'œil au commentaire de Robert Muehsig - cette méthode permet de faire des groupes imbriqués et est encore plus rapide. Le seul inconvénient est qu'elle ne renvoie que les groupes de sécurité et non les groupes de distribution.

11voto

Oliver Points 19006

Dans l'AD, chaque utilisateur a une propriété memberOf . Celui-ci contient la liste de tous les groupes auxquels il appartient.

Voici un petit exemple de code :

// (replace "part_of_user_name" with some partial user name existing in your AD)
var userNameContains = "part_of_user_name";

var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();

var allSearcher = allDomains.Select(domain =>
{
    var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));

    // Apply some filter to focus on only some specfic objects
    searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains);
    return searcher;
});

var directoryEntriesFound = allSearcher
    .SelectMany(searcher => searcher.FindAll()
        .Cast<SearchResult>()
        .Select(result => result.GetDirectoryEntry()));

var memberOf = directoryEntriesFound.Select(entry =>
{
    using (entry)
    {
        return new
        {
            Name = entry.Name,
            GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString())
        };
    }
});

foreach (var item in memberOf)
{
    Debug.Print("Name = " + item.Name);
    Debug.Print("Member of:");

    foreach (var groupName in item.GroupName)
    {
        Debug.Print("   " + groupName);
    }

    Debug.Print(String.Empty);
}
}

1 votes

@Tassisto : Oui, il vous comprend. L'extrait de code ci-dessus fera exactement ce que vous voulez. Remplacez simplement la boucle foreach finale par une boucle qui génère une liste des noms de groupe au lieu de l'impression de débogage.

2 votes

Il ne parviendra pas à répertorier le groupe primaire de l'utilisateur (souvent les utilisateurs du domaine). Vous devez revenir en arrière et demander cette information séparément. GetAuthorizationGroups ne présente pas ce problème.

2voto

Darcu Points 31

Ma solution :

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, myDomain), IdentityType.SamAccountName, myUser);
List<string> UserADGroups = new List<string>();            
foreach (GroupPrincipal group in user.GetGroups())
{
    UserADGroups.Add(group.ToString());
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X