How can you find a user in active directory from C#?

后端 未结 8 654
时光取名叫无心
时光取名叫无心 2020-12-24 08:13

I\'m trying to figure out how to search AD from C# similarly to how \"Find Users, Contacts, and Groups\" works in the Active Directory Users and Computers tool. I have a str

相关标签:
8条回答
  • 2020-12-24 08:31

    Are you on .NET 3.5 ? If so - AD has great new features in .NET 3.5 - check out this article Managing Directory Security Principals in .NET 3.5 by Ethan Wilanski and Joe Kaplan.

    One of the big new features is a "PrincipalSearcher" class which should greatly simplify finding users and/or groups in AD.

    If you cannot use .NET 3.5, one thing that might make your life easier is called "Ambiguous Name Resolution", and it's a little known special search filter that will search in just about any name-related attribute all at once.

    Specify your LDAP search query like this:

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

    Also, I would recommend filtering on the "objectCategory" attribute, since that's single-valued and indexed by default in AD, which is a lot faster than using "objectClass".

    Marc

    0 讨论(0)
  • 2020-12-24 08:34
    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;
    }
    
    0 讨论(0)
  • 2020-12-24 08:34

    The other answers were poorly described, didn't describe how to implement them, and most gave the wrong filter properties. You don't even need to use .Filter -- you can just assign your properties (last name = .Surname, first name = .GivenName) to a UserPrincipal object, then search on that object using a PrincipalSearcher in whatever event that triggers the search:

    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;
    

    I'm assuming you have textboxes for First and Last Name to get it, with IDs/Names of txtFirstName and txtLastName. Note that if you do not have a value in the property you are looking for, do not add it to the UserPrincipal, or it will cause an exception. That's the reason for the checks I included, above.

    You then do a .FindAll on srch to get search results into a PrincipalSearchResult collection of Principal objects:

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

    Note that results won't be null even if its .Count() is 0, and why both checks are there.

    You iterate using that foreach to get the properties you need, and this answers the question of how to find a user in AD using C#, but note you can only get to a few properties using the Principal object, and if I reached this question through Google (as I did), I would be very disheartened. If you find that's all you need - great, you're done! But in order to get the rest (and rest my own conscience), you have to dive down, and I'll describe how to do that.

    I found you can't just use that username I put above, but you have to get the whole DOMAIN\doej kind of name. This is how you do that. Instead, put this in that foreach loop, above:

    string userId = GetUserIdFromPrincipal(found);
    

    and use this function:

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

    Once you have that, you can call this function:

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

    Note that the FormatPhoneNumber function is for North American numbers. It will take a number it finds (##########) and separate it into ###-###-####.

    You can then get the properties like this, back in that foreach loop:

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

    But, as a whole solution, you can even add these results into a DataRow column, and return it as part of a DataTable that you could then bind to a ListView or GridView. This is how I did it, sending in a List<string> filled with the properties I needed:

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

    You would call this function like this:

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

    The DataTable will be filled with those columns, and a NAME column as the first column, that will have the user's actual .DisplayName from AD.

    Note: You must reference System.DirectoryServices and System.DirectoryServices.AccountManagement, System.Text.RegularExpressions, System.Data to use all this.

    HTH!

    0 讨论(0)
  • 2020-12-24 08:42

    The code I were looking for in this post was:

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

    It seems that wildcard for any character is Asterisk (*). That's why:

    user.GivenName = "*firstname*";
    

    Read more in Microsoft documentation

    0 讨论(0)
  • 2020-12-24 08:43

    To add onto Miyagi's answer....

    Here's a filter/query to apply to DirectorySearcher

    DirectorySearcher ds = new DirectorySearcher();
    
    ds.Filter = "samaccountname=" + userName;
    
    SearchResult result = ds.FindOne();
    
    0 讨论(0)
  • 2020-12-24 08:44

    Got this from the Joe Kaplan and Ethan Wilansky Article Use this Using (from referencing the System.DirectoryServices.AccountManagement dll):

    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 讨论(0)
提交回复
热议问题