I am fairly new to MVC5/ASP.NET Identity and I have found an issue that has stumped me a bit.
I am writing a small form for my ASP.NET MVC5 application that will allow an admin user (member of the Admins role) to review the users that have signed up to the site and edit the details of and assign roles to those users. When the form is submitted, if a role has been assigned to the user the UserManager.AddToRole method is called.
I noticed after this that once this is done, that user is then unable to log into the application. Looking in the database, it appears when AddToRole is called, the PasswordHash field is set to null. Is this normal and if not how to I get around this issue?
For reference my relevant code is below
Controller
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult Details(EditUserViewModel model)
{
model.Save();
return RedirectToAction("List", "Account");
}
Relevant view models
public class EditUserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
[Required]
[StringLength(255)]
[Display(Name = "Email address")]
public string Email { get; set; }
[StringLength(255)]
[Display(Name = "First name")]
public string FirstName { get; set; }
[StringLength(255)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[StringLength(255)]
[Display(Name = "Mobile Number")]
public string MobileNumber { get; set; }
public IList<EditUserRolesViewModel> UserRoles { get; set; }
public void Save()
{
using (ApplicationDbContext context = new ApplicationDbContext())
{
ApplicationUser user = new ApplicationUser()
{
Id = this.UserId,
UserName = this.UserName,
Email = this.Email,
FirstName = this.FirstName,
LastName = this.LastName,
MobileNumber = this.MobileNumber
};
context.Users.Attach(user);
context.Entry(user).Property(x => x.Email).IsModified = true;
context.Entry(user).Property(x => x.FirstName).IsModified = true;
context.Entry(user).Property(x => x.LastName).IsModified = true;
context.Entry(user).Property(x => x.MobileNumber).IsModified = true;
context.SaveChanges();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
foreach (var row in this.UserRoles)
{
if (row.RowChanged)
{
if (row.RoleAssigned)
{
UserManager.AddToRole(this.UserId, row.RoleName);
}
else
{
UserManager.RemoveFromRole(this.UserId, row.RoleName);
}
}
}
}
}
}
public class EditUserRolesViewModel
{
public string RoleId { get; set; }
[Display(Name = "Role name")]
public string RoleName { get; set; }
[Display(Name = "Assigned")]
public bool RoleAssigned { get; set; }
public bool RowChanged { get; set; }
}
As I see from your code, you have attached partially initialized object user
to context.Users
. As a result when UserManager
gets control: AddToRole
, it tries to update Database. And you'll have lot empty or null fields in the current users row.
You can fix doing any of the following (both will help):
instead of
ApplicationUser user = new ApplicationUser()
use
user = UserManager.FindById(UserId)
after assigning values from viewmodel EntityFramework will take care of modified fields.
use another context when dealing with roles
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));