web-dev-qa-db-fra.com

Comment trouver un utilisateur dans Active Directory à partir de C #?

J'essaie de comprendre comment rechercher AD dans C # de la même manière que "Rechercher des utilisateurs, des contacts et des groupes" dans l'outil Utilisateurs et ordinateurs Active Directory. J'ai une chaîne qui contient soit un nom de groupe, soit un nom d'utilisateur (généralement au format prenom middleinitial [s'ils en ont un] nom, 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?

Je sais déjà comment utiliser la classe DirectorySearcher, le problème est que je ne peux pas trouver une requête qui fasse ce que je voudrais. Ni cn, ni samaccount name n’ont rien à voir avec le nom de l’utilisateur, je ne peux donc pas les rechercher. Le fractionnement et la recherche sur sn et givenName n'attrapent pas autant que cet outil.

18
Sunookitsune

Êtes-vous sur .NET 3.5? Si tel est le cas - AD présente de grandes nouvelles fonctionnalités dans .NET 3.5 - consultez cet article Gestion des principaux de sécurité des annuaires dans .NET 3.5 par Ethan Wilanski et Joe Kaplan.

L'une des principales nouvelles fonctionnalités est la 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 des choses qui pourrait vous faciliter la vie s'appelle "Résolution de nom ambiguë". Il s'agit d'un filtre de recherche spécial peu connu qui permet de rechercher simultanément tout attribut lié au nom.

Spécifiez votre requête de recherche LDAP comme ceci:

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

Aussi, je recommanderais de filtrer l'attribut "objectCategory", puisqu'il s'agit d'une valeur unique et indexée par défaut dans AD, ce qui est beaucoup plus rapide que d'utiliser "objectClass".

Marc

20
marc_s

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

Plus d'infos 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 voulez filtrer par nom de compte, définissez le filtre sur:

"(&(sAMAccountName=bsmith))"

et exécutez la méthode FilterAll. Cela retournera une SearchResultCollection que vous pourrez parcourir en boucle et extraire des informations sur l'utilisateur.

10
Miyagi Coder
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
Scott Lance

Vous devez créer la chaîne de recherche en fonction de la manière dont vous recherchez l'utilisateur.

using (var adFolderObject = new DirectoryEntry())
{
     using(var adSearcherObject = new DirectorySearcher(adFolderObject))
     {
          adSearcherObject.SearchScope = SearchScope.Subtree;
          adSearcherObject.Filter = "(&(objectClass=person)(" + userType + "=" + userName + "))";

          return adSearcherObject.FindOne();
     }
}

userType doit être soit sAMAccountName, soit CN, en fonction du format du nom d'utilisateur.

ex:
prenom.nom (ou flastname) sera généralement sAMAccountName
Prénom Nom est généralement le CN

3
Slipfish

Obtenu par la Joe Kaplan et Ethan Wilansky Article Utilisez cette utilisation (en faisant 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;
}
2
Gabriel Guimarães

Les autres réponses étaient mal décrites, ne décrivaient pas comment les mettre en œuvre et la plupart donnaient les mauvaises propriétés de filtrage. Vous n'avez même pas besoin d'utiliser .Filter - vous pouvez simplement affecter vos propriétés (nom = .Surname, prénom = .GivenName) à un objet UserPrincipal, puis effectuer une recherche sur cet objet à l'aide de PrincipalSearcher dans l'événement 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 les zones de texte pour le prénom et le nom pour l'obtenir, avec les identifiants/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 à la variable UserPrincipal, sinon cela provoquera une exception. C'est la raison des chèques que j'ai inclus, ci-dessus.

Vous faites ensuite un .FindAll sur srch pour obtenir les résultats de la recherche dans une collection PrincipalSearchResult d'objets Principal:

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 nuls même si sa .Count() est 0, et pourquoi les deux vérifications sont là.

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

J'ai découvert que vous ne pouvez pas simplement utiliser la variable username que j'ai indiquée ci-dessus, mais vous devez obtenir le type complet de nom DOMAIN\doej. Voici comment tu fais ça. Au lieu de cela, mettez ceci dans cette boucle foreach ci-dessus:

string userId = GetUserIdFromPrincipal(found);

et utiliser 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 cela, 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 concerne les nombres nord-américains. Il faudra un nombre trouvé (##########) et le séparer en ###-###-####.

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

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

Mais, en tant que solution complète, vous pouvez même ajouter ces résultats dans une colonne DataRow et les renvoyer dans le cadre d'une DataTable que vous pourrez ensuite lier à une ListView ou GridView. Voici comment je l'ai fait, en envoyant un List<string> contenant les propriétés dont j'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 appelleriez 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);

La DataTable sera remplie avec ces colonnes et une colonne NAME en tant que première colonne, qui contiendra le .DisplayName actuel de AD.

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

HTH!

1
vapcguy

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

Voici un filtre/requête à appliquer à DirectorySearcher

DirectorySearcher ds = new DirectorySearcher();

ds.Filter = "samaccountname=" + userName;

SearchResult result = ds.FindOne();
1
curtisk

Le code que je cherchais dans ce post é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 le caractère générique de n'importe quel caractère soit un astérisque (*). C'est pourquoi:

user.GivenName = "*firstname*";

En savoir plus dans Documentation Microsoft

0
Ivanzinho