У меня есть служба, которая отвечает за изменение пароля пользователя при использовании UserManager
, и все работает нормально, но когда я хочу написать несколько тестов, происходит сбой при методе CheckPassword
, который в основном проверяет, является ли current (old password)
правильный
myService:
public async bool TryChangePassword(User user, string OldPassword, string NewPassword)
{
(...)
// it returns false
var checkOldPassword = await _userManager.CheckPasswordAsync(user, OldPassword);
if (!checkOldPassword)
{
return false;
}
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
var result = await _userManager.ResetPasswordAsync(user, token, NewPassword);
return result.Succeeded;
}
myTests:
private readonly UserManager<User> _userManager;
[Fact]
public void password_change_attempt_1()
{
var service = new myService(_context, _userManager);
var user = new User("john");
var register = _userManager.CreateAsync(user, "123456");
_context.SaveChanges();
Assert.True(_context.Users.Any());
var result = service.TryChangePassword(user, "123456", "newPassword");
}
, но по какой-то причине он не работает по адресу:
var checkOldPassword = await _userManager.CheckPasswordAsync (пользователь, OldPassword);
возвращает false, но, как вы видите, при условии, что пароль верен, возможно, что-то не так с насмешливым менеджером пользователя
Вот как мне создать макет UserManager
в Tests'
конструкторе
public Tests()
{
var o = new DbContextOptionsBuilder<Context>();
o.UseInMemoryDatabase(Guid.NewGuid().ToString());
_context = new Context(o.Options);
_context.Database.EnsureCreated();
var userStore = new MockUserStore(_context);
_userManager = new MockUserManager(userStore,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<User>>().Object,
new IUserValidator<User>[0],
new IPasswordValidator<User>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<User>>>().Object);
}
public class MockUserManager : UserManager<User>
{
public MockUserManager(IUserStore<User> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<User> passwordHasher, IEnumerable<IUserValidator<User>> userValidators,
IEnumerable<IPasswordValidator<User>> passwordValidators, ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<User>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public override Task<IdentityResult> CreateAsync(User user)
{
this.Store.CreateAsync(user, new CancellationToken());
return Task.FromResult(IdentityResult.Success);
}
}
public class MockUserStore : IUserStore<User>, IUserPasswordStore<User>
{
public readonly Context _ctx;
public MockUserStore(Context ctx)
{
_ctx = ctx;
}
public Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken)
{
_ctx.Users.Add(user);
return Task.FromResult(IdentityResult.Success);
}
public Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Task<User> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<User> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetPasswordHashAsync(User user, CancellationToken cancellationToken)
{
return Task.FromResult<string>(user.PasswordHash);
}
public Task<string> GetUserIdAsync(User user, CancellationToken cancellationToken)
{
return Task.FromResult<string>(user.Id);
}
public Task<string> GetUserNameAsync(User user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<bool> HasPasswordAsync(User user, CancellationToken cancellationToken)
{
return Task.FromResult<bool>(!String.IsNullOrEmpty(user.PasswordHash));
}
public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(User user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
Полагаю, это можно исправить здесь:
public Task<IdentityResult> CreateAsync(User user, CancellationToken cancellationToken)
{
_ctx.Users.Add(user);
return Task.FromResult(IdentityResult.Success);
}
добавив что-то вроде
user.PasswordHash = generateHash(password)
но как я могу узнать, сколько итераций использует ASP.NET Core Identity?
https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing?view=aspnetcore-2.2
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));