I have this simple User Class and for the Password property I want to make the getter private (only used internally - exposing a public VerifyPassword method that will perform a verification internally and return a boolean)
Now the question comes with model binding on a route in my Controller. When I try to use the asp-for
tag helper to point at the Model.Password it tells me that "Password can not be used in this context because the get-accessor is unavailable"
I am a beginner. I thought making the getter private was a smart move to protect it from being exposed. But now I can not use it in the model binding.
So a few questions: Is this the correct way of protecting the Password? Is this the correct way of using model binding? Any suggestions on how this can be improved?
Here is the model:
using System;
namespace user_signup.Models {
public class User
{
private string username;
private string email;
private string password;
public string Username {
get => username;
set {
if (value.Length < 5) {
throw new ArgumentException("Username is too short.");
}
if (value.Length > 15) {
throw new ArgumentException("Username is too long.");
}
username = value;
}
}
public string Email {
get => email;
set {
// email validation here
email = value;
}
}
public string Password {
// the getter is private (only accessable internally)
private get => password;
set {
if (value.Length < 5) {
throw new ArgumentException("Password is too short.");
}
if (value.Length > 15) {
throw new ArgumentException("Password is too long.");
}
password = value;
}
}
// public means of verifying a password (the password itself is never exposed)
public bool VerifyPassword(string pass) => Password.Equals(pass);
// counter of all users that have been instantiated (prevents overlapping IDs)
// static so it is at the Class level (can be counted globally outside of all User instances)
private static int NextId = 0;
// can only be set internally (private setter)
// defaults to the CURRENT VALUE of NextId and THEN increments NextId for the next instantiation
public int UserId { get; } = NextId++;
// can only be set internally (private setter)
// defaults to current DateTime in string format
public string CreateDate { get; private set; } = DateTime.Now.ToString();
// the model will be populated from form data sent in a Request
public User() {}
// for creating a user by other means (manually)
public User(string u, string e, string p) {
Username = u;
Email = e;
Password = p;
}
public override string ToString() {
return String.Format(
"UserId: {0}\nUsername: {1}\nEmail: {2}\n\n",
UserId,
Username,
Email
);
}
}
}
Here is the Controller (specifically the Add.cshtml GET and POST handlers)
[HttpGet]
public IActionResult Add() {
VModel.User = new User();
return View(VModel);
}
[HttpPost]
public IActionResult Add(User user, string verify) {
VModel.User = user;
VModel.Users = Users.GetUsers();
if (!user.VerifyPassword(verify)) {
VModel.Errors["verify"] = "Verify password failed. Passwords do not match.";
return View(VModel);
}
return View("Index", VModel);
}
and finally the Add.cshtml View
@using user_signup.Controllers
@model UserController.ViewModel
<!DOCTYPE html>
<html>
<head>
<title>title</title>
</head>
<body>
<div>
<form asp-controller="User" asp-action="Add" method="post">
<label>Username: </label>
<input type="text" asp-for="@Model.User.Username" required/>
<label>Email: </label>
<input type="text" asp-for="@Model.User.Email"/>
<label>Password: </label>
<input type="password" asp-for="@Model.User.Password" required/>
<label>Verify Password: </label>
<input type="password" name="verify" required/>
<input type="submit" value="Sign Up"/>
</form>
</div>
</body>
</html>