2010-11-22 178 views
20

Sé que este tipo de pregunta se ha hecho antes, pero otros métodos me están fallando en este momento.C# Active Directory: ¿Obtener el nombre de dominio del usuario?

Tal como está, nuestro servicio de Windows sondea AD, dado un LDAP (es decir, LDAP: //10.32.16.80) y una lista de grupos de usuarios dentro de ese servidor de AD para buscar. Recupera todos los usuarios dentro de esos grupos dados, buscando recursivamente esos grupos para más grupos también. Cada usuario se agrega a la lista de usuarios autenticados de otras aplicaciones.

Esta parte de la aplicación se está ejecutando correctamente. Sin embargo, necesitamos el nombre de dominio amigable de cada usuario (es decir, la parte de su nombre de usuario/DOMINIO)

Entonces, si hay un usuario que es parte del dominio TEST, llamado Steve: TEST/steve es su inicio de sesión . Puedo encontrar steve en el AD, sin embargo, también necesito que se almacene "TEST" junto con su información AD.

De nuevo, puedo encontrar 'steve' bien utilizando un buscador de directorio y la dirección IP de LDAP que me dieron, pero dado el IP de LDAP, ¿cómo puedo encontrar el nombre de dominio amigable?

Cuando intento el siguiente código que estoy señaliza un error al intentar acceder a la 'defaultNamingContext':

System.Runtime.InteropServices.COMException (0x8007202A): El mecanismo de autenticación es desconocido.

Aquí está el código:

private string SetCurrentDomain(string server) 
    { 
     string result = string.Empty; 
     try 
     { 
      logger.Debug("'SetCurrentDomain'; Instantiating rootDSE LDAP"); 
      DirectoryEntry ldapRoot = new DirectoryEntry(server + "/rootDSE", username, password); 
      logger.Debug("'SetCurrentDomain'; Successfully instantiated rootDSE LDAP"); 

      logger.Debug("Attempting to retrieve 'defaultNamingContext'..."); 
      string domain = (string)ldapRoot.Properties["defaultNamingContext"][0]; //THIS IS WHERE I HIT THE COMEXCEPTION 
      logger.Debug("Retrieved 'defaultNamingContext': " + domain); 
      if (!domain.IsEmpty()) 
      { 

       logger.Debug("'SetCurrentDomain'; Instantiating partitions/configuration LDAP entry"); 
       DirectoryEntry parts = new DirectoryEntry(server + "/CN=Partitions,CN=Configuration," + domain, username, password); 

       logger.Debug("'SetCurrentDomain'; Successfully instantiated partitions/configuration LDAP entry"); 
       foreach (DirectoryEntry part in parts.Children) 
       { 
        if (part.Properties["nCName"] != null && (string)part.Properties["nCName"][0] != null) 
        { 
         logger.Debug("'SetCurrentDomain'; Found property nCName"); 
         if ((string)part.Properties["nCName"][0] == domain) 
         { 
          logger.Debug("'SetCurrentDomain'; nCName matched defaultnamingcontext"); 
          result = (string)part.Properties["NetBIOSName"][0]; 
          logger.Debug("'SetCurrentDomain'; Found NetBIOSName (friendly domain name): " + result); 
          break; 
         } 
        } 
       } 
      } 
      logger.Debug("finished setting current domain..."); 
     } 
     catch (Exception ex) 
     { 
      logger.Error("error attempting to set domain:" + ex.ToString()); 
     } 
     return result; 
    } 

edición

añadí este método de muestra con el fin de intentar una sugerencia, pero estoy recibiendo una excepción: "Error no especificado" cuando pulso el "FindAll() "llamar al buscador. La cadena que se pasa in: "USER CN = TEST, CN = Users, DC = tempe, DC = ktregression, DC = com"

 private string GetUserDomain(string dn) 
    { 
     string domain = string.Empty; 
     string firstPart = dn.Substring(dn.IndexOf("DC=")); 
     string secondPart = "CN=Partitions,CN=Configuration," + firstPart; 
     DirectoryEntry root = new DirectoryEntry(secondPart, textBox2.Text, textBox3.Text); 
     DirectorySearcher searcher = new DirectorySearcher(root); 
     searcher.SearchScope = SearchScope.Subtree; 
     searcher.ReferralChasing = ReferralChasingOption.All; 
     searcher.Filter = "(&(nCName=" + firstPart + ")(nETBIOSName=*))"; 
     try 
     { 
      SearchResultCollection rs = searcher.FindAll(); 
      if (rs != null) 
      { 
       domain = GetProperty(rs[0], "nETBIOSName"); 
      } 
     } 
     catch (Exception ex) 
     { 

     } 


     return domain; 
+0

¿El dominio 'TEST' está bajo el mismo bosque que el dominio actual? Si es así, puede consultar el dominio correcto para este usuario, ya que el usuario puede existir en su dominio actual, pero no en el otro. –

+0

Sí, el dominio TEST estará en el mismo bosque que el dominio actual. En ese sentido, ¿cómo hago para consultar el dominio de un usuario determinado? Tenga en cuenta que mi conocimiento de AD es limitado, en el sentido de que no estoy muy familiarizado con la creación de cadenas de LDAP y ... –

+0

posible duplicación de [Obtener dominio de estilo NT \ usuario dado DN] (http://stackoverflow.com/ preguntas/1796426/get-nt-style-domain-user-given-dn) - esto proporciona información sobre cómo convertir el DN de un usuario a su nombre de dominio NETBIOS, que es lo que quiere aquí. –

Respuesta

25

En este artículo me ayudó mucho a entender cómo trabajar con el Active Directorio.
Howto: (Almost) Everything In Active Directory via C#

Desde este punto en adelante, si necesita más assitance, por favor hágamelo saber con preguntas adecuadas en comentario, y he de responder por usted a lo mejor de mi conocimiento.

editar # 1

Es mejor que ir con filtro de este ejemplo en su lugar. He escrito un código de muestra para mostrar brevemente cómo trabajar con los espacios de nombres System.DirectoryServices y System.DirectoryServices.ActiveDirectory. El espacio de nombres System.DirectoryServices.ActiveDirectory se usa para recuperar información sobre los dominios dentro de su bosque.

private IEnumerable<DirectoryEntry> GetDomains() { 
    ICollection<string> domains = new List<string>(); 

    // Querying the current Forest for the domains within. 
    foreach(Domain d in Forest.GetCurrentForest().Domains) 
     domains.Add(d.Name); 

    return domains; 
} 

private string GetDomainFullName(string friendlyName) { 
    DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, friendlyName); 
    Domain domain = Domain.GetDomain(context); 
    return domain.Name; 
} 

private IEnumerable<string> GetUserDomain(string userName) { 
    foreach(string d in GetDomains()) 
     // From the domains obtained from the Forest, we search the domain subtree for the given userName. 
     using (DirectoryEntry domain = new DirectoryEntry(GetDomainFullName(d))) { 
      using (DirectorySearcher searcher = new DirectorySearcher()){ 
       searcher.SearchRoot = domain; 
       searcher.SearchScope = SearchScope.Subtree; 
       searcher.PropertiesToLoad.Add("sAMAccountName"); 
       // The Filter is very important, so is its query string. The 'objectClass' parameter is mandatory. 
       // Once we specified the 'objectClass', we want to look for the user whose login 
       // login is userName. 
       searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", userName); 

       try { 
        SearchResultCollection results = searcher.FindAll(); 

        // If the user cannot be found, then let's check next domain. 
        if (results == null || results.Count = 0) 
         continue; 

        // Here, we yield return for we want all of the domain which this userName is authenticated. 
        yield return domain.Path; 
       } finally { 
        searcher.Dispose(); 
        domain.Dispose(); 
       } 
      } 
} 

Aquí, no probé este código y es posible que tenga un problema menor que corregir. Esta muestra se proporciona tal como es para ayudarlo. Espero que esto sea de ayuda.

editar # 2

descubrí otra salida:

  1. Tienes primero en examinar si se puede encontrar la cuenta de usuario dentro de su dominio;
  2. Si lo encuentra, obtenga el nombre NetBIOS del dominio; y
  3. concatenarlo a una barra diagonal inversa (****) y al inicio de sesión encontrado.

El siguiente ejemplo utiliza un NUnit TestCase que se puede probar por sí mismo y ver si hace lo que se le exige a.

[TestCase("LDAP://fully.qualified.domain.name", "TestUser1")] 
public void GetNetBiosName(string ldapUrl, string login) 
    string netBiosName = null; 
    string foundLogin = null; 

    using (DirectoryEntry root = new DirectoryEntry(ldapUrl)) 
     Using (DirectorySearcher searcher = new DirectorySearcher(root) { 
      searcher.SearchScope = SearchScope.Subtree; 
      searcher.PropertiesToLoad.Add("sAMAccountName"); 
      searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", login); 

      SearchResult result = null; 

      try { 
       result = searcher.FindOne(); 

       if (result == null) 
        if (string.Equals(login, result.GetDirectoryEntry().Properties("sAMAccountName").Value)) 
         foundLogin = result.GetDirectoryEntry().Properties("sAMAccountName").Value 
      } finally { 
       searcher.Dispose(); 
       root.Dispose(); 
       if (result != null) result = null; 
      } 
     } 

    if (!string.IsNullOrEmpty(foundLogin)) 
     using (DirectoryEntry root = new DirectoryEntry(ldapUrl.Insert(7, "CN=Partitions,CN=Configuration,DC=").Replace(".", ",DC=")) 
      Using DirectorySearcher searcher = new DirectorySearcher(root) 
       searcher.Filter = "nETBIOSName=*"; 
       searcher.PropertiesToLoad.Add("cn"); 

       SearchResultCollection results = null; 

       try { 
        results = searcher.FindAll(); 

        if (results != null && results.Count > 0 && results[0] != null) { 
         ResultPropertyValueCollection values = results[0].Properties("cn"); 
         netBiosName = rpvc[0].ToString(); 
       } finally { 
        searcher.Dispose(); 
        root.Dispose(); 

        if (results != null) { 
         results.Dispose(); 
         results = null; 
        } 
       } 
      } 

    Assert.AreEqual("FULLY\TESTUSER1", string.Concat(netBiosName, "\", foundLogin).ToUpperInvariant()) 
} 

La fuente de la que me inspiré es:
Find the NetBios Name of a domain in AD

+0

Will, he usado ese sitio muchas veces, pero no parece dar detalles sobre la cuestión en cuestión. Gracias sin embargo. –

+0

@Micheal Velasquez: Por favor, vea mi edición para obtener una muestra del código que, espero, lo ayudará. –

+0

He transferido su muestra de código a mi aplicación de prueba C# y con algunos ajustes (es decir, el tipo de devolución del método GetDomains(), etc.) Estoy teniendo un problema con el rendimiento del método GetUserDomain(). El objeto "dominio" del objeto DirectoryEntry no tiene una propiedad "Dominio" (es decir dominio.dominio) –

3

puede recuperar el nombre del dominio que el usuario actual es el uso de la Environment.UserDomainName Property.

string domainName; 
domainName = System.Environment.UserDomainName; 
+2

¿Qué sucede si deseo obtener el nombre de dominio de un usuario específico? – SearchForKnowledge

4

Como no pude encontrar ningún código de ejemplo, me gustaría compartir mi propia solución. Esto buscará los padres del objeto DirectoryEntry hasta que llegue a la clase domainDNS.

using System.DirectoryServices; 

public static class Methods 
{ 
    public static T ldap_get_value<T>(PropertyValueCollection property) 
    { 
     object value = null; 
     foreach (object tmpValue in property) value = tmpValue; 
     return (T)value; 
    } 

    public static string ldap_get_domainname(DirectoryEntry entry) 
    { 
     if (entry == null || entry.Parent == null) return null; 
     using (DirectoryEntry parent = entry.Parent) 
     { 
      if (ldap_get_value<string>(parent.Properties["objectClass"]) == "domainDNS") 
       return ldap_get_value<string>(parent.Properties["dc"]); 
      else 
       return ldap_get_domainname(parent); 
     } 
    } 
} 

utilizar de esta manera:

string[] _properties = new string[] { "objectClass", "distinguishedName", "samAccountName", "userPrincipalName", "displayName", "mail", "title", "company", "thumbnailPhoto", "useraccountcontrol" }; 
string account = "my-user-name"; 
// OR even better: 
// string account = "[email protected]"; 

using (DirectoryEntry ldap = new DirectoryEntry()) 
{ 
    using (DirectorySearcher searcher = new DirectorySearcher(ldap)) 
    { 
     searcher.PropertiesToLoad.AddRange(_properties); 
     if (account.Contains('@')) searcher.Filter = "(userPrincipalName=" + account + ")"; 
     else searcher.Filter = "(samAccountName=" + account + ")"; 
     var user = searcher.FindOne().GetDirectoryEntry(); 

     Console.WriteLine("Name: " + Methods.ldap_get_value<string>(user.Properties["displayName"])); 
     Console.WriteLine("Domain: " + Methods.ldap_get_domainname(user)); 
     Console.WriteLine("Login: " + Methods.ldap_get_domainname(user) + "\\" + Methods.ldap_get_value<string>(user.Properties["samAccountName"])); 
    } 
} 

no tengo un bosque para probarlo en pero en teoría esto debería cortarlo.

+0

Funciona bien pero hay una implementación más elegante para 'ldap_get_value':' return entry.Properties [propertyName] .OfType () .LastOrDefault(); ' –

+0

Gracias Tiele, su código funciona para mí con pequeños cambios, pero parece mucho más rápido que el otro código que encontré en línea. –

+0

Recibo la excepción "Nombre de usuario o contraseña incorrecta" en var 'user = searcher.FindOne(). GetDirectoryEntry();' line. Si es un error de autenticación? – Shesha

1

Tal vez no del todo correcta, pero ...

DirectoryEntry dirEntry = new DirectoryEntry();   
DirectorySearcher dirSearcher = new DirectorySearcher(dirEntry); 
dirSearcher.SearchScope = SearchScope.Subtree; 
dirSearcher.Filter = string.Format("(&(objectClass=user)(|(cn={0})(sn={0}*)(givenName={0})(sAMAccountName={0}*)))", userName); 
var searchResults = dirSearcher.FindAll(); 

foreach (SearchResult sr in searchResults) 
{ 
    var de = sr.GetDirectoryEntry(); 
    string user = de.Properties["SAMAccountName"][0].ToString();    
    string domain = de.Path.ToString().Split(new [] { ",DC=" },StringSplitOptions.None)[1]; 
    MessageBox.Show(domain + "/" + user); 
} 

Debido a que el valor de de.Path es

LDAP: // CN = NombreCompleto, DC = dominio , DC = local

+0

funciona para mí con menos saltos de red. Probablemente no funcionará para todos porque esos no tienen que ser de 1 a 1, no creo –

Cuestiones relacionadas