How do I search for a user in a particular OU on a remote LDAP server?

855 views Asked by At

I would like to use the AccountManagement namespace introduced in .NET 3.5 to find a user and set their password. However, the ADLDS server is not part of our company domain so I'm using ContextType.Machine. When I search for the user it's never found (I suspect it's searching in the wrong container, but according to the documentation when using ContextType.Machine you can't specify a container).

using (var context = new PrincipalContext(ContextType.Machine, "test-server", null, "username", "password")) {
    using (var u = UserPrincipal.FindByIdentity(context, "SuperAdmin")) {
        //u is always null.   :(
    }
}

However, I know I can find the user using plain ol' DirectoryEntry:

using (var de = new DirectoryEntry("LDAP://test-server:389/CN=SuperAdmin,CN=SuperUsers,OU=test-server,DC=foo,DC=bar,DC=com", "username", "password", AuthenticationTypes.Secure)) {
    //The user is found, but SetPassword fails with "The directory property cannot be found in the cache"
    de.Invoke("SetPassword", new object[] { "foobar" });
}

One last thing to point out is that I can use ADSI Edit to change the password with these same credentials. Is it possible to use the newer directory objects to perform this search?

1

There are 1 answers

0
smr5 On

It's really an old question, but just recently I had to work on a similar project... I'll post the answer if anybody runs into the same issue.

The reason you cannot find the user using UserPrincipal class is that as you mentioned you're searching using ContextType.Machine. But in DirectEntry class you're just doing a simple LDAP:// query.

Here's my solution.

I store my server information in .config file.

... 
//Server name and port
<add key ="ADLDS_Server" value="Servername:port"/> 
//Note* depending on structure container will be different for everybody
<add key ="ADLDS_Container" value="CN=Some Value, DC=some value,DC=value"/>
...

I then created ADLDSUtility class that returns PrincipalContext object.

...
using System.DirectoryServices.AccountManagement
...
public class ADLDSUtility
{
    public static ContextOptions ContextOptions = ContextOptions.SecureSocketLayer | ContextOptions.Negotiate;

    public static PrincipalContext Principal
    {
        get
        {
            return new PrincipalContext(
               ContextType.ApplicationDirectory,
               ConfigurationManager.AppSettings["ADLDS_Server"],
               ConfigurationManager.AppSettings["ADLDS_Container"],
               //you can specify username and password if need to
               ContextOptions);
        }
    }

From there, I wrote a method that accepts (username, currentPassword and newPassword) as paramaters.

public void ChangePassword(string userName, string currentPassword, string newPassword)
    {
        using (PrincipalContext ctx = ADLDSUtility.Principal)
        {
            using (UserPrincipal principal = new UserPrincipal(ctx))
            {
                using (var searchUser = UserPrincipal.FindByIdentity(ctx, IdentityType.UserPrincipalName, userName))
                {
                    if (searchUser != null)
                    {
                        searchUser.ChangePassword(currentPassword, newPassword);
                        // searchUser.SetPassword(newPassword);
                        if (String.IsNullOrEmpty(searchUser.Guid.ToString()))
                        {
                            throw new Exception("Could not change password");
                        }
                    }
                }
            }
        }
    }

In this example, I'm searching user by UserPrincipalName. But we are not limited to that. We can also search user by IdentityType.Guid etc.

Now searchUser has two methods that involves password. I provided both of them.

//requires current and new password
searchUser.ChangePassword(currentPassword, newPassword);
//setting a password. Only requires new password.
searchUser.SetPassword(newPassword);

NOTE it's preferred to use SSL to set or change passwords.*