103 lines
3.4 KiB
C#
103 lines
3.4 KiB
C#
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Mongo2Go;
|
|
using MongoDB.Driver;
|
|
using StellaOps.Authority.Plugins.Abstractions;
|
|
using StellaOps.Authority.Plugin.Standard.Security;
|
|
using StellaOps.Authority.Plugin.Standard.Storage;
|
|
|
|
namespace StellaOps.Authority.Plugin.Standard.Tests;
|
|
|
|
public class StandardUserCredentialStoreTests : IAsyncLifetime
|
|
{
|
|
private readonly MongoDbRunner runner;
|
|
private readonly IMongoDatabase database;
|
|
private readonly StandardPluginOptions options;
|
|
private readonly StandardUserCredentialStore store;
|
|
|
|
public StandardUserCredentialStoreTests()
|
|
{
|
|
runner = MongoDbRunner.Start(singleNodeReplSet: true);
|
|
var client = new MongoClient(runner.ConnectionString);
|
|
database = client.GetDatabase("authority-tests");
|
|
options = new StandardPluginOptions
|
|
{
|
|
PasswordPolicy = new PasswordPolicyOptions
|
|
{
|
|
MinimumLength = 8,
|
|
RequireDigit = true,
|
|
RequireLowercase = true,
|
|
RequireUppercase = true,
|
|
RequireSymbol = false
|
|
},
|
|
Lockout = new LockoutOptions
|
|
{
|
|
Enabled = true,
|
|
MaxAttempts = 2,
|
|
WindowMinutes = 1
|
|
}
|
|
};
|
|
store = new StandardUserCredentialStore(
|
|
"standard",
|
|
database,
|
|
options,
|
|
new Pbkdf2PasswordHasher(),
|
|
NullLogger<StandardUserCredentialStore>.Instance);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task VerifyPasswordAsync_ReturnsSuccess_ForValidCredentials()
|
|
{
|
|
var registration = new AuthorityUserRegistration(
|
|
"alice",
|
|
"Password1!",
|
|
"Alice",
|
|
null,
|
|
false,
|
|
new[] { "admin" },
|
|
new Dictionary<string, string?>());
|
|
|
|
var upsert = await store.UpsertUserAsync(registration, CancellationToken.None);
|
|
Assert.True(upsert.Succeeded);
|
|
|
|
var result = await store.VerifyPasswordAsync("alice", "Password1!", CancellationToken.None);
|
|
Assert.True(result.Succeeded);
|
|
Assert.Equal("alice", result.User?.Username);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task VerifyPasswordAsync_EnforcesLockout_AfterRepeatedFailures()
|
|
{
|
|
await store.UpsertUserAsync(
|
|
new AuthorityUserRegistration(
|
|
"bob",
|
|
"Password1!",
|
|
"Bob",
|
|
null,
|
|
false,
|
|
new[] { "operator" },
|
|
new Dictionary<string, string?>()),
|
|
CancellationToken.None);
|
|
|
|
var first = await store.VerifyPasswordAsync("bob", "wrong", CancellationToken.None);
|
|
Assert.False(first.Succeeded);
|
|
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, first.FailureCode);
|
|
|
|
var second = await store.VerifyPasswordAsync("bob", "stillwrong", CancellationToken.None);
|
|
Assert.False(second.Succeeded);
|
|
Assert.Equal(AuthorityCredentialFailureCode.LockedOut, second.FailureCode);
|
|
Assert.NotNull(second.RetryAfter);
|
|
Assert.True(second.RetryAfter.Value > System.TimeSpan.Zero);
|
|
}
|
|
|
|
public Task InitializeAsync() => Task.CompletedTask;
|
|
|
|
public Task DisposeAsync()
|
|
{
|
|
runner.Dispose();
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|