2011-03-15 16 views
85

Utilizo este código para obtener los grupos del usuario actual. Pero quiero dar manualmente al usuario y luego obtener sus grupos. ¿Cómo puedo hacer esto?¿Cómo obtener los grupos de un usuario en Active Directory? (C#, asp.net)

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

Respuesta

135

Si está en .NET 3.5 o arriba, se puede utilizar el nuevo espacio de nombres System.DirectoryServices.AccountManagement (S.DS.AM) que hace esto mucho más fácil de lo solía ser.

leer todos los detalles aquí: Managing Directory Security Principals in the .NET Framework 3.5

Actualización: más viejos artículos de revistas MSDN no están en línea más, por desgracia - que necesita para download the CHM for the January 2008 MSDN magazine de Microsoft y leer el artículo en ese país.

Básicamente, es necesario tener un "contexto principal" (por lo general su dominio), un principal de usuario, y luego obtener sus grupos muy fácilmente:

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

y eso es todo lo que hay! Ahora tiene un resultado (una lista) de los grupos de autorización a los que pertenece el usuario: itere sobre ellos, imprima sus nombres o lo que necesite hacer.

Actualización: Con el fin de acceder a ciertas propiedades, que no se salieron a la superficie en el objeto UserPrincipal, es necesario profundizar en la DirectoryEntry subyacente:

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

Actualización # 2: parece que no debe ser demasiado difícil poner estos dos fragmentos de código juntos .... pero bien - que aquí va:

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

@Tassisto: desafortunadamente, esa propiedad no está disponible directamente en 'UserPrincipal'; vea mi respuesta actualizada para saber cómo llegar a ella. –

+0

Necesito dar el nombre de usuario para obtener el valor de su campo de departamento – SamekaTV

+0

@Tassito: bien, entonces 1) crear un contexto de dominio, 2) encontrar ese usuario por su nombre, y 3) usar mi fragmento de código para obtener su departamento –

9

Dentro del AD cada usuario tiene una propiedad memberOf. Esto contiene una lista de todos los grupos a los que pertenece.

Aquí hay un pequeño ejemplo de código:

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

@Tassisto: Sí, él te entiende. El fragmento de código anterior hará exactamente lo que quiera. Simplemente reemplace el bucle foreach final con un bucle que genere una lista de los nombres de grupo en lugar de la impresión de depuración. –

+2

No se mostrará el grupo principal del usuario (a menudo Usuarios del dominio). Debe volver y consultar esa información por separado. GetAuthorizationGroups no tiene este problema. – Andy

+0

@Andy: De acuerdo, es bueno saberlo. – Oliver

45

GetAuthorizationGroups() hace n ot encuentra grupos anidados. Para conseguir realmente todos los grupos de un usuario dado es miembro de (incluidos los grupos anidados), intente esto:

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

usar try/catch porque tenía algunas excepciones con 2 de cada 200 grupos en un gran AD debido a que algunos SID ya no estaban disponibles (La llamada Translate() hace un SID -> conversión de nombre.)

+2

se mejoraron las actuaciones mediante el uso de esta técnica en lugar de ejecutar AD. – Philippe

1

En mi caso, la única forma en que podía seguir usando GetGroups() sin ningún tipo de expiación era agregar el usuario (USER_WITH_PERMISSION) al grupo que tiene permiso para leer el AD (Directorio Activo). Es extremadamente esencial construir el PrincipalContext pasando este usuario y contraseña.

var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS"); 
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName); 
var groups = user.GetGroups(); 

Pasos que puede seguir dentro de Active Directory para que funcione:

  1. Into Active Directory create a group (or take one) and under secutiry tab add "Windows Authorization Access Group"
  2. Click on "Advanced" button
  3. Select "Windows Authorization Access Group" and click on "View"
  4. Check "Read tokenGroupsGlobalAndUniversal"
  5. Locate the desired user and add to the group you created (taken) from the first step
+1

Esto probablemente entre en juego si usa cuentas integradas para una cuenta de grupo de servicio/aplicación en su aplicación web. Si usa una cuenta de dominio como la cuenta de grupo de servicio/aplicación, o suplanta una cuenta de dominio dentro del código, debería haber leído los derechos por defecto y no tener este problema. – vapcguy

10

En primer lugar, GetAuthorizationGroups() es una gran función, pero por desgracia tiene 2 inconvenientes:

  1. Rendimiento es pobre, especialmente en grandes compañías con muchos usuarios y grupos. Obtiene mucha más información de la que realmente necesita y hace una llamada al servidor para cada iteración de bucle en el resultado
  2. Contiene errores que pueden hacer que su aplicación deje de funcionar "algún día" cuando los grupos y los usuarios evolucionan. Microsoft reconoció el problema y está relacionado con algunos SID. El error que obtendrá es "Ocurrió un error al enumerar los grupos"

Por lo tanto, he escrito una pequeña función para reemplazar GetAuthorizationGroups() con un mejor rendimiento y sin errores. Solo hace 1 llamada LDAP con una consulta usando campos indexados. Se puede extender fácilmente si necesita más propiedades que solo los nombres de grupo (propiedad "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

¡Impresionante! Gracias. Empecé a escribir algunos códigos y estaba usando GetAuthorizationGroups y estaba horrorizado de que tomara 300ms-2.5s para obtener todos los grupos. Su método se realiza en 20-30 ms. – Keith

+2

Esto parecía prometedor, pero no resuelve los grupos anidados, p. un usuario es miembro del grupo a, que a su vez es miembro del grupo x.El código anterior solo mostrará el grupo a, pero no el grupo x. Usé este método a través de tokenGroups: http://stackoverflow.com/a/4460658/602449 –

Cuestiones relacionadas