Search
Close this search box.

MVC Validation of a Password with a Regular Expression

I have requirement (specification) that the password has to be at least 7 characters long and contain a special character (~@#$&*()-_+=) which are all the specials in the number keyboard row. I found that using the RegularExpression DataAnnotations is really slick, but there was a point that caught me for a while.

The problem is that @”.*([!@#$&*()-_+=]+).*$” works in the UI, but fails in the unit test. @”[!@#$&*()-_+=]+” works in the test, but not in the UI (the MVC unobtrusive library wants a full match). When I removed the ‘()’ it works in both. So my solution, after losing some time, is to add the escape before the ‘-‘. This works: ^.*(?=.*[!@#$%^&*\(\)_\-+=]).*$ (thanks to my co-worker Jason).

Here comes the code:

The Model for the MVC binding. Focus on the RegEx for NewPassword:

public class PasswordResetModel {
  /// <summary>
  /// Gets or sets the new password.
  /// It must be at least 7 characters and have at least one special character.
  /// </summary>
  /// <value>
  /// The new password.
  /// </value>
  [Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
            ErrorMessageResourceName = "NewPasswordRequiredMessage")]
  [StringLength(100,
                ErrorMessageResourceType = typeof(AuthenticationModelsResource),
                ErrorMessageResourceName = "PasswordTooShortErrorMessage",
                MinimumLength = 7)]
  [RegularExpression(
      @"^.*(?=.*[!@#$%^&*\(\)_\-+=]).*$",
      ErrorMessageResourceType = typeof(AuthenticationModelsResource),
      ErrorMessageResourceName = "PasswordNotStrongEnoughMessage")]
  [DataType(DataType.Password)]
  [Display(ResourceType = typeof(AuthenticationModelsResource),
           Name = "NewPassword")]
  public string NewPassword { get; set; }

  /// <summary>
  /// Gets or sets the confirm password.
  /// </summary>
  /// <value>
  /// The confirm password.
  /// </value>
  [DataType(DataType.Password)]
  [Display(ResourceType = typeof(AuthenticationModelsResource),
           Name = "ConfirmNewPassword")]
  [Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
            ErrorMessageResourceName = "ConfirmNewPasswordRequiredMessage")]
  [Compare("NewPassword",
           ErrorMessageResourceType = typeof(AuthenticationModelsResource),
           ErrorMessageResourceName = "PasswordCompareErrorMessage")]
  public string ConfirmPassword { get; set; }
}

The Unit test:

[TestMethod,TestCategory("HomeController")]
public void It_Should_Validate_Password_Doesnt_Have_A_Special_Character()
{
    PasswordResetViewModel vm = new PasswordResetViewModel("match", 10)
                            {
                                PasswordResetModel =
                                {
                                    NewPassword = "test1122",
                                    ConfirmPassword = "test1122"
                                }
                            };
    ((ChangePasswordModel)vm.PasswordResetModel).OldPassword = "test1";
    this.Controller.PasswordReset(vm);

    // assert
    ValidateViewModel(vm.PasswordResetModel, this.Controller);
    Should.BooleanAssertionExtensions.ShouldBeTrue(this.Controller.ModelState.Values.Last()
        .Errors.Any(e => e.ErrorMessage == AuthenticationModelsResource.PasswordNotStrongEnoughMessage));
}

The ValidateViewModel method:

protected static void ValidateViewModel<TVm, TC>(TVm viewModelToValidate, TC controller) where TC : Controller
{
    var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModelToValidate, viewModelToValidate.GetType());
    var validator = ModelValidator.GetModelValidator(modelMetadata, controller.ControllerContext);
    var result = validator.Validate(viewModelToValidate);
    foreach (var validationResult in result)
    {
        if (!string.IsNullOrWhiteSpace(validationResult.MemberName))
        {
            controller.ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
        }
        else
        {
            controller.ModelState.AddModelError(string.Empty, validationResult.Message);
        }
    }
}

he MVC Unobtrusive Validation library wants a complete match (from the source code):

$jQval.addMethod("regex", function (value, element, params) {
    var match;
    if (this.optional(element)) {
        return true;
    }

    match = new RegExp(params).exec(value);
    return (match && (match.index === 0) && (match[0].length === value.length));
});
This article is part of the GWB Archives. Original Author: Programming and Learning from SD

Related Posts