2009-05-05 7 views
18

J'essaie de comprendre comment rechercher AD à partir de C# de façon similaire à la façon dont "Rechercher des utilisateurs, des contacts et des groupes" fonctionne dans l'outil Utilisateurs et ordinateurs Active Directory. J'ai une chaîne qui contient soit un nom de groupe, soit le nom d'un utilisateur (généralement au format prénom middleinitial [s'ils en ont un], mais pas toujours). Même si je fais une requête séparée pour les groupes par rapport aux utilisateurs, je ne peux pas trouver un moyen de recherche qui capture la plupart des comptes d'utilisateurs. L'outil Rechercher des utilisateurs, des contacts et des groupes les ramène presque à chaque fois. Quelqu'un a des suggestions?Comment trouver un utilisateur dans Active Directory à partir de C#?

Je sais déjà comment utiliser la classe DirectorySearcher, le problème est que je ne peux pas trouver une requête qui fait ce que je veux. Ni cn ni samaccount n'ont de lien avec le nom de l'utilisateur, donc je ne peux pas les chercher. Diviser les choses et chercher sur sn et givenName ne se rapproche pas autant de cet outil.

+0

Les réponses ci-dessous sont tous sur l'utilisation du sAMAccountName, et je ne sais pas pourquoi ils ont tant de upvotes. Cette question concerne l'utilisation du prénom et du nom pour obtenir des propriétés! Seule la réponse correcte supérieure/marquée est même proche. – vapcguy

Répondre

18

Êtes-vous sur .NET 3 .5? Si c'est le cas - AD a de nouvelles fonctionnalités dans .NET 3.5 - consultez cet article Managing Directory Security Principals in .NET 3.5 par Ethan Wilanski et Joe Kaplan.

L'une des grandes nouveautés est une classe "PrincipalSearcher" qui devrait grandement simplifier la recherche d'utilisateurs et/ou de groupes dans AD. Si vous ne pouvez pas utiliser .NET 3.5, une chose qui pourrait vous rendre la vie plus facile est appelée "Résolution des noms ambigus", et c'est un filtre de recherche spécial peu connu qui va chercher dans n'importe quel attribut lié au nom en une seule fois .

Précisez votre recherche LDAP requête comme ceci:

searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", yourSearchTerm) 

En outre, je recommande le filtrage sur l'attribut « objectCategory », puisque c'est à valeur unique et indexé par défaut dans AD, ce qui est beaucoup plus rapide que en utilisant "objectClass".

Marc

+0

C'est exactement ce que je cherchais! Merci beaucoup! – Sunookitsune

10

System.DirectoryServices a deux espaces de noms ... DirectoryEntry et DirectorySearcher.

Plus d'informations sur le DirectorySearcher ici:

http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx

Vous pouvez ensuite utiliser la propriété Filter pour filtrer par groupe, utilisateur etc ...

Donc, si vous vouliez filtrer par nom de compte vous définissez la .Filtre à:

"(&(sAMAccountName=bsmith))" 

et exécuter la méthode FilterAll. Cela retournera une SearchResultCollection que vous pouvez parcourir et extraire des informations sur l'utilisateur.

+0

Merci, même si je connais DirectorySearcher. Le problème est que je ne peux pas trouver une requête pour trouver les utilisateurs dans AD. – Sunookitsune

+0

@Sunookitsune - Vous essayez donc de répliquer la fonctionnalité exacte "Rechercher des utilisateurs, des contacts et des groupes"? –

+0

@Miyagi Coder Je ne dirais pas exactement cette fonctionnalité, mais au moins obtenir quelque chose près de la façon dont il recherche des utilisateurs. Ou trouver quelque chose d'autre qui va marcher, parce que je suis perplexe. – Sunookitsune

4

Vous devez créer la chaîne de recherche en fonction de la façon dont vous recherchez l'utilisateur. UserType doit être sAMAccountName ou CN en fonction de la manière dont le nom d'utilisateur est formaté.

ex:
prénom.nom (ou flastname) seront généralement sAMAccountName
habituellement LastName FirstName le CN

+0

Non, 'sAMAccountName' est le nom d'utilisateur, c'est-à-dire' doej' pour John Doe, et l'OP n'a pas le nom d'utilisateur. Si vous voulez utiliser 'CN' pour' userType' et mettre 'FirstName LastName' au lieu de' username', il serait préférable d'utiliser le filtre '" (& (objectCategory = user) (objectClass = user) (givenName = "+ firstName +") (sn = "+ lastName +")) ";' à la place. – vapcguy

2

Pour ajouter sur la réponse de Miyagi ....

Voici un filtre/requête à appliquer à DirectorySearcher

DirectorySearcher ds = new DirectorySearcher(); 

ds.Filter = "samaccountname=" + userName; 

SearchResult result = ds.FindOne(); 
+0

Bonzai Curtis !!!! –

+0

OP n'avait pas le nom d'utilisateur, c'est-à-dire 'DOMAIN \ doej' pour John Doe - il reçoit le prénom et le nom à la place. Ce filtre est inutile pour ça. – vapcguy

3
public DirectoryEntry Search(string searchTerm, string propertyName) 
{ 
    DirectoryEntry directoryObject = new DirectoryEntry(<pathToAD>); 

    foreach (DirectoryEntry user in directoryObject.Children) 
    { 
     if (user.Properties[propertyName].Value != null)  
     if (user.Properties[propertyName].Value.ToString() == searchTerm) 
      return user;      
    } 

    return null; 
} 
3

a obtenu ce de l'article Joe Kaplan and Ethan Wilansky Utilisez cette aide (de référence à la dll System.DirectoryServices.AccountManagement):

using System.DirectoryServices.AccountManagement; 

private bool CheckUserinAD(string domain, string username) 
{ 
    PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain); 
    UserPrincipal user = new UserPrincipal(domainContext); 
    user.Name = username; 
    PrincipalSearcher pS = new PrincipalSearcher(); 
    pS.QueryFilter = user; 
    PrincipalSearchResult<Principal> results = pS.FindAll(); 
    if (results != null && results.Count() > 0) 
     return true; 
    return false; 
} 
+0

Downvote. OP a dit qu'il avait le nom réel de la personne, pas le nom d'utilisateur, comme 'user.Name' est montré comme prenant, ci-dessus. Cela fait une recherche sur les noms d'utilisateur, pas de prénom, nom de famille. – vapcguy

0

Les autres réponses ont été mal décrits, ne décrit pas comment pour les implémenter, et la plupart ont donné les mauvaises propriétés de filtre. Vous ne même pas besoin d'utiliser .Filter - vous pouvez simplement affecter vos propriétés (nom = .Surname, prénom = .GivenName) à un objet UserPrincipal, puis recherchez sur cet objet en utilisant un PrincipalSearcher en tout cas qui déclenche la recherche:

string firstName = txtFirstName.Text; 
string lastName = txtLastName.Text; 

PrincipalContext ctx = new PrincipalContext(ContextType.Domain); 

UserPrincipal up = new UserPrincipal(ctx); 
if (!String.IsNullOrEmpty(firstName)) 
    up.GivenName = firstName; 
if (!String.IsNullOrEmpty(lastName)) 
    up.Surname = lastName; 

PrincipalSearcher srch = new PrincipalSearcher(up); 
srch.QueryFilter = up; 

Je suppose que vous avez pour textboxes Prénom et nom pour l'obtenir, avec les ID/noms de txtFirstName et txtLastName. Notez que si vous n'avez pas de valeur dans la propriété que vous recherchez, ne l'ajoutez pas au UserPrincipal, sinon cela entraînera une exception. C'est la raison des contrôles que j'ai inclus, ci-dessus.

Vous pouvez ensuite faire un .FindAll sur srch pour obtenir des résultats de recherche dans une collection PrincipalSearchResult de Principal objets:

using (PrincipalSearchResult<Principal> results = srch.FindAll()) 
{ 
    if (results != null) 
    { 
     int resultCount = results.Count(); 
     if (resultCount > 0) // we have results 
     { 
      foreach (Principal found in results) 
      { 
       string username = found.SamAccountName; // Note, this is not the full user ID! It does not include the domain. 
      } 
     } 
    } 
} 

Notez que les résultats ne seront pas nulle même si son .Count() est 0, et pourquoi les deux contrôles sont Là.

Vous itérer à l'aide que foreach pour obtenir les propriétés dont vous avez besoin, et cela répond à la question de savoir comment trouver un utilisateur dans AD en utilisant C#, mais notez que vous ne pouvez obtenir à quelques propriétés en utilisant l'objet Principal, et si je atteint cette question par Google (comme je l'ai fait), je serais très découragé. Si vous trouvez que c'est tout ce dont vous avez besoin - génial, vous avez terminé! Mais pour obtenir le repos (et reposer ma propre conscience), vous devez plonger, et je vais décrire comment faire cela.

J'ai trouvé que vous ne pouvez pas simplement utiliser que username je mets ci-dessus, mais vous devez obtenir le type de nom entier DOMAIN\doej. C'est comme ça que tu fais ça. , Mettre à la place cela dans cette boucle foreach, au-dessus:

string userId = GetUserIdFromPrincipal(found); 

et utilisez cette fonction:

private static string GetUserIdFromPrincipal(Principal prin) 
{ 
    string upn = prin.UserPrincipalName; 
    string domain = upn.Split('@')[1]; 
    domain = domain.Substring(0, domain.IndexOf(".YOURDOMAIN")); 

    // "domain" will be the subdomain the user belongs to. 
    // This may require edits depending on the organization. 

    return domain + @"\" + prin.SamAccountName; 
} 

Une fois que vous avez, vous pouvez appeler cette fonction:

public static string[] GetUserProperties(string strUserName) 
    { 
     UserPrincipal up = GetUser(strUserName); 
     if (up != null) 
     { 
      string firstName = up.GivenName; 
      string lastName = up.Surname; 
      string middleInit = String.IsNullOrEmpty(up.MiddleName) ? "" : up.MiddleName.Substring(0, 1); 
      string email = up.EmailAddress; 
      string location = String.Empty; 
      string phone = String.Empty; 
      string office = String.Empty; 
      string dept = String.Empty; 

      DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject(); 
      DirectorySearcher ds = new DirectorySearcher(de); 
      ds.PropertiesToLoad.Add("l"); // city field, a.k.a location 
      ds.PropertiesToLoad.Add("telephonenumber"); 
      ds.PropertiesToLoad.Add("department"); 
      ds.PropertiesToLoad.Add("physicalDeliveryOfficeName"); 

      SearchResultCollection results = ds.FindAll(); 
      if (results != null && results.Count > 0) 
      { 
       ResultPropertyCollection rpc = results[0].Properties; 
       foreach (string rp in rpc.PropertyNames) 
       { 
        if (rp == "l") // this matches the "City" field in AD properties 
         location = rpc["l"][0].ToString(); 
        if (rp == "telephonenumber") 
         phone = FormatPhoneNumber(rpc["telephonenumber"][0].ToString());      
        if (rp == "physicalDeliveryOfficeName") 
         office = rpc["physicalDeliveryOfficeName"][0].ToString(); 
        if (rp == "department") 
         dept = rpc["department"][0].ToString(); 
       } 
      } 

      string[] userProps = new string[10]; 
      userProps[0] = strUserName; 
      userProps[1] = firstName; 
      userProps[2] = lastName; 
      userProps[3] = up.MiddleName; 
      userProps[4] = middleInit; 
      userProps[5] = email; 
      userProps[6] = location; 
      userProps[7] = phone; 
      userProps[8] = office; 
      userProps[9] = dept; 

      return userProps; 
     } 
     else 
      return null; 
    } 

    /// <summary> 
    /// Returns a UserPrincipal (AD) user object based on string userID being supplied 
    /// </summary> 
    /// <param name="strUserName">String form of User ID: domain\username</param> 
    /// <returns>UserPrincipal object</returns> 
    public static UserPrincipal GetUser(string strUserName) 
    { 
     PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain); 
     try 
     { 
      UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, strUserName); 
      return oUserPrincipal; 
     } 
     catch (Exception ex) { return null; } 
    } 

    public static string FormatPhoneNumber(string strPhoneNumber) 
    { 
     if (strPhoneNumber.Length > 0) 
      // return String.Format("{0:###-###-####}", strPhoneNumber); // formating does not work because strPhoneNumber is a string and not a number 
      return Regex.Replace(strPhoneNumber, @"(\d{3})(\d{3})(\d{4})", "$1-$2-$3"); 
     else 
      return strPhoneNumber; 
    } 

Notez que la fonction FormatPhoneNumber est pour les numéros nord-américains. Il prendra un nombre qu'il trouve (##########) et le séparera en ###-###-####.

Vous pouvez alors obtenir les propriétés comme celui-ci, de retour dans cette boucle foreach:

string[] userProps = GetUserProperties(userId); 
string office = userProps[8]; 

Mais, comme une solution tout, vous pouvez même ajouter ces résultats dans une colonne DataRow, et revenir comme partie d'un DataTable que vous pourriez ensuite lier à un ListView ou GridView. Voici comment je l'ai fait, l'envoi d'un List<string> rempli avec les propriétés que je avais besoin:

/// <summary> 
    /// Gets matches based on First and Last Names. 
    /// This function takes a list of acceptable properties: 
    /// USERNAME 
    /// MIDDLE_NAME 
    /// MIDDLE_INITIAL 
    /// EMAIL 
    /// LOCATION 
    /// POST 
    /// PHONE 
    /// OFFICE 
    /// DEPARTMENT 
    /// 
    /// The DataTable returned will have columns with these names, and firstName and lastName will be added to a column called "NAME" 
    /// as the first column, automatically. 
    /// </summary> 
    /// <param name="firstName"></param> 
    /// <param name="lastName"></param> 
    /// <param name="props"></param> 
    /// <returns>DataTable of columns from "props" based on first and last name results</returns> 
    public static DataTable GetUsersFromName(string firstName, string lastName, List<string> props) 
    { 
     string userId = String.Empty; 
     int resultCount = 0; 

     DataTable dt = new DataTable(); 
     DataRow dr; 
     DataColumn dc; 

     // Always set the first column to the Name we pass in 
     dc = new DataColumn(); 
     dc.DataType = System.Type.GetType("System.String"); 
     dc.ColumnName = "NAME"; 
     dt.Columns.Add(dc); 

     // Establish our property list as columns in our DataTable 
     if (props != null && props.Count > 0) 
     { 
      foreach (string s in props) 
      { 
       dc = new DataColumn(); 
       dc.DataType = System.Type.GetType("System.String"); 
       if (!String.IsNullOrEmpty(s)) 
       { 
        dc.ColumnName = s; 
        dt.Columns.Add(dc); 
       } 
      } 
     } 

     // Start our search 
     PrincipalContext ctx = new PrincipalContext(ContextType.Domain); 

     UserPrincipal up = new UserPrincipal(ctx); 
     if (!String.IsNullOrEmpty(firstName)) 
      up.GivenName = firstName; 
     if (!String.IsNullOrEmpty(lastName)) 
      up.Surname = lastName; 

     PrincipalSearcher srch = new PrincipalSearcher(up); 
     srch.QueryFilter = up; 

     using (PrincipalSearchResult<Principal> results = srch.FindAll()) 
     { 
      if (results != null) 
      { 
       resultCount = results.Count(); 
       if (resultCount > 0) // we have results 
       { 
        foreach (Principal found in results) 
        { 
         // Iterate results, set into DataRow, add to DataTable 
         dr = dt.NewRow(); 
         dr["NAME"] = found.DisplayName; 

         if (props != null && props.Count > 0) 
         { 
          userId = GetUserIdFromPrincipal(found); 

          // Get other properties 
          string[] userProps = GetUserProperties(userId); 

          foreach (string s in props) 
          { 
           if (s == "USERNAME")     
            dr["USERNAME"] = userId; 

           if (s == "MIDDLE_NAME") 
            dr["MIDDLE_NAME"] = userProps[3]; 

           if (s == "MIDDLE_INITIAL") 
            dr["MIDDLE_INITIAL"] = userProps[4]; 

           if (s == "EMAIL") 
            dr["EMAIL"] = userProps[5]; 

           if (s == "LOCATION") 
            dr["LOCATION"] = userProps[6]; 

           if (s == "PHONE") 
            dr["PHONE"] = userProps[7]; 

           if (s == "OFFICE") 
            dr["OFFICE"] = userProps[8];          

           if (s == "DEPARTMENT") 
            dr["DEPARTMENT"] = userProps[9]; 
          } 
         } 
         dt.Rows.Add(dr); 
        } 
       } 
      } 
     } 

     return dt; 
    } 

Vous appelez cette fonction comme ceci:

string firstName = txtFirstName.Text; 
string lastName = txtLastName.Text; 

List<string> props = new List<string>(); 
props.Add("OFFICE"); 
props.Add("DEPARTMENT"); 
props.Add("LOCATION"); 
props.Add("USERNAME"); 

DataTable dt = GetUsersFromName(firstName, lastName, props); 

Le DataTable sera rempli de ces colonnes, et une colonne NAME en tant que première colonne, qui aura le .DisplayName réel de l'utilisateur de AD.

Note: Vous devez faire référence System.DirectoryServices et System.DirectoryServices.AccountManagement, System.Text.RegularExpressions, System.Data à utiliser tout cela.

HTH!

0

Le code je regardais dans ce poste était:

 string uid = Properties.Settings.Default.uid; 
     string pwd = Properties.Settings.Default.pwd; 
     using (var context = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", uid, pwd)) 
     { 
      using (UserPrincipal user = new UserPrincipal(context)) 
      { 
       user.GivenName = "*adolf*"; 
       using (var searcher = new PrincipalSearcher(user)) 
       { 
        foreach (var result in searcher.FindAll()) 
        { 
         DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry; 
         Console.WriteLine("First Name: " + de.Properties["givenName"].Value); 
         Console.WriteLine("Last Name : " + de.Properties["sn"].Value); 
         Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value); 
         Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value); 
         Console.WriteLine("Mail: " + de.Properties["mail"].Value); 

         PrincipalSearchResult<Principal> groups = result.GetGroups(); 

         foreach (Principal item in groups) 
         { 
          Console.WriteLine("Groups: {0}: {1}", item.DisplayName, item.Name); 
         } 
         Console.WriteLine(); 
        } 
       } 
      } 
     } 
     Console.WriteLine("End"); 
     Console.ReadLine(); 

Il semble que générique pour tout caractère astérisque (*). Voilà pourquoi:

user.GivenName = "*firstname*"; 

Lire la suite dans Microsoft documentation

Questions connexes