Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Lockout Functionality: Add Test for AccessFailedAsync Incrementing Count #60350

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions src/Identity/Extensions.Core/src/LockoutOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ public class LockoutOptions
/// </summary>
/// <value>The <see cref="TimeSpan"/> a user is locked out for when a lockout occurs.</value>
public TimeSpan DefaultLockoutTimeSpan { get; set; } = TimeSpan.FromMinutes(5);

/// <summary>
/// Specifies whether the lockout should be permanent.
/// If true, the user is locked out.
/// </summary>
public bool PermanentLockout { get; set; }
}
2 changes: 2 additions & 0 deletions src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable
*REMOVED*Microsoft.AspNetCore.Identity.UserLoginInfo.UserLoginInfo(string! loginProvider, string! providerKey, string? displayName) -> void
Microsoft.AspNetCore.Identity.UserLoginInfo.UserLoginInfo(string! loginProvider, string! providerKey, string? providerDisplayName) -> void
Microsoft.AspNetCore.Identity.LockoutOptions.PermanentLockout.get -> bool
Microsoft.AspNetCore.Identity.LockoutOptions.PermanentLockout.set -> void
21 changes: 18 additions & 3 deletions src/Identity/Extensions.Core/src/UserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1818,15 +1818,30 @@ public virtual async Task<IdentityResult> AccessFailedAsync(TUser user)
var store = GetUserLockoutStore();
ArgumentNullThrowHelper.ThrowIfNull(user);

// If this puts the user over the threshold for lockout, lock them out and reset the access failed count
// If PermanentLockout is enabled, lock the user indefinitely
if (Options.Lockout.PermanentLockout)
{
Logger.LogDebug(LoggerEventIds.UserLockedOut, "User is permanently locked out.");
await store.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue, CancellationToken).ConfigureAwait(false);
return await UpdateUserAsync(user).ConfigureAwait(false);
}

// Increment access failed count
var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken).ConfigureAwait(false);
if (count < Options.Lockout.MaxFailedAccessAttempts)
{
return await UpdateUserAsync(user).ConfigureAwait(false);
}

Logger.LogDebug(LoggerEventIds.UserLockedOut, "User is locked out.");
await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan),
CancellationToken).ConfigureAwait(false);

// Set the lockout time based on configuration.
var now = DateTimeOffset.UtcNow;
DateTimeOffset lockoutEnd = Options.Lockout.DefaultLockoutTimeSpan == TimeSpan.MaxValue
? DateTimeOffset.MaxValue
: now.Add(Options.Lockout.DefaultLockoutTimeSpan);

await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken).ConfigureAwait(false);
await store.ResetAccessFailedCountAsync(user, CancellationToken).ConfigureAwait(false);
return await UpdateUserAsync(user).ConfigureAwait(false);
}
Expand Down
36 changes: 36 additions & 0 deletions src/Identity/test/Identity.Test/UserManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,42 @@
IdentityResultAssert.IsSuccess(await manager.ResetAccessFailedCountAsync(user));
}

[Fact]
public async Task AccessFailedAsyncIncrementsAccessFailedCount()
{
// Arrange
var user = new PocoUser() { UserName = "testuser" };
var store = new Mock<IUserLockoutStore<PocoUser>>();
int failedCount = 1;

store.Setup(x => x.SupportsUserLockout).Returns(true);

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

store.Setup(x => x.GetAccessFailedCountAsync(user, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => failedCount);

store.Setup(x => x.IncrementAccessFailedCountAsync(user, It.IsAny<CancellationToken>()))
.Callback(() => failedCount++)
.ReturnsAsync(() => failedCount);

store.Setup(x => x.UpdateAsync(user, It.IsAny<CancellationToken>()))
.ReturnsAsync(IdentityResult.Success);

var manager = MockHelpers.TestUserManager(store.Object);
manager?.Options?.Lockout?.PermanentLockout = false;

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

// Act
var result = await manager.AccessFailedAsync(user);

// Assert
Assert.NotNull(result);
Assert.True(result.Succeeded, "AccessFailedAsync should return success.");

store.Verify(x => x.IncrementAccessFailedCountAsync(user, It.IsAny<CancellationToken>()), Times.Once);

var newFailedCount = await manager.GetAccessFailedCountAsync(user);
Assert.Equal(2, newFailedCount);
}

[Fact]
public async Task ManagerPublicNullChecks()
{
Expand Down
Loading