How to unit test asp.net identity extended functionality - c#

I extended my User class:
public class ApplicationUser : IdentityUser
{
public bool Blocked { get; set; }
}
and wrote a method:
[Authorize(Roles="admin")]
public ActionResult Ban(string id)
{
var context = new ApplicationDbContext();
var user = context.Users.Find(id);
if(user.Blocked)
{
user.Blocked = false;
}
else
{
user.Blocked = true;
}
context.SaveChanges();
return RedirectToAction("ViewUsers", "Admin");
}
How can I test it using Moq?
Also I've no idea how to test this method:
[Authorize(Roles = "admin")]
public ActionResult ViewUsers()
{
var context = new ApplicationDbContext();
var users = from u in context.Users
where u.Roles.Any(r => r.Role.Name == "user")
select u;
ViewBag.Users = users;
return View(users.ToList());
}
I've tried to mock ApplicationDbContext or UserManager, but I think I don't know something important. Thank you.

Related

ASP.NET Core Web API - Put action with DTO didn't update the existing object

I've been trying to write Put (update) action without the id property, by using DTO. But every time I'm trying I'm getting the existing object and not the updated one, and I can't figure out why and how to change it so it will work.
My repository:
public User Update(Guid id, User user)
{
var userToUpdate=_context.Users.FirstOrDefault(x => x.Id == id);
_context.Entry(userToUpdate).State = EntityState.Modified;
_context.SaveChanges();
return userToUpdate;
}
My DTO:
public class UserPostDTO
{
public UserPostDTO()
{
}
public UserPostDTO(User user)
{
UserName= user.UserName;
Password= user.Password;
LastLogin= user.LastLogin;
}
[StringLength(255)]
public string UserName { get; set; } = null!;
[StringLength(255)]
public string Password { get; set; } = null!;
[Column(TypeName = "datetime")]
public DateTime? LastLogin { get; set; }
public User ToPostUser()
{
var user = new User();
user.UserName = UserName;
user.Password = Password;
user.LastLogin = LastLogin;
return user;
}
}
My Controller:
public class UserController : ControllerBase
{
private readonly IUserRepository _userRepository;
public UserController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
[HttpPut("{id}")]
public IActionResult Put(Guid id, [FromBody] UserPostDTO user)
{
_userRepository.Update(id, user.ToPostUser());
return Ok();
}
Didn't see you updating the User object with the new value.
Probably this is what you need:
public User Update(Guid id, User user)
{
var userToUpdate = _context.Users.FirstOrDefault(x => x.Id == id)
.AsNoTracking();
if (userToUpdate == null)
{
// Handle ID is not existed
throw new ArgumentNullException("ID is not existed");
}
user.Id = userToUpdate.Id;
_context.Entry(user).State = EntityState.Modified;
_context.SaveChanges();
return user;
}

How to give custom implementation of UpdateAsync method of asp.net identity?

I am doing custom asp.net identity and not using asp.net inbuilt tables.I have successfully created user with implementing custom CreateAsync
Now i want to update user with new encrypted password and so i am not getting how to provide custom implementation of UpdateAsync method.
This is my table:
User : Id,Name,EmailId,Password,Statistics,Salary
Model:
public class UserModel : IUser
{
public string Id { get; set; }
public string Name { get; set; }
public string EmailId { get; set; }
public string Password { get; set; }
public int Salary { get; set; }
}
My custom class which implements IUserstore:
public class UserStore : IUserStore<UserModel>, IUserPasswordStore<UserModel>
{
private readonly MyEntities _dbContext;
private readonly HttpContext _httpContext;
// How to implement this method for updating only user password
public Task UpdateAsync(UserModel user)
{
throw new NotImplementedException();
}
public Task CreateAsync(UserModel user)
{
return Task.Factory.StartNew(() =>
{
HttpContext.Current = _httpContext ?? HttpContext.Current;
var user = _dbContext.User.Create();
user.Name = user.Name;
user.EmailId = user.EmailId;
user.EmailAddress = user.Email;
user.Password = user.Password;
_dbContext.Users.Add(dbUser);
_dbContext.SaveChanges();
});
}
public Task SetPasswordHashAsync(UserModel user, string passwordHash)
{
return Task.Factory.StartNew(() =>
{
HttpContext.Current = _httpContext ?? HttpContext.Current;
var userObj = GetUserObj(user);
if (userObj != null)
{
userObj.Password = passwordHash;
_dbContext.SaveChanges();
}
else
user.Password = passwordHash;
});
}
public Task<string> GetPasswordHashAsync(UserModel user)
{
//other code
}
}
Controller:
public class MyController : ParentController
{
public MyController()
: this(new UserManager<UserModel>(new UserStore(new MyEntities())))
{
}
public UserManager<UserModel> UserManager { get; private set; }
[HttpPost]
public async Task<JsonResult> SaveUser(UserModel userModel)
{
IdentityResult result = null;
if (userModel.Id > 0) //want to update user with new encrypted password
result = await UserManager.UpdateAsync(user);
else
result = await UserManager.CreateAsync(userModel.EmailId, userModel.Password);
}
}
Not sure if this is what your looking for...
public Task UpdateAsync(UserModel model)
{
var user = _dbContext.User.Find(x => x.id == model.id);
user.Password = model.Password;
_dbContext.SaveChanges();
return Task.CompletedTask;
}
It Will get the specific record and Update the password and then save the record.
Edit
Since the password is not getting encrypted i added code to take that string and leave the model as it is, this extension method will encrypt the value of password, i have not test this but i am sure it will work.
user.Password = model.Password.EncryptPassword(EncryptKey);
Extension methods to encrypt password
Sometime back, I created a complete wrapper over .NET Identity and code can be found here. It might be helpful for you. You can also find nuget here. I also explained the library in a blog here.

ChangepasswordAsync not work

I used this code for admin panel to changed password of customer by manager,I did not get any Error. but password not changed,
i got my coed from this
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult ChangeUserPassword(SetPasswordViewModel model,string userId)
{
if (ModelState.IsValid)
{
//var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
ApplicationUser appUser = db.Users.Find(userId);
var result = UserManager.ChangePasswordAsync(appUser, model.NewPassword);
if (result.IsCompleted)
{
var user = UserManager.FindById(User.Identity.GetUserId());
//var user = db.Users.Find(userId);
if (user != null)
{
//await SignInManager<,>.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageController.ManageMessageId.SetPasswordSuccess });
}
// AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
and in controller for change use this
public async Task<IdentityResult> ChangePasswordAsync(ApplicationUser Appuser, string newPassword)
{
var store = this.Store as Microsoft.AspNet.Identity.IUserPasswordStore<ApplicationUser,string>;
if (store == null)
{
var errors = new string[]
{
"Current UserStore doesn't implement IUserPasswordStore"
};
return IdentityResult.Failed(errors);
// return Task.FromResult(new IdentityResult(errors) { Succeeded = false });
}
var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
await store.SetPasswordHashAsync(Appuser, newPasswordHash);
await store.UpdateAsync(Appuser);
//return await Task.FromResult<IdentityResult>(IdentityResult.Success);
return IdentityResult.Success;
}
}
whats my mistake?
after update answer i use this method instead
[HttpPost]
public async Task<IdentityResult> ChangePasswordAsync(ApplicationUser appuserId, string newPassword)
{
ApplicationDbContext db = new ApplicationDbContext();
//var userr =await db.Users.FirstOrDefaultAsync(x => x.Id == appuserId.Id);
var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
db.Entry(Users).State = EntityState.Modified;
if (appuserId != null) appuserId.PasswordHash = newPasswordHash;
db.SaveChanges();
return IdentityResult.Success;
}
but had the same problem again
in IdentityModels.cs i had
public DbSet<CommonAction> CommonActions { get; set; }
public DbSet<PublicContent> PublicContents { get; set; }
public DbSet<MainSubject> MainSubjects { get; set; }
public DbSet<EducationLevel> EducationLevels { get; set; }
public DbSet<TimePeriod> TimePeriods { get; set; }
public DbSet<HelpType> HelpTypes { get; set; }
public DbSet<FinancialSupport> FinancialSupports { get; set; }
public DbSet<LinksStatistic> LinksStatistics { get; set; }
public DbSet<SlideOfCommonAction> SlideOfCommonActions { get; set; }
in normal model IdentityModel User is not register as DbSet
()
You're not awaiting ChangePasswordAsync, you're just checking if it's completed or not. Which may result in the View being returned before the password has been changed.
Then you should try to use async/await all the way, if possible. That means that your action should be async as well.
[HttpPost]
[ValidateAntiForgeryToken]
public virtual async Task<ActionResult> ChangeUserPassword(SetPasswordViewModel model,string userId)
{
if (ModelState.IsValid)
{
//var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
ApplicationUser appUser = db.Users.Find(userId);
// await the result.
var result = await UserManager.ChangePasswordAsync(appUser, model.NewPassword);
if (result.Succeeded) // Or whatever you're expecting.
{
var user = UserManager.FindById(User.Identity.GetUserId());
//var user = db.Users.Find(userId);
if (user != null)
{
//await SignInManager<,>.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageController.ManageMessageId.SetPasswordSuccess });
}
// AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
Update after comments:
The entity type DbSet`1 is not part of the model for the current context
db.Entry(Users).State = EntityState.Modified;
This is when EF cannot connect your model to an entity in the context. Without seeing the code, it's hard to say exactly why. But it could be that your're missing a DbSet in your context.
Have you configured your UserManager correctly? Does the other UserManager actions work?
Try debugging and see where it crashes.

Testing property used on mvc controller after injection

public class MyUser: IIdentity, IMyUser{
// ommited for abbrev.
}
public interface IMyUser
{
int Id { get; set; }
int? CompanyId { get; set; }
}
inside MyController I'm using MyUser besides others to populate
comboboxes
public ActionResult Details(int? subsidId = null, int? req = null)
{
...
MyUser user = this.User.GetInfo();
var obj1 = ... // ommited on purpose for abbrev.
populateCombos(subsidId, user.CompanyId, req);
}
I'm getting exception on this line populateCombos cause user object is always null.
Inside same controller I'm injecting interface which IMyUser implements
[Inject]
public IMyUser MyUser { get; set; }
this property is correctly binded using ninject (like others in my app)
kernel.Bind<IMyUser>().To<MyUser>().InRequestScope();
now on testing project I'm initializing controller with mocking requested dependencies
[SetUp]
public void Setup()
{
_controller = new MyController(){
... repositories....
MyUser = MockMyUser()
}
}
private IMyUser MockMyUser()
{
var u = new Mock<IMyUser>();
u.SetupGet(x => x.Id).Returns(1);
u.SetupGet(x => x.CompanyId).Returns(99);
return u.Object;
}
and inside test method I wrote simple test
[Test]
public void CanDoDetails()
{
ViewResult res = this.controller.Details(1, 2) as ViewResult;
var model = result.Model as MyModel;
Assert.IsNotNull(model);
}
Question is:
why I'm getting this dependency (MyUser inside MyController as
null) cause it's injected properly? What I'm doing wrong?
Update:
public static MyUser GetInfo(this IPrincipal principal)
{
if (principal != null)
{
return principal.Identity as MyUser;
}
return null;
}
Update 2:
Based on Nkosi answer bellow I make following changes
public interface IMyUser : IIdentity { ... }
and inside Details ActionMethod controller
IMyUser user = this.User.GetInfo();
and in test method under
[SetUp]
public void Setup()
{
var mockUser = MockMyUser();
string[] roles = new[] { "Admin", "SuperUser" };
_controller = new MyController()
{
....
MyUser = this.MockMyUser(),
ControllerContext = new ControllerContext() {
Controller = _controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
}
}
}
but I'm still getting `IMyUser user = this.User.GetInfo();`
`this.User` is still null.
p.s. I also changed GetInfo to return IMyUser instead of MyUser.
What about mocking controller also?
Mock<MyController> mockController = new Mock<MyController>();
mockController.SetupGet(t => t.MyUser).Returns(MockMyUser());
then you can access your controller via mockController.Object and try your test.
My advice would also be to make IMyUser inherit from IIdentity
public class MyUser: IMyUser {
// ommited for abbrev.
}
public interface IMyUser: IIdentity {
int Id { get; set; }
int? CompanyId { get; set; }
}
For your unit test to work if you are using the Controller.User { get; } is to create a mock/Fake user for the controller. How ever in order to get access to the User, which is read-only, you have to create a mock HttpContext. yuck. Luckily you only want access to the User anyway.
private class MockHttpContext : HttpContextBase {
private readonly IPrincipal user;
public MockHttpContext(IIdentity identity , string[] roles = null) {
var principal = new GenericPrincipal(identity, roles ?? new string[] { });
user = principal;
}
public override IPrincipal User {
get {
return user;
}
set {
base.User = value;
}
}
}
You can setup the principal to suit your authentication setup with what ever claims you apply at runtime. This is just an example.
[SetUp]
public void Setup()
{
string[] roles = new[] { "Admin", "SuperUser" };
var mockUser = MockMyUser();
_controller = new MyController(){
... repositories....
MyUser = mockUser
};
_controller.ControllerContext = new ControllerContext() {
Controller = _controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
};
}
private IMyUser MockMyUser()
{
var u = new Mock<IMyUser>();
u.Setup(x => x.Name).Returns("username#test.io");
u.Setup(x => x.Id).Returns(1);
u.Setup(x => x.CompanyId).Returns(99);
return u.Object;
}
This should now allow
public static IMyUser GetInfo(this IPrincipal principal) {
if (principal != null) {
return principal.Identity as IMyUser;
}
return null;
}
To return the principal.Identity as IMyUser to not be null.
UPDATE
I recreated a minimal version of your test using what I provided you above and was able to test it and pass.
[TestClass]
public class MyUserDependentControllerTest {
[TestMethod]
public void CanDoDetails() {
//Arrange
string[] roles = new[] { "Admin", "SuperUser" };
var u = new Mock<IMyUser>();
u.Setup(x => x.Name).Returns("username#test.io");
u.Setup(x => x.Id).Returns(1);
u.Setup(x => x.CompanyId).Returns(99).Verifiable();
var mockUser = u.Object;
var controller = new MyController() {
//... repositories....
MyUser = mockUser
};
controller.ControllerContext = new ControllerContext() {
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
};
//Act
var result = controller.Details(1, 2);
//Assert
var viewResult = result as ViewResult;
Assert.IsNotNull(viewResult);
var model = viewResult.Model as MyModel;
Assert.IsNotNull(model);
u.Verify();
}
public class MyController : Controller {
public ActionResult Details(int? subsidId = null, int? req = null) {
//...
var user = this.User.GetInfo();
//
populateCombos(subsidId, user.CompanyId, req);
//this is just for matching test expectations
var model = new MyModel();
return View(model);
}
private void populateCombos(int? subsidId, int? nullable, int? req) {
//Empty as I have no clue what happens in here
}
public IMyUser MyUser { get; set; }
}
public class MyModel { }
}

How best to override the authentication in MVC 5?

I have a project in which does not have registration. Administrator registers users in admin. The project does not have the roles, I have only one type of user. I do not need "AspNetRoles", "AspNetUserClaims", "AspNewUserLogins", "AspNetUserRoles". And in the table "AspNetUsers" I need only "Id", "Email", "Password" and a few custom properties. What is the best way to implement this in mvc 5?
To Add more columns/fields to AspNetUsers you need to add those in Identity Model adn do Data Migration using -update database command
you can also control the Keys and Table Name by Overriding as below
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>().ToTable("MyUsers").Property(p => p.Id).HasColumnName("UserId");
modelBuilder.Entity<ApplicationUser>().ToTable("MyUsers").Property(p => p.Id).HasColumnName("UserId");
modelBuilder.Entity<IdentityUserRole>().ToTable("MyUserRoles");
modelBuilder.Entity<IdentityUserLogin>().ToTable("MyUserLogins");
modelBuilder.Entity<IdentityUserClaim>().ToTable("MyUserClaims");
modelBuilder.Entity<IdentityRole>().ToTable("MyRoles");
}
When you are using ASPNET Schema for user registration, I don't think you can avoid Claims,Roles and other tables, but you can just ignore those.
Update
In order to avoid Roles and Claims in ASPNET membership
First of all create a MVC 5 application. Then implement IUser,
public class ApplicationUser : IUser
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
}
public ApplicationUser(string userName): this()
{
UserName = userName;
}
public virtual string Id { get; set; }
public virtual string PasswordHash { get; set; }
public virtual string SecurityStamp { get; set; }
public virtual string UserName { get; set; }
}
Next we need a DbContet to store the Users,
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public virtual IDbSet<ApplicationUser> Users { get; set; }
}
and then we need to implement IUserStore, IUserPasswordStore and IUserSecurityStampStore,
public class MyUserStore : IUserStore<ApplicationUser>, IUserPasswordStore<ApplicationUser>, IUserSecurityStampStore<ApplicationUser>
{
UserStore<IdentityUser> userStore = new UserStore<IdentityUser>(new ApplicationDbContext());
public MyUserStore()
{
}
public Task CreateAsync(ApplicationUser user)
{
var context = userStore.Context as ApplicationDbContext;
context.Users.Add(user);
context.Configuration.ValidateOnSaveEnabled = false;
return context.SaveChangesAsync();
}
public Task DeleteAsync(ApplicationUser user)
{
var context = userStore.Context as ApplicationDbContext;
context.Users.Remove(user);
context.Configuration.ValidateOnSaveEnabled = false;
return context.SaveChangesAsync();
}
public Task<ApplicationUser> FindByIdAsync(string userId)
{
var context = userStore.Context as ApplicationDbContext;
return context.Users.Where(u => u.Id.ToLower() == userId.ToLower()).FirstOrDefaultAsync();
}
public Task<ApplicationUser> FindByNameAsync(string userName)
{
var context = userStore.Context as ApplicationDbContext;
return context.Users.Where(u => u.UserName.ToLower() == userName.ToLower()).FirstOrDefaultAsync();
}
public Task UpdateAsync(ApplicationUser user)
{
var context = userStore.Context as ApplicationDbContext;
context.Users.Attach(user);
context.Entry(user).State = EntityState.Modified;
context.Configuration.ValidateOnSaveEnabled = false;
return context.SaveChangesAsync();
}
public void Dispose()
{
userStore.Dispose();
}
public Task<string> GetPasswordHashAsync(ApplicationUser user)
{
var identityUser = ToIdentityUser(user);
var task = userStore.GetPasswordHashAsync(identityUser);
SetApplicationUser(user, identityUser);
return task;
}
public Task<bool> HasPasswordAsync(ApplicationUser user)
{
var identityUser = ToIdentityUser(user);
var task = userStore.HasPasswordAsync(identityUser);
SetApplicationUser(user, identityUser);
return task;
}
public Task SetPasswordHashAsync(ApplicationUser user, string passwordHash)
{
var identityUser = ToIdentityUser(user);
var task = userStore.SetPasswordHashAsync(identityUser, passwordHash);
SetApplicationUser(user, identityUser);
return task;
}
public Task<string> GetSecurityStampAsync(ApplicationUser user)
{
var identityUser = ToIdentityUser(user);
var task = userStore.GetSecurityStampAsync(identityUser);
SetApplicationUser(user, identityUser);
return task;
}
public Task SetSecurityStampAsync(ApplicationUser user, string stamp)
{
var identityUser = ToIdentityUser(user);
var task = userStore.SetSecurityStampAsync(identityUser, stamp);
SetApplicationUser(user, identityUser);
return task;
}
private static void SetApplicationUser(ApplicationUser user, IdentityUser identityUser)
{
user.PasswordHash = identityUser.PasswordHash;
user.SecurityStamp = identityUser.SecurityStamp;
user.Id = identityUser.Id;
user.UserName = identityUser.UserName;
}
private IdentityUser ToIdentityUser(ApplicationUser user)
{
return new IdentityUser
{
Id = user.Id,
PasswordHash = user.PasswordHash,
SecurityStamp = user.SecurityStamp,
UserName = user.UserName
};
}
}
For password hash and security stamp, I am using the UserStore's implementation for making thing simpler. Finally we just need to change AccountController's constructor to leverage our MyUserStore implementation,
public AccountController()
: this(new UserManager<ApplicationUser>(new MyUserStore()))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
To Drop unnecessary columns in Users Table. You can try somethign like this
public partial class ModifyUser: DbMigration
{
public override void Up()
{
AddColumn("dbo.AspNetUsers", "NewField", c => c.String());
}
public override void Down()
{
DropColumn("dbo.AspNetUsers", "NewColumn");
}
}
Then in packageManager run PM> update-database

Categories

Resources