Add CheckPasswordSignIn method

This commit is contained in:
Hao Kung 2016-08-08 13:43:30 -07:00
parent ddaab5f549
commit fe60304df5
2 changed files with 110 additions and 26 deletions

View File

@ -241,32 +241,10 @@ namespace Microsoft.AspNetCore.Identity
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user);
if (error != null)
{
return error;
}
if (await IsLockedOut(user))
{
return await LockedOut(user);
}
if (await UserManager.CheckPasswordAsync(user, password))
{
await ResetLockout(user);
return await SignInOrTwoFactorAsync(user, isPersistent);
}
Logger.LogWarning(2, "User {userId} failed to provide the correct password.", await UserManager.GetUserIdAsync(user));
if (UserManager.SupportsUserLockout && lockoutOnFailure)
{
// If lockout is requested, increment access failed count which might lock out the user
await UserManager.AccessFailedAsync(user);
if (await UserManager.IsLockedOutAsync(user))
{
return await LockedOut(user);
}
}
return SignInResult.Failed;
var attempt = await CheckPasswordSignInAsync(user, password, lockoutOnFailure);
return attempt.Succeeded
? await SignInOrTwoFactorAsync(user, isPersistent)
: attempt;
}
/// <summary>
@ -291,6 +269,52 @@ namespace Microsoft.AspNetCore.Identity
return await PasswordSignInAsync(user, password, isPersistent, lockoutOnFailure);
}
/// <summary>
/// Attempts a password sign in for a user.
/// </summary>
/// <param name="user">The user to sign in.</param>
/// <param name="password">The password to attempt to sign in with.</param>
/// <param name="lockoutOnFailure">Flag indicating if the user account should be locked if the sign in fails.</param>
/// <returns>The task object representing the asynchronous operation containing the <see name="SignInResult"/>
/// for the sign-in attempt.</returns>
/// <returns></returns>
public virtual async Task<SignInResult> CheckPasswordSignInAsync(TUser user, string password, bool lockoutOnFailure)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user);
if (error != null)
{
return error;
}
if (await IsLockedOut(user))
{
return await LockedOut(user);
}
if (await UserManager.CheckPasswordAsync(user, password))
{
await ResetLockout(user);
return SignInResult.Success;
}
Logger.LogWarning(2, "User {userId} failed to provide the correct password.", await UserManager.GetUserIdAsync(user));
if (UserManager.SupportsUserLockout && lockoutOnFailure)
{
// If lockout is requested, increment access failed count which might lock out the user
await UserManager.AccessFailedAsync(user);
if (await UserManager.IsLockedOutAsync(user))
{
return await LockedOut(user);
}
}
return SignInResult.Failed;
}
/// <summary>
/// Returns a flag indicating if the current client browser has been remembered by two factor authentication
/// for the user attempting to login, as an asynchronous operation.

View File

@ -136,6 +136,37 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Verify();
}
[Fact]
public async Task CheckPasswordSignInReturnsLockedOutWhenLockedOut()
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = SetupUserManager(user);
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Value).Returns(identityOptions);
var claimsFactory = new UserClaimsPrincipalFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object);
var logStore = new StringBuilder();
var logger = MockHelpers.MockILogger<SignInManager<TestUser>>(logStore);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object);
// Act
var result = await helper.CheckPasswordSignInAsync(user, "bogus", false);
// Assert
Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut);
Assert.True(logStore.ToString().Contains($"User {user.Id} is currently locked out."));
manager.Verify();
}
private static Mock<UserManager<TestUser>> SetupUserManager(TestUser user)
{
var manager = MockHelpers.MockUserManager<TestUser>();
@ -534,9 +565,11 @@ namespace Microsoft.AspNetCore.Identity.Test
var helper = SetupSignInManager(manager.Object, context.Object, logStore);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
var checkResult = await helper.CheckPasswordSignInAsync(user, "bogus", false);
// Assert
Assert.False(result.Succeeded);
Assert.False(checkResult.Succeeded);
Assert.True(logStore.ToString().Contains($"User {user.Id} failed to provide the correct password."));
manager.Verify();
context.Verify();
@ -587,6 +620,33 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Verify();
}
[Fact]
public async Task CheckPasswordSignInFailsWithWrongPasswordCanAccessFailedAndLockout()
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = SetupUserManager(user);
var lockedout = false;
manager.Setup(m => m.AccessFailedAsync(user)).Returns(() =>
{
lockedout = true;
return Task.FromResult(IdentityResult.Success);
}).Verifiable();
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable();
var context = new Mock<HttpContext>();
var helper = SetupSignInManager(manager.Object, context.Object);
// Act
var result = await helper.CheckPasswordSignInAsync(user, "bogus", true);
// Assert
Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut);
manager.Verify();
}
[Theory]
[InlineData(true)]
[InlineData(false)]