Add SignInManager CanSignIn

new hook which can be used to block sign in
Fixes https://github.com/aspnet/Identity/issues/129
This commit is contained in:
Hao Kung 2014-10-27 15:46:48 -07:00
parent 2c9f43a160
commit 556c15273e
3 changed files with 123 additions and 19 deletions

View File

@ -58,19 +58,19 @@ namespace Microsoft.AspNet.Identity
return await ClaimsFactory.CreateAsync(user);
}
//public virtual async Task<bool> CanSignInAsync(TUser user,
// CancellationToken cancellationToken = default(CancellationToken))
//{
// if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user, cancellationToken)))
// {
// return false;
// }
// if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user, cancellationToken)))
// {
// return false;
// }
// return true;
//}
public virtual async Task<bool> CanSignInAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user, cancellationToken)))
{
return false;
}
if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user, cancellationToken)))
{
return false;
}
return true;
}
public virtual async Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null,
CancellationToken cancellationToken = default(CancellationToken))
@ -94,6 +94,19 @@ namespace Microsoft.AspNet.Identity
return UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user, token);
}
private async Task<SignInStatus?> PreSignInCheck(TUser user, CancellationToken token)
{
if (!await CanSignInAsync(user, token))
{
return SignInStatus.NotAllowed;
}
if (await IsLockedOut(user, token))
{
return SignInStatus.LockedOut;
}
return null;
}
private Task ResetLockout(TUser user, CancellationToken token)
{
if (UserManager.SupportsUserLockout)
@ -129,9 +142,10 @@ namespace Microsoft.AspNet.Identity
public virtual async Task<SignInStatus> PasswordSignInAsync(TUser user, string password,
bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
{
if (await IsLockedOut(user, cancellationToken))
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return SignInStatus.LockedOut;
return error.Value;
}
if (await UserManager.CheckPasswordAsync(user, password, cancellationToken))
{
@ -233,9 +247,10 @@ namespace Microsoft.AspNet.Identity
{
return SignInStatus.Failure;
}
if (await IsLockedOut(user, cancellationToken))
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return SignInStatus.LockedOut;
return error.Value;
}
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code, cancellationToken))
{
@ -283,9 +298,10 @@ namespace Microsoft.AspNet.Identity
{
return SignInStatus.Failure;
}
if (await IsLockedOut(user, cancellationToken))
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return SignInStatus.LockedOut;
return error.Value;
}
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider);
}

View File

@ -8,6 +8,7 @@ namespace Microsoft.AspNet.Identity
Success,
LockedOut,
RequiresVerification,
NotAllowed,
Failure
}
}

View File

@ -501,6 +501,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
claimsFactory.VerifyAll();
}
[Fact]
@ -528,6 +530,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert
Assert.Equal(SignInStatus.Failure, result);
manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
}
[Fact]
@ -552,6 +556,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert
Assert.Equal(SignInStatus.Failure, result);
manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
}
[Fact]
@ -588,5 +594,86 @@ namespace Microsoft.AspNet.Identity.Test
manager.VerifyAll();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task CanRequireConfirmedEmailForPasswordSignIn(bool confirmed)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsEmailConfirmedAsync(user, CancellationToken.None)).ReturnsAsync(confirmed).Verifiable();
if (confirmed)
{
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
}
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
if (confirmed)
{
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == false), It.IsAny<ClaimsIdentity>())).Verifiable();
}
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedEmail = true;
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
// Assert
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task CanRequireConfirmedPhoneNumberForPasswordSignIn(bool confirmed)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsEmailConfirmedAsync(user, CancellationToken.None)).ReturnsAsync(confirmed).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
if (confirmed)
{
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == false), It.IsAny<ClaimsIdentity>())).Verifiable();
}
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedEmail = true;
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
// Assert
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();
}
}
}