2010-11-22 1 views
20

Ich weiß, dass diese Art von Frage schon einmal gestellt wurde, aber andere Methoden scheitern mir gerade.C# Active Directory: Abrufen des Domänennamens des Benutzers?

So wie es aussieht, fragt unser Windows-Dienst AD nach einem LDAP (d. H. LDAP: //10.32.16.80) und einer Liste von Benutzergruppen auf diesem AD-Server, nach denen gesucht werden soll. Es ruft alle Benutzer innerhalb dieser Gruppen ab und durchsucht diese Gruppen rekursiv nach weiteren Gruppen. Jeder Benutzer wird dann einer anderen Liste authentifizierter Benutzer hinzugefügt.

Dieser Teil der Anwendung wird erfolgreich ausgeführt. Wir benötigen jedoch den benutzerfreundlichen Domainnamen jedes Benutzers (dh den Teil seiner Login-Domain/Benutzername)

Wenn also ein Benutzer Teil der TEST-Domäne ist, heißt Steve: TEST/steve ist sein Login . Ich kann Steve in der AD finden, aber ich brauche auch "TEST" zusammen mit seinen AD-Informationen gespeichert werden.

Wieder kann ich 'steve' in Ordnung finden, indem ich einen Verzeichnissucher und die LDAP-IP benutze, die ich bekomme, aber wie kann ich den freundlichen Domain-Namen finden?

Wenn ich den folgenden Code versuche ich einen Fehler gegeben habe, wenn die ‚defaultNamingContext‘ zuzugreifen versuchen:

System.Runtime.InteropServices.COMException (0x8007202A): Der Authentifizierungsmechanismus ist nicht bekannt. Hier

ist der Code:

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

bearbeiten

ich diese Stichprobe Methode, um einen Vorschlag hinzugefügt, um zu versuchen, aber ich erhalte eine Ausnahme: „Unbekannter Fehler“, wenn ich auf den „FindAll() "Rufen Sie den Sucher an. Der String übergeben wird, ist: "CN = TEST USER, 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

Ist die Domäne 'TEST' unter derselben Gesamtstruktur wie die aktuelle Domäne? Wenn dies der Fall ist, können Sie die richtige Domäne für diesen Benutzer abfragen, da der Benutzer möglicherweise in Ihrer aktuellen Domäne vorhanden ist, aber nicht in der anderen Domäne. –

+0

Ja, die Domäne TEST befindet sich in derselben Gesamtstruktur wie die aktuelle Domäne. Wie gehe ich in diesem Zusammenhang vor, um die Domain für einen bestimmten Benutzer abzufragen? Denken Sie daran, mein AD-Wissen ist begrenzt, in dem ich bin nicht versiert auf den Aufbau von LDAP-Strings und so ... –

+0

mögliche Duplikate von [Get NT-Stil Domain \ Benutzer angegebene DN] (http://stackoverflow.com/ questions/1796426/get-nt-style-domain-user-given-dn) - Hier finden Sie Informationen darüber, wie Sie den DN eines Benutzers in seinen NETBIOS-Domänennamen konvertieren, was Sie hier wünschen. –

Antwort

25

Dieser Artikel hat mir geholfen, viel zu verstehen, wie mit dem Active arbeiten Verzeichnis.
Howto: (Almost) Everything In Active Directory via C#

Von diesem Punkt an, wenn Sie weitere assitance benötigen, lassen Sie mich mit dem richtigen Fragen in Kommentar bitte wissen, und ich werde sie die beste Wissen und Gewissen für Sie beantworten.

EDIT # 1

Sie hatte gehen besser mit diesem stattdessen Filters Beispiel. Ich habe einen Beispielcode geschrieben, um kurz zu zeigen, wie man mit den Namespaces System.DirectoryServices und System.DirectoryServices.ActiveDirectory arbeitet. Der Namespace System.DirectoryServices.ActiveDirectory wird verwendet, um Informationen zu den Domänen in Ihrer Gesamtstruktur abzurufen.

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

Hier habe ich diesen Code nicht getestet und könnte ein kleines Problem beheben. Dieses Beispiel wird zur Verfügung gestellt, um Ihnen zu helfen. Ich hoffe, das wird helfen.

EDIT # 2

fand ich einen anderen Ausweg aus:

  1. zu können, müssen schauen, ob Sie das Benutzerkonto in der Domäne finden kann;
  2. Wenn gefunden, dann erhalten Sie die Domain NetBIOS Name; und
  3. verketten Sie es mit einem Backslash (****) und dem gefundenen Login.

Das folgende Beispiel verwendet eine NUnit Testcase, die Sie für sich selbst testen und sehen, ob es das tut, was Sie benötigt.

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

Die Quelle, aus der ich mich inspiriert ist:
Find the NetBios Name of a domain in AD

+0

Will, ich habe diese Seite schon oft benutzt, aber es scheint keine genauen Angaben zur Frage zu geben. Trotzdem danke. –

+0

@Micheal Velasquez: Bitte sehen Sie meine Bearbeitung für ein Codebeispiel, das, ich hoffe, Ihnen helfen wird. –

+0

Ich portierte Ihr Codebeispiel in meine C# -Test-App und mit ein paar Optimierungen (d. H. Dem Rückgabetyp der GetDomains() -Methode usw.) lief ein Problem mit der Yield-Rückgabe der GetUserDomain() -Methode. Das DirectoryEntry-Objekt "Domäne" hat keine Eigenschaft "Domäne" (d. H. Domain.Domain) –

4

Da ich keinen Beispielcode finden konnte, möchte ich meine eigene Lösung teilen. Dies wird die Eltern des DirectoryEntry-Objekts durchsuchen, bis es die DomainDNS-Klasse erreicht.

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

es wie folgt verwendet:

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

Ich habe einen Wald nicht bekommen, es zu testen auf, aber dies sollte es in der Theorie schneiden.

+0

Es funktioniert gut, aber es gibt eine elegantere Implementierung für 'ldap_get_value':' return entry.Properties [propertyName] .OfType () .LastOrDefault(); ' –

+0

Danke Tiele, Ihr Code funktioniert für mich mit kleinen Änderungen, aber es sieht viel schneller als im Vergleich zu anderen Code ich online gefunden. –

+0

Ich erhalte die Ausnahme "Benutzername oder Passwort ist falsch" in var 'user = searcher.FindOne(). GetDirectoryEntry();' line. Ist es ein Authentifizierungsfehler? – Shesha

1

Vielleicht nicht ganz richtig, aber ...

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

Da der Wert von de.Path

ist

LDAP: // CN = Fullname, DC = Domäne, DC = local

+0

funktioniert bei mir mit weniger Netzwerk-Hops. wahrscheinlich wird nicht für jeden arbeiten, weil diese nicht 1 zu 1 sein müssen, ich thnk nicht –

Verwandte Themen