2011-03-15 21 views
20

Tengo una aplicación que utiliza la autorización ActiveDirecotry y se ha decidido que necesita admitir grupos anidados, por ejemplo:Cómo determinar todos los grupos a los que pertenece un usuario (incluidos los grupos anidados) en ActiveDirectory y .NET 3.5

MAIN_AD_GROUP 
    | 
    |-> SUB_GROUP 
       | 
       |-> User 

Por lo tanto, el usuario en no directamente un miembro de MAIN_AD_GROUP. Me gustaría poder buscar al usuario recursivamente, buscando en los grupos anidados en MAIN_AD_GROUP.

El principal problema es que estoy usando .NET 3.5 y hay un error en System.DirectoryServices.AccountManagement en .NET 3.5 mediante el cual el método UserPrincipal.IsMemberOf()no funcionará para grupos con más de 1500 usuarios. Entonces no puedo usar UserPrincipal.IsMemberOf() y no, tampoco puedo cambiar a .NET 4.

He trabajado en torno a este último problema con la siguiente función:

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

Pero userPrincipal.GetGroups() sólo devuelve el grupo del que el usuario es miembro directo.

¿Cómo puedo hacer que esto funcione con grupos anidados?

Respuesta

31

Solución nº 1

Se informa sobre el fallo here at Microsoft Connect junto con el siguiente código que funciona en torno a este tema mediante la iteración de forma manual a través de las PrincipalSearchResult<Principal> objetos devueltos, la captura de esta excepción, y continuando en:

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

Solución # 2

Otra solución found here evita la clase , y utiliza el System.DirectoryServices API en lugar:

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

     } 
    } 
} 
+0

Elegí implementar la Solución # 2. YMMV con la Solución # 1. –

+0

Buena publicación. La solución n.º 1 falla en iterGroup.MoveNext() con el mismo error "No hay tal objeto en el servidor". – Kjensen

+0

La solución 2 solo puede funcionar si el programa se ejecuta desde una computadora que ha iniciado sesión en el dominio. No puede funcionar si está consultando el ldap desde un dominio diferente. –

13

Uso UserPrincipal.GetAuthorizationGroups() lugar - de su MSDN docs:

Este método busca todos los grupos recursiva y devuelve los grupos en la que el usuario es miembro. El conjunto devuelto también puede incluir grupos adicionales que el sistema podría considerar como miembro del usuario para propósitos de autorización .

Los grupos devueltos por este método pueden incluir grupos de un diferente alcance y tienda que el principal. Por ejemplo, si el principal es un objeto DS AD que tiene un DN de "CN = SpecialGroups, DC = Fabrikam, DC = com, el conjunto devuelto pueden contener grupos que pertenecen a los " CN = NormalGroups, DC = Fabrikam, DC = com.

+1

Gracias, pero desafortunadamente esto arroja una excepción 'PrincipalOperationException' con el mensaje" No hay tal objeto en el servidor. " El UserPrincipal definitivamente existe, ya que mi método anterior * does * devuelve las autorizaciones apropiadas para grupos de nivel superior. –

+2

'UserPrincipal.GetAuthorizationGroups()' se bloquea si hay grupos eliminados de los que el usuario aún es miembro de – jproch

+0

Simplemente compruebe si group.Name no es nulo en ese caso @jproch –

1

La forma eficiente es hacer una sola consulta AD por tener el filtro DirectorySearcher adecuado para, por ejemplo,

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

Debe reemplazar YOURDOMAIN y GROUPNAME con los valores correctos de su AD.

Fuente: How to Recursively Get the Group Membership of a User in Active Directory using .NET/C# and LDAP (without just 2 hits to Active Directory)

Necesidad de incluir, using System.DirectoryServices;

6

Sé que esto es un hilo viejo, pero es el primer resultado en Google, por lo que en caso de que esto ayuda a nadie, esto es lo que se me ocurrió que utiliza las cosas de gestión de cuentas, pero hace que esta consulta particular sea mucho más fácil.

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