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

View File

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

View File

@ -501,6 +501,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert // Assert
context.VerifyAll(); context.VerifyAll();
response.VerifyAll(); response.VerifyAll();
contextAccessor.VerifyAll();
claimsFactory.VerifyAll();
} }
[Fact] [Fact]
@ -528,6 +530,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert // Assert
Assert.Equal(SignInStatus.Failure, result); Assert.Equal(SignInStatus.Failure, result);
manager.VerifyAll(); manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
} }
[Fact] [Fact]
@ -552,6 +556,8 @@ namespace Microsoft.AspNet.Identity.Test
// Assert // Assert
Assert.Equal(SignInStatus.Failure, result); Assert.Equal(SignInStatus.Failure, result);
manager.VerifyAll(); manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
} }
[Fact] [Fact]
@ -588,5 +594,86 @@ namespace Microsoft.AspNet.Identity.Test
manager.VerifyAll(); 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();
}
} }
} }