I’m stuck with the UserPrincipal class manipulating AD-LDS user-objects. While I can query users, change attributes and save them back to AD LDS, as soon as I try to set (or change) the password, I always get a PrincipalOperationException
with the additional information “The directory property cannot be found in the cache.”
If I change the user’s password via ADSI-Edit, everything works fine. Therefore I assume it’s not a schema-issue. I bind with simple binding and without SSL (it’s a development-machine). I have allowed the unsecure password-setting option via dsmgmt. The user I use to connect is in the admin-role of the directory hive where the users reside in. The code looks like this (simplified):
using (var ctx = GetPrincipalContext()) {
UserPrincipal u = UserPrincipal.FindByIdentity(ctx, IdentityType.Name, identityValue);
u.SetPassword("12345$äöAAA1234_");
u.Save();
}
where the context is created in GetPrincipalContext() like this:
new PrincipalContext(ContextType.ApplicationDirectory, "localhost", "[DC=...]", ContextOptions.SimpleBind,"[username of adminuser]","[Password of admin user]");
The same issue arises when I try to create a new UserPrincipal and then saving it. I assume, it is also because of the attached Password.
To tell the long story short, it’s a security problem. As long as the connection to AD-LDS is not established via SSL, everything that deals with passwords is a headache.
After finding the solution to the initial issue (see below), I came to the conclusion, that it is far more efficient to install AD-LDS with a certificate, than to try resolving all the problems that arise from a configuration without SSL (even in development). Therefore I recommend to everyone who wants to work with AD-LDS, to do the extra work of installing a development-certificate. The complexity of doing this is ways below the complexity of working around all the problems that arise without SSL. After some googling, I was able to create and install the necessary certificate(s) with the makecert-programm of the windows sdk.
For those interested in a solution anyways:
After reading a lot about AD-LDS and the way it authenticates, it is my understanding that setting the password with the UserPrincipal-class without SSL is not possible. As a workaround, the DirectoryEntry-class can be used to invoke the SetPassword-methdod via LDAP. However it needs some parametrization:
const long ADS_OPTION_PASSWORD_PORTNUMBER = 6;
const long ADS_OPTION_PASSWORD_METHOD = 7;
const int ADS_PASSWORD_ENCODE_CLEAR = 1;
directoryEntry.Invoke("SetOption", new object[] { ADS_OPTION_PASSWORD_PORTNUMBER, intPort });
directoryEntry.Invoke("SetOption", new object[] { ADS_OPTION_PASSWORD_METHOD, ADS_PASSWORD_ENCODE_CLEAR});
directoryEntry.Invoke("SetPassword", new object[] { "MyPassword" });
Furthermore, it is necessary to allow the unsave password-communication via the dsmgmt-command line tool.
The understanding of what is going on, I got from an online preview of the book The .Net Developer's Guide to Directory Services Programming Although I probably do not need the book anymore, I ordered it to donate the author, he saved me hours of search. Although it is old, the book contains very good explanations about AD LDS (ADAM) which are still relevant nowadays.