Allow password validation failures to propogate (#6034)

This commit is contained in:
Hao Kung 2019-05-16 09:38:30 -07:00 committed by GitHub
parent ae4157d086
commit 394eb3ca13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 11 deletions

View File

@ -2517,15 +2517,21 @@ namespace Microsoft.AspNetCore.Identity
protected async Task<IdentityResult> ValidatePasswordAsync(TUser user, string password)
{
var errors = new List<IdentityError>();
var isValid = true;
foreach (var v in PasswordValidators)
{
var result = await v.ValidateAsync(this, user, password);
if (!result.Succeeded)
{
errors.AddRange(result.Errors);
if (result.Errors.Any())
{
errors.AddRange(result.Errors);
}
isValid = false;
}
}
if (errors.Count > 0)
if (!isValid)
{
Logger.LogWarning(14, "User {userId} password validation failed: {errors}.", await GetUserIdAsync(user), string.Join(";", errors.Select(e => e.Code)));
return IdentityResult.Failed(errors.ToArray());

View File

@ -44,12 +44,15 @@ namespace Microsoft.AspNetCore.Identity.Test
/// <summary>
/// Asserts that the result has not Succeeded and that first error matches error's code and Description.
/// </summary>
public static void IsFailure(IdentityResult result, IdentityError error)
public static void IsFailure(IdentityResult result, IdentityError error = null)
{
Assert.NotNull(result);
Assert.False(result.Succeeded);
Assert.Equal(error.Description, result.Errors.First().Description);
Assert.Equal(error.Code, result.Errors.First().Code);
if (error != null)
{
Assert.Equal(error.Description, result.Errors.FirstOrDefault()?.Description);
Assert.Equal(error.Code, result.Errors.FirstOrDefault()?.Code);
}
}
/// <summary>
@ -70,4 +73,4 @@ namespace Microsoft.AspNetCore.Identity.Test
}
}
}
}
}

View File

@ -163,6 +163,39 @@ namespace Microsoft.AspNetCore.Identity.Test
}
}
private class EmptyBadValidator : IUserValidator<TUser>,
IPasswordValidator<TUser>
{
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
{
return Task.FromResult(IdentityResult.Failed());
}
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
{
return Task.FromResult(IdentityResult.Failed());
}
}
/// <summary>
/// Test.
/// </summary>
/// <returns>Task</returns>
[Fact]
public async Task PasswordValidatorWithNoErrorsCanBlockAddPassword()
{
if (ShouldSkipDbTests())
{
return;
}
var manager = CreateManager();
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new EmptyBadValidator());
IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"));
}
/// <summary>
/// Test.
/// </summary>
@ -497,13 +530,78 @@ namespace Microsoft.AspNetCore.Identity.Test
}
var manager = CreateManager();
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new AlwaysBadValidator());
manager.PasswordValidators.Add(new EmptyBadValidator());
manager.PasswordValidators.Add(new AlwaysBadValidator());
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
var result = await manager.AddPasswordAsync(user, "pwd");
IdentityResultAssert.IsFailure(result, AlwaysBadValidator.ErrorMessage);
Assert.Equal(2, result.Errors.Count());
Assert.Single(result.Errors);
}
/// <summary>
/// Test.
/// </summary>
/// <returns>Task</returns>
[Fact]
public async Task PasswordValidatorWithNoErrorsCanBlockChangePassword()
{
if (ShouldSkipDbTests())
{
return;
}
var manager = CreateManager();
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"));
}
/// <summary>
/// Test.
/// </summary>
/// <returns>Task</returns>
[Fact]
public async Task PasswordValidatorWithNoErrorsCanBlockCreateUser()
{
if (ShouldSkipDbTests())
{
return;
}
var manager = CreateManager();
var user = CreateTestUser();
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.CreateAsync(user, "password"));
}
/// <summary>
/// Test.
/// </summary>
/// <returns>Task</returns>
[Fact]
public async Task PasswordValidatorWithNoErrorsCanBlockResetPasswordWithStaticTokenProvider()
{
if (ShouldSkipDbTests())
{
return;
}
var manager = CreateManager();
manager.RegisterTokenProvider("Static", new StaticTokenProvider());
manager.Options.Tokens.PasswordResetTokenProvider = "Static";
var user = CreateTestUser();
const string password = "password";
const string newPassword = "newpassword";
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
var stamp = await manager.GetSecurityStampAsync(user);
Assert.NotNull(stamp);
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword));
Assert.True(await manager.CheckPasswordAsync(user, password));
Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
}
/// <summary>

View File

@ -982,11 +982,21 @@ namespace Microsoft.AspNetCore.Identity.Test
// TODO: Can switch to Mock eventually
var manager = MockHelpers.TestUserManager(new EmptyStore());
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new BadPasswordValidator<PocoUser>());
manager.PasswordValidators.Add(new BadPasswordValidator<PocoUser>(true));
IdentityResultAssert.IsFailure(await manager.CreateAsync(new PocoUser(), "password"),
BadPasswordValidator<PocoUser>.ErrorMessage);
}
[Fact]
public async Task PasswordValidatorWithoutErrorsBlocksCreate()
{
// TODO: Can switch to Mock eventually
var manager = MockHelpers.TestUserManager(new EmptyStore());
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new BadPasswordValidator<PocoUser>());
IdentityResultAssert.IsFailure(await manager.CreateAsync(new PocoUser(), "password"));
}
[Fact]
public async Task ResetTokenCallNoopForTokenValueZero()
{
@ -1175,10 +1185,22 @@ namespace Microsoft.AspNetCore.Identity.Test
{
public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad." };
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
private IdentityResult badResult;
public BadPasswordValidator(bool includeErrorMessage = false)
{
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
if (includeErrorMessage)
{
badResult = IdentityResult.Failed(ErrorMessage);
}
else
{
badResult = IdentityResult.Failed();
}
}
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password)
=> Task.FromResult(badResult);
}
private class EmptyStore :