0

We have a .NET Framework application used for changing user passwords. It uses the System.DirectoryServices.AccountManagement assembly.

Process goes as follows:

  1. A user fills out a simple form (username, old pw, new pw, new pw repeated).
  2. UserPrincipal.FindByIdentity() gets called to find the user
  3. UserPrincipal.ChangePassword() gets called to change the password

Note: Principal context type is set to DOMAIN

Note2: everything works, the issue is with the user folders.

So apparently ChangePassword() creates a user profile in C:\Users folder in the application's machine and I cannot find any information why that happens. I would understand in the context was set to Machine, but in this case it's not. There are over 6k folders now, one for each user, it's taking up a lot of space and slowing the machine down.

I tried recreating the problem locally and apparently the ChangePassword() creates a TEMP user profile in C:\Users in my computer, but then it disappears.

Code

Configuration

container.RegisterType<PasswordChangeService>(new InjectionFactory(m => new PasswordChangeService(
                new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["LdapDomain"], ConfigurationManager.AppSettings["LdapQuery"]),
                new LogService(new Persistence.LogDbContext())
                )));

Code for finding user, changing password

using (var user = UserPrincipal.FindByIdentity(_principalContext, IdentityType.SamAccountName, passwordChange.Username)) {
                    if (user == null) {
                        Fail(passwordChange, "User not found");
                    }

                    user.ChangePassword(passwordChange.CurrentPassword, passwordChange.NewPassword);

                    LogSuccess(passwordChange);
                }
  1. Can anyone tell me why the user profiles get created?
  2. How to fix this issue? Is this a configuration or permission problem?
  3. (optional) I've seen examples where UserPrincipal.Save() gets called after, say, ChangePassword(), but it has always worked without it, so in what situation can it be used?

Thank you.

Update after some unsuccessful searching I found out a few things that might be worth mentioning:

1. Someone from 2009 had the same problem technet link

The last comment "Since all the code snippets utilize the ChangePasword implementation of ADSI (which causes the profile generation) the simplest way to accomplish this programmatically would be to not use ADSI but System.DirectoryServices.Protocols instead and perform an attribute modification against unicodePwd".

2. When calling ChangePassword(), user directory was sometimes not created and at at those times Event Viewer showed a couple of errors

"Windows has backed up this user profile. Windows will automatically try to use the backup profile the next time this user logs on."

"Windows cannot find the local profile and is logging you on with a temporary profile. Changes you make to this profile will be lost when you log off."

3. Every "fix" I've seen resulted in fixing the consequence, but not the cause. I still have no idea why the profiles get created, but if the technet comment is legit, I guess I need to use another implementation.

I ended up using an example from SO link and this is my result:

string ldapPath = ConfigurationManager.AppSettings["LdapPath"];
                string domainWithUserName = string.Format(ConfigurationManager.AppSettings["LdapDomain"], "\\", passwordChange.Username);
                DirectoryEntry directoryEntry = new DirectoryEntry(ldapPath, domainWithUserName, passwordChange.CurrentPassword);
                if (directoryEntry != null) {
                    DirectorySearcher search = new DirectorySearcher(directoryEntry);
                    search.Filter = "(SAMAccountName=" + passwordChange.Username + ")";
                    SearchResult result = search.FindOne();
                    if (result != null) {
                        DirectoryEntry userEntry = result.GetDirectoryEntry();
                        if (userEntry == null) {
                            Fail(passwordChange, "User not found.");
                        }

                        userEntry.Invoke("ChangePassword", new object[] { passwordChange.CurrentPassword, passwordChange.NewPassword});
                        userEntry.CommitChanges();

                        LogSuccess(passwordChange);
                    }
                }

I still need to narrow down the AD search to a specific OU, etc.

This implementation works, no user profile gets created, Event Viewer shows nothing. It's a shame, really, because I'd rather call a few methods instead of using 15 lines of code that do the same thing.

0 Answers0