Bypassing model validation in the controller?

I created the ADO.NET model of my database. Created a new controller with CRUD (entity structure and using the ADO.NET entity model that I created).

My database has a simple user table. The Password line in the table will store user passwords encrypted using SimpleCrypto (PBKDF2).

In my ADO.NET Users.cs model, I added the following check:

[Required]
[DataType(DataType.Password)]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Password")]
public string Password { get; set; }

This works with jQuery in a validated browser. But in my controller, I encrypt the password, and then the string "Password" will contain more than 20 characters in length.

var crypto = new SimpleCrypto.PBKDF2();
var encryptedPass = crypto.Compute(user.Password);

user.Password = encryptedPass;
user.PasswordSalt = crypto.Salt;

_db.Users.Add(user);
_db.SaveChanges();

And that gives me and "Failed to validate for one or more objects." - mistake.

"var newUser", , ?

EDIT: , . , , , 6-20 +100 lengt - .

: .

[HttpPost]
public ActionResult Create(Users user)
{
    if (!ModelState.IsValid)
    {
        return View();
    }
    if (_db.Users.FirstOrDefault(u => u.Email == user.Email) != null)
    {
        ModelState.AddModelError("", "User already exists in database!");
        return View();
    }

    var crypto = new SimpleCrypto.PBKDF2();
    var encryptedPass = crypto.Compute(user.Password);

    user.Password = encryptedPass;
    user.PasswordSalt = crypto.Salt;

    _db.Users.Add(user);
    _db.SaveChanges();

    return RedirectToAction("Index", "User");
}
+4
3

, .
20

[StringLength(20, MinimumLength = 6)]

20 .
entity , ( Entity , , )

, ClientSide viewmodel, .
, , .

viewModel , viewModel maxlength 20, 100.

, , unmapped password, html , .
:

public class RegisterModel
{
    [Required]
    public string UserName { get; set; }

    [Required]
    [NotMapped]
    [StringLength(20, MinimumLength = 6)]
    [Display(Name = "Password")]
    public string PlainTextPassword { get; set; }

    [Required]
    [StringLength(300)]//This is optional
    [DataType(DataType.Password)]
    public string Password { get; set; }
}
+2

ViewModels. , .

ViewModel:

public class RegisterModel
{
    [Required]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [StringLength(20, MinimumLength = 6)]
    [Display(Name = "Password")]
    public string Password { get; set; }
}

( User):

public class User
{
    // other properties..

    [Required]
    public string Password { get; set; }
}

:

GET:

public ActionResult Register()
{
    var registerModel = new RegisterModel();
    return View(registerModel)
}

:

@model RegisterModel

@Html.LabelFor(model => model.UserName)
@Html.TextBoxFor(model => model.UserName)
@Html.ValidationMessageFor(model => model.UserName)

@Html.LabelFor(model => model.Password)
@Html.PasswordFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)

POST:

[HttpPost]
public ActionResult Register(RegisterModel registerModel)
{
    // Map RegisterModel to a User model.       
    var user = new User
                   {
                        UserName = registerModel.UserName,
                        Password = registerModel.Password   // Do the hasing here for example.
                    };
    db.Users.Add(user);
    db.SaveChanges();                           
}
+8

You say that your password encryption takes place in the controller. In this case, you should not encrypt after verification? For example:

public ActionResult SomeControllerAction(UserViewModel user)
{
    if (!ModelState.IsValid)
    {
        // at this point the human readable (and assuming < 20 length) password
        // would be getting validated
        return View(user);
    }

    // now when you're writing the record to the DB, encrypt the password
    var crypto = new SimpleCrypto.PBKDF2();
    var encryptedPass = crypto.Compute(user.Password);

    user.Password = encryptedPass;
    user.PasswordSalt = crypto.Salt;

    _db.Users.Add(user);
    _db.SaveChanges();

    // return or redirect to whatever route you need
}

If you want to specifically control your validation, try implementing IValidatableObjectview models in the class and perform validation here, and not through attributes. For example:

public class UserViewModel : IValidatableObject
{
    public string Username { get; set; }
    public string Password { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // validate the unencrypted password length to be < 20
        if (this.Password.Length > 20)
        {
            yield return new ValidationResult("Password too long!");
        }
    }        
}
+3
source

All Articles