DirectoryServices UserPrincipal.SetPassword ignores password policy (password history)

As the title suggests, I am having an issue regarding respecting the password policy when setting a users password, specifically, the password history restriction.

The scenario is a user password reset, when the user does not know his current password. I am using the following to accomplish this:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "XXXX", "ADMINUSER", "ADMINPASSWORD")) {
    using (UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username)) {
        user.SetPassword(password);
    }
}

This works against every policy MINUS the password history restriction.

Now take this scenario, when a user wants to change their password and knows their current password I am using:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "XXXX.XXX.com")) {
    using (UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username)) {
        user.ChangePassword(currentPassword, newPassword);
    }
}

… which works as expected, and validates against all password policy restrictions.

Has anyone ever had to deal this?

Cheers 🙂

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

This is by design, as far as I have used it. The SetPassword is intented to act like an admin who resets user password – the complexity policy holds but there are no restrictions on the history. Suppose admin resets your password, sees “can’t set the same password” – one of your passwords is compromised.

Our workaround was to allow the management to go through one of our web subsystems only and persist the history of hashes so that the responsibility to verify the history was put on the custom subsystem rather than the ad.

Method 2

I know this is an old post, but we never found an acceptable answer. Our systems folks didn’t like the idea of storing our own hashes for history. We ended up implementing our solution like this:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, 
        "XXXX","ADMINUSER", "ADMINPASSWORD"))
{
    using (UserPrincipal user = 
        UserPrincipal.FindByIdentity(context,IdentityType.SamAccountName, username)) 
    {
        string tempPassword = Guid.NewGuid().ToString();
        user.SetPassword(tempPassword);
        user.ChangePassword(tempPassword, password);
    }
}

We reset the person’s password to a random sufficiently long and complex password that our code knows. We then use that password as the old password in the change process using the new password that the user typed in. If the process fails the policy check including the password history, we pass that error back to the end user and they have to try again.

Method 3

Incase the username is not found via the FindByIdentify, you may want to also check it for null first!

using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "XXXX.XXX.com")) {
    using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, username))
    {
        if (user != null)
        {
            user.ChangePassword(currentPassword, newPassword);
        }
        else
        {
            throw new Exception(string.Format("Username not found: {0}", username));
        }
    }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x