0

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>
vampiire
  • 1,111
  • 2
  • 15
  • 27
  • Just make it public. It isn't necessary to make it private for the reasons you have stated above. Read through the following for reasons as to why you would want to make a property private: https://stackoverflow.com/questions/3847832/understanding-private-setters – RichardMc Mar 06 '18 at 17:32
  • @RichardMc this is talking about private setter. I have a private getter so the password can only be read internally in the class – vampiire Mar 06 '18 at 17:44
  • I don't mean to argue. I just want to know why it's not a good idea so I can learn. My intuition says making the getter private will be safer for a password property. Can you correct my intuition so I can learn – vampiire Mar 06 '18 at 17:45
  • I think you might be a little confused to the purpose of private. you are right in saying its limiting access to its current class but its not making the password property safer unless you don't trust your own code. Password safety is dealt with how you store and transport it. What tchelidze outlines below describes how to properly secure a password. There is a lot there but its worth knowing. – RichardMc Mar 06 '18 at 17:55

1 Answers1

3
  • Do not store password as plain text in database
  • Use ASP.NET Identity for managing Auth
  • Do not use Domain Entities (User in this case) directly in View, instead use ViewModel
  • User Fluent Validation for validating user input
tchelidze
  • 8,050
  • 1
  • 29
  • 49
  • Thank you for he information. These are good next steps for me to take. For now I am just practicing data binding to a model. Unfortunately the ms docs are very convoluted and confusing – vampiire Mar 06 '18 at 17:40
  • 1
    For model binding, there is not point having `private` properties, `ViewModel`'s are ment to transfer data from browser to you controller. – tchelidze Mar 06 '18 at 17:42
  • Can you expand on why using view model instead of entity? The tutorial I was following said to put the user as a paremeter of the route handler so that "'model binding of form data" could occur – vampiire Mar 06 '18 at 17:46
  • @vampiire Sure, take a look at [this](https://stackoverflow.com/questions/23648832/viewmodels-in-mvc-mvvm-seperation-of-layers-best-practices) – tchelidze Mar 06 '18 at 17:47
  • The alternative I see is to just receive the form inputs in the controller and the handle populating the model manually in the handler body. Does this seem the more correct way to do it? – vampiire Mar 06 '18 at 17:47