Mel
Mel

Reputation: 2385

How to describe PasswordValidator rules

The Microsoft.AspNet.Identity.PasswordValidator is configurable with a number of parameters which define the minimum password length and complexity rules. If the user enters a password that does not meet the criteria, PasswordValidator will throw an exception which describes the violation, but it provides no other way to describe the rules to a user (e.g. "Passwords must be 8-12 characters and must include ..." etc).

I searched StackOverflow and the web in general figuring there must be hundreds of examples out there of what I need, but it's beginning to look like I'm the only person who has ever needed this, which I know isn't the case. Before I write my own implementation, I just have to ask...

Am I completely overlooking an accepted "standard" answer to this problem?

Upvotes: 0

Views: 604

Answers (2)

Mel
Mel

Reputation: 2385

Here's what I came up with since I needed to get something working, and wasn't finding the canned standard answer I was looking for. I implemented it as an extension over the PasswordValidator class for convenience, although that's not strictly necessary.

public static string ToDescription(this PasswordValidator validator)
{
    var options = new List<string>();
    if (validator.RequireUppercase) options.Add("upper-case letters");
    if (validator.RequireLowercase) options.Add("lower-case letters");
    if (validator.RequireDigit) options.Add("numbers");
    if (validator.RequireNonLetterOrDigit) options.Add("special characters");

    var result = "Passwords must be at least " 
        + validator.RequiredLength + " characters long"
        + (options.Any() ? ", and include " + options.OxfordJoin() : ".");

    return result;
}

I also added the OxfordJoin extension to turn the list of requirements into a nicely formatted sentence:

public static string OxfordJoin(this IEnumerable<string> source)
{
    var array = source.ToArray();
    var joined = string.Join(", ", array);
    var result = (array.Length > 2)
        ? joined.ReplaceLast(",", ", and")
        : joined.ReplaceLast(",", " and");

    return result;
}

...and finally a "so standard it should just be in the framework already" ReplaceLast implementation:

public static string ReplaceLast(this string value, string oldValue, string newValue)
{
    var position = value.LastIndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase);
    var result = (position > -1)
        ? value.Substring(0, position) + newValue + value.Substring(position + 1)
        : value;

    return result;
}

Upvotes: 0

rustem
rustem

Reputation: 153

It may sound a bit hackish but sending an empty string to ValidateAsync() method of PasswordValidator will do the work. Example of UserManager with default configuration :

var rules = await UserManager.PasswordValidator.ValidateAsync("");

rules.Errors.First() : 
Passwords must be at least 6 characters. 
Passwords must have at least one non letter or digit character. 
Passwords must have at least one digit ('0'-'9'). 
Passwords must have at least one lowercase ('a'-'z'). 
Passwords must have at least one uppercase ('A'-'Z').

you can even list them

var list = rules.Errors.First().Split('.'); 

Upvotes: 1

Related Questions