2

I have a requirement to apply different min length values in a change password function to a new password being created based on a user's roles. If a user has no administrative roles min length is 12, if they have admin roles min length is 16.

The current code has no such variable requirement logic. The implementation for the new password property was like so in model class called ChangePasswordData:

    ///summary>
    /// Gets and sets the new Password.
    /// </summary>
    [Display(Order = 3, Name = "NewPasswordLabel", ResourceType = typeof(UserResources))]
    [Required]
    [PasswordSpecialChar]
    [PasswordMinLower]
    [PasswordMinUpper]
    [PasswordMaxLength]
    [PasswordMinLength]
    [PasswordMinNumber]
    public string NewPassword { get; set; }

The validation attribute is set like so:

/// <summary>
/// Validates Password meets minimum length.
/// </summary>
public class PasswordMinLength : ValidationAttribute
{
    public int MinLength { get; set; }

    public bool IsAdmin { get; set; }

    public PasswordMinLength()
    {
        // Set this here so we override the default from the Framework
        this.ErrorMessage = ValidationErrorResources.ValidationErrorBadPasswordLength;

        //Set the default Min Length
        this.MinLength = 12;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null || !(value is string) || !UserRules.PasswordMinLengthRule(value.ToString(), this.MinLength))
        {
            return new ValidationResult(this.ErrorMessageString, new string[] { validationContext.MemberName });
        }

        return ValidationResult.Success;
    }
}

I want to be able to set the value of MinLength to 12 or 16 based on the value of IsAdmin however i cannot figure out how to decorate the attribute [PasswordMinLength(IsAdmin=myvarable)]. Only constants are allowed. How can I inject a property value into the ValidationAttribute that I can evaluate to determine the correct minimum length?

Thanks!

djcohen66
  • 115
  • 10
  • I do not think what you want to achieve is possible, because only static constants are allowed in an attribute value. – DevilSuichiro Aug 16 '16 at 19:06
  • I realize I cant just decorate the attribute with a variable value, but is there a way to inject values into the validation context items collection or some other method so that inside IsValid i can evaluate something to know that the user in question has admin roles? – djcohen66 Aug 16 '16 at 19:08
  • 1
    One option is to just use [class level validation](http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3) on your view model. Otherwise, you should be able to create an annotation like [PasswordMinLength("IsAdmin")] and then access that property in your validation using a technique similar to this:http://odetocode.com/blogs/scott/archive/2011/02/21/custom-data-annotation-validator-part-i-server-code.aspx – Steve Greene Aug 16 '16 at 20:18
  • Excellent reference! I will try to work this in, thanks! – djcohen66 Aug 16 '16 at 21:00

1 Answers1

0

Thanks to Steve Greene for providing this link to an example (http://odetocode.com/blogs/scott/archive/2011/02/21/custom-data-annotation-validator-part-i-server-code.aspx), I have the answer to my validation question. Here is the updated code:

/// <summary>
/// Validates Password meets minimum length.
/// </summary>
public class PasswordMinLength : ValidationAttribute
{
    public int MinLength { get; set; }

    public PasswordMinLength(string IsAdminName)
    {
        // Set this here so we override the default from the Framework
        this.ErrorMessage = ValidationErrorResources.ValidationErrorBadPasswordLength;
        IsAdminPropertyName = IsAdminName;
        //Set the default Min Length
        this.MinLength = 12;
    }

    public string IsAdminPropertyName{ get; set; }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name, IsAdminPropertyName);
    }

    protected bool? GetIsAdmin(ValidationContext validationContext)
    {
        var retVal = false;
        var propertyInfo = validationContext
                              .ObjectType
                              .GetProperty(IsAdminPropertyName);
        if (propertyInfo != null)
        {
            var adminValue = propertyInfo.GetValue(
                validationContext.ObjectInstance, null);

            return adminValue as bool?;
        }
        return retVal;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (GetIsAdmin(validationContext) != null)
        {
            if (GetIsAdmin(validationContext) == true)
                this.MinLength = 16;
            else
                this.MinLength = 12;
        }
        else
            this.MinLength = 12;

        if (value == null || !(value is string) || !UserRules.PasswordMinLengthRule(value.ToString(), this.MinLength))
        {
            return new ValidationResult(this.ErrorMessageString, new string[] { validationContext.MemberName });
        }

        return ValidationResult.Success;
    }

}

I simply added an IsAdmin property to my Model class and decorated the PasswordMinLength attribute like so:

[Display(Order = 3, Name = "NewPasswordLabel", ResourceType = typeof(UserResources))]
[Required]
[PasswordSpecialChar]
[PasswordMinLower]
[PasswordMinUpper]
[PasswordMaxLength]
[PasswordMinLength("IsAdmin")]
[PasswordMinNumber]
public string NewPassword { get; set; }

public bool IsAdmin { get; set; }

Works like a charm. Thanks Steve!

djcohen66
  • 115
  • 10