ASP.NET MVC authentication on groups - c#

I'm implementing an ASP.NET MVC web application just for fun. In my application, users can log in and join groups. I have a user authentication which works, where I inherit the identityUser. But I'd also like to authenticate the groups, i.e. if user tries to join a group, he would have to know the groups password to be able to join the group.
The entity model for the groups is like that:
public int Id { get; set; }
public string groupName { get; set; } <-- this is unique
public string Password { get; set; }
The view model would be similar but with [Required] attribute and what follows the View model.
But what I'm having trouble with is how I can authenticate the groups, have the passwordhash and check if password is valid and so on?
I'm using Visual Studio 2015.
My view when registering group:
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
My group view model:
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
Then I'm wondering how the controller should be so it'll authenticate the group.
[HttpPost, Authorize]
public ActionResult CreateLeague(RegisterGroupViewModel model)
{
if (!ModelState.IsValid) return View(model);
// check here if password is okay, and also hash it <-- here is my problem
// I've implemented if group is free to create, and create the group in the database.. which would be here below, no need to show it here.
}

Related

List of CC for email purposes not working

I have an EmailFormModel class.
public class EmailFormModel
{
[Required, Display(Name = "Your Name:")]
public string FromName { get; set; }
[Required, Display(Name = "Your Email:")]
public string FromEmail { get; set; }
[Required, Display(Name = "To Email:")]
public string ToEmail { get; set; }
public List<SelectListItem> CCEmail { get; set; }
[Required]
[AllowHtml]
public string Message { get; set; }
public EmailFormModel()
{
CCEmail = new List<SelectListItem>();
}
}
Now I need this email to have multiple CC recipients, hence why I made the property CCEmail a type of List. In my HttpGet method I am populating the list which is correctly working. In my HttpPost I am doing this:
foreach(var item in model.CCEmail)
{
message.CC.Add(new MailAddress(item.Text));
}
Now, in my View... what can I do to display these email addresses.. so that when I hit Submit they will be submitted as email addresses?
Currently in my View I have this:
<div class="form-group">
#Html.LabelFor(m => m.CCEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.ListBoxFor(m => m.CCEmail,null, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.CCEmail)
</div>
</div>
Is there another/better way to display the email addresses rather than ListBoxFor?
But when I select the email addresses.. and then hit Submit, I get an error message:
The value 'John.Doe#test.com,Test.User1#test.com' is invalid.
Those aren't the real email addresses.. the ones that I am using are valid.
Any help is appreciated.
Even though I have found an alternative solution, I am still looking for a cleaner solution. Here is what I have done.
I changed the CCEmail property to a List<string>.
So, in the HttpPost method I changed the foreach loop to this syntax:
foreach(var item in model.CCEmail)
{
message.CC.Add(new MailAddress(item));
}
Then in my view, I did this:
<div class="form-group">
#foreach(var item in Model.CCEmail)
{
#Html.LabelFor(m => m.CCEmail, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBox("CCEmail", item, htmlAttributes: new { #class = "form-control", #readonly = true } )
</div>
}
</div>
Even though this creates 2 separate textboxes, it still submits as 2 separate email addresses instead of both of them combined as what I think the error in my OP was.
Again, if you know of a simpler/cleaner solution, please post!

Remote validation works for creating or editing but not both

I want to use remote validation to check to see if a Username exists. I am using a Viewmodel to create users. While I can do this to get validation for creation or editing purposes, it will not work for both creating and editing. Here is my model:
[Required]
[Display(Name = "Homeowner Username")]
[Remote("doesUserNameExist", "Homeowners", HttpMethod = "POST", ErrorMessage = "User name already exists. Please enter a different user name.", AdditionalFields = "InitialUsername")]
Here is my edit view:
#Html.Hidden("Homeowner.InitialUsername", Model.Username)
<div class="form-group">
#Html.LabelFor(model => model.Username, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Username, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Username, "", new { #class = "text-danger" })
</div>
</div>
Here is my controller in the version that works for registration but not editing(when editing, parameters are null):
public JsonResult doesUserNameExist([Bind(Prefix = "Homeowner.Username")]string Username, [Bind(Prefix = "InitialUsername")] string InitialUsername)
{
MY CODE
}
Here is my controller that works for editing but not creating(when creating, both parameters are null):
public JsonResult doesUserNameExist([Bind(Include = "Homeowner.Username")]string Username, [Bind(Include = "InitialUsername")] string InitialUsername)
{
MY CODE
}
I have tried many variations of this but just can't get it.
I have looked here: ASP.NET MVC Binding with Remote Validation
Here:
Remote ViewModel validation of nested objects not working
And here: http://forums.asp.net/t/1652512.aspx?Compound+View+Model+object+causing+remote+validation+failure
But I seem to be missing something. Is there a way I can make this work for both editing and registering? I am pretty new at this, and would greatly appreciate any ideas!
Edit:
Perhaps this is a poor design choice(first time using view models, only been coding a few months), but I was trying to create a new homeowner and address at the same time as when I create a new application user in that role. Here is the viewmodel I am using:
public class RegisterHomeownerViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public int roles { get; set; }
public virtual Address Address { get; set; }
public virtual Homeowner Homeowner { get; set; }
}
Here is my method in the account controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RegisterHomeowner(RegisterHomeownerViewModel model, Address address, Homeowner homeowner)
{
ApplicationDbContext db = new ApplicationDbContext();
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var role = db.Roles.Find("0");
UserManager.AddToRole(user.Id, role.Name);
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
db.Addresses.Add(address);
homeowner.UserId = user.Id;
homeowner.AddressID = address.ID;
db.Homeowners.Add(homeowner);
db.SaveChanges();
return RedirectToAction("Index", "Homeowners");
}
AddErrors(result);
}
return View(model);
}
Here is the view I am using to create those entities:
<div class="form-horizontal">
<h4>RegisterViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Homeowner.Username, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Homeowner.Username, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Homeowner.Username, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</div>
</div>
Everything does work as far as I can tell except for remote validation on Username. I can get it to give me an error when creating in the account method above, or I can get an error when editing by deleting the Prefix (which makes it so my Username is not recognized. There is obviously something I am doing wrong.
I got it. I read Stephen's post a few times and a few threads on virtual properties. I went to bed, got up, and revised my viewmodel by removing all data models. Then, for validation when creating, I compared the input to the validation properties in my view model instead of my actual model. This works for creation. Then when I edit, I compare my input against the validation properties in my actual model. It made sense when I learned you can have different validation for a viewmodel than there is in the model. This thread helped too: View Model virtual properties and drop down lists. Thanks so much! I learned a lot from this!

changing a role with html select box asp.net mvc4

I am using the ASP.NET web application template and trying to allow a user to pick a role when registering.
Here is what I got at the moment.
Does it
View
<fieldset class="col-lg-5 .col-md-5">
<legend>Registration Form</legend>
<p>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
</p>
<p>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
</p>
<p>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</p>
<p>
#Html.DropDownListFor(model => model.Type, Model.TypeList)
</p>
<input type="submit" value="Register" class="btn btn-default" />
</fieldset>
Model
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Type { get; set; }
public IEnumerable<SelectListItem> TypeList
{
get
{
return new List<SelectListItem>
{
new SelectListItem { Text = "athlete", Value = "athlete"},
new SelectListItem { Text = "coach", Value = "coach"},
};
}
}
}
Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
Roles.AddUserToRole(model.UserName, model.Type);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I am feel like I am close but I can't quiet get it and i am getting this error
Compiler Error Message: Models.RegisterModel>' does not contain a definition for 'DropDownListFor' and the best extension method overload 'System.Web.Mvc.Html.SelectExtensions.DropDownListFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>, System.Collections.Generic.IEnumerable)' has some invalid arguments
Did you initialize your model in HttpGet Register method? Like below...
[AllowAnonymous]
public ActionResult Register()
{
var model = new RegisterModel();
return View(model);
}
I create an empty template MVC4 app, added your code, got object reference not set error as default Register model does not pass model object to view which you are trying to access (i.e. loading TypeInt in DropDownListFor()).
I then initialized model in Get method as shown above. All works fine, i was able to pick a role on register view.
Check if this helps.
take a look your last comma should be removed:
new SelectListItem { Text = "athlete", Value = "athlete"},
new SelectListItem { Text = "coach", Value = "coach"}

ASP.NET MVC Razor Model state not valid on RegisterModel

I'm having two issues with editing my applications RegisterModel.
A) The fields UserName and Email are rendered as password fields?
B) The modelstate is always invalid (and my model is empty)
I think they are both caused because I have a "HomeModel" which contains "LoginModel" and "RegisterModel" property and it passes the entire HomeModel instead of the corresponding property. How can I make it pass the correct one?
I have the following form:
#using (Ajax.BeginForm("Register", "Account", new AjaxOptions { UpdateTargetId = "RegisterAjaxResponse" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-row">
<div id="RegisterAjaxResponse"></div>
</div>
<div class="form-row">
#Html.LabelFor(m => m.RegisterModel.UserName)
#Html.PasswordFor(m => m.RegisterModel.UserName)
#Html.ValidationMessageFor(m => m.RegisterModel.UserName)
</div>
<div class="form-row">
#Html.LabelFor(m => m.RegisterModel.Password)
#Html.PasswordFor(m => m.RegisterModel.Password)
#Html.ValidationMessageFor(m => m.RegisterModel.Password)
</div>
<div class="form-row">
#Html.LabelFor(m => m.RegisterModel.ConfirmPassword)
#Html.PasswordFor(m => m.RegisterModel.ConfirmPassword)
#Html.ValidationMessageFor(m => m.RegisterModel.ConfirmPassword)
</div>
<div class="form-row">
#Html.LabelFor(m => m.RegisterModel.Email)
#Html.PasswordFor(m => m.RegisterModel.Email)
#Html.ValidationMessageFor(m => m.RegisterModel.Email)
</div>
<div class="form-row">
<input type="submit" value='Register' />
</div>
}
The model:
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
[DataType(DataType.Text)]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "EmailAddress")]
public string Email { get; set; }
}
But the UserName and Email field are rendered as an password field.
http://i.imgur.com/GCamint.png
-Can't page images yet, sorry.
And my modelstate is always invalid.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
string returnValue = "";
if (ModelState.IsValid)
{
//Some code that is never executed
}
return Content(returnValue, "text/html");
}
Problem A: you're rendering the textfields for email and username using #Html.PasswordFor(), this will render password fields, try using #Html.TextboxFor()
And for problem B, it depends if you're targetting MVC3 or 4 and which version of .NET.
Later versions of .NET use the compare annotation as
[Compare(CompareField = Password, ErrorMessage = "Passwords do not
match")]
a) Because you have in razor view for them
#Html.PasswordFor(m => m.RegisterModel.UserName)
need to be
#Html.TextboxFor(m => m.RegisterModel.Email)

How does the MVC3 Controller retrieve HTTPPOST params?

I'm not quite understanding how this works.
Passing parameters from my entity objects works fine. But when I create new fields, only the first one is retrieved.
Model User Class:
public class User {
[Key]
public long Uid { get; set; }
[Required]
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 4)]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email:")]
public string Email { get; set; }
[Required]
[StringLength(20, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 4)]
[Display(Name = "User Name:")]
public string Username { get; set; }
public string Password { get; set; }
public byte Role { get; set; }
public DateTime Created { get; set; }
}
CSHTML:
#using (Html.BeginForm( null,
null,
FormMethod.Post,
new { id = "regform" })
) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Register</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Username)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Username)
#Html.ValidationMessageFor(model => model.Username)
</div>
<div class="editor-label">
Password:
</div>
<div class="editor-field">
#Html.Password("pwd")
</div>
<div class="editor-label">
Confirm Password:
</div>
<div class="editor-field">
#Html.Password("confirm")
</div>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
}
Controller:
[HttpPost]
public ActionResult Register(User user, string pwd, string confirm) {
user.Username = confirm;
user.Created = DateTime.Now;
user.Role = 255;
user.Password = EncryptPassword.Password(pwd);
if (ModelState.IsValid && pwd == confirm) {
db.Users.Add(user);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
Where I'm getting confused, is pwd picks up fine. confirm on the other hand remains null. My initial thought that it was calling by order and confirm in the model was simply conPwd. When that didn't work, I changed it's name to confirm. It still is not working and I can't find anything that explains how multiple parameters are passed to the controller.
Edit:
Updated my code. Believe it or not, this alone has taken me most of the day to write because I've been trying to understand what I'm doing. There is just so much to take in when you're learning Entities, LINQ, MVC, ASP.NET and Razor all at the same time. Basic C# is the only part I came in to this knowing. :)
You need a strongly typed view for your RegisterModel then use a Html.BeginForm to post the data to the controller.
Model
// This is the Model that you will use to register users
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
View (CSHTML)
// This is your strongly typed view that will use
// model binding to bind the properties of RegisterModel
// to the View.
#model Trainer.Models.RegisterModel
// You can find these scripts in default projects in Visual Studio, if you are
// not using VS, then you can still find them online
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
// This is where your form starts
// The "Account" parameter states what controller to post the form to
#using (Html.BeginForm((string)ViewBag.FormAction, "Account")) {
#Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email)
#Html.ValidationMessageFor(m => m.Email)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
#Html.ValidationMessageFor(m => m.ConfirmPassword)
</li>
</ol>
<!-- The value property being set to register tells the form
what method of the controller to post to -->
<input type="submit" value="Register" />
</fieldset>
}
Controller
// The AccountController has methods that only authorized
// users should be able to access. However, we can override
// this with another attribute for methods that anyone
// can access
[Authorize]
public class AccountController : Controller
{
// This will allow the View to be rendered
[AllowAnonymous]
public ActionResult Register()
{
return ContextDependentView();
}
// This is one of the methods that anyone can access
// Your Html.BeginForm will post to this method and
// process what you posted.
[AllowAnonymous]
[HttpPost]
public ActionResult Register(RegisterModel model)
{
// If all of the information in the model is valid
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true, providerUserKey: null, status: out createStatus);
// If the out parameter createStatus gives us a successful code
// Log the user in
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
return RedirectToAction("Index", "Home");
}
else // If the out parameter fails
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
}

Categories