Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
using Omika.Diagnostics.Security;
|
||||
using Omika.Diagnostics.Tests.Fakes;
|
||||
using Omika.Diagnostics.Users;
|
||||
|
||||
namespace Omika.Diagnostics.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// T23 — Authentification hybride : utilisateurs PostgreSQL en priorité, sinon opérateurs
|
||||
/// bootstrap. Échec générique (jamais « inconnu » vs « désactivé »). DB autoritaire.
|
||||
/// </summary>
|
||||
public class DiagnosticUserAuthenticatorTests
|
||||
{
|
||||
private const string Password = "correct-password-123";
|
||||
|
||||
private static DiagnosticUserRecord DbUser(string username, string password, string role = DiagnosticRoles.Support, bool enabled = true) =>
|
||||
new()
|
||||
{
|
||||
Id = 1,
|
||||
Username = username,
|
||||
PasswordHash = DiagnosticPasswordHasher.Hash(password),
|
||||
Role = role,
|
||||
Enabled = enabled
|
||||
};
|
||||
|
||||
private static DiagnosticOperator Operator(string username, string password, params string[] roles) =>
|
||||
new(username, DiagnosticPasswordHasher.Hash(password), roles);
|
||||
|
||||
// -------------------------------------------------- DB prioritaire ----
|
||||
[Fact]
|
||||
public async Task DbUser_CorrectPassword_Succeeds()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("alice", Password, DiagnosticRoles.Admin));
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), "alice", Password);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.True(result.IsDatabaseUser);
|
||||
Assert.Equal("alice", result.Username);
|
||||
Assert.Contains(DiagnosticRoles.Admin, result.Roles);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DbUser_WrongPassword_Fails()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("alice", Password));
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), "alice", "wrong-password-123");
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DbUser_Disabled_Fails_EvenWithCorrectPassword()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("alice", Password, enabled: false));
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), "alice", Password);
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
// -------------------------------------------------- fallback opérateurs ----
|
||||
[Fact]
|
||||
public async Task UnknownUser_OperatorFallback_Succeeds()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo,
|
||||
new[] { Operator("bootstrap", Password, DiagnosticRoles.Admin) },
|
||||
"bootstrap",
|
||||
Password);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.False(result.IsDatabaseUser);
|
||||
Assert.Equal("bootstrap", result.Username);
|
||||
Assert.Contains(DiagnosticRoles.Admin, result.Roles);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UnknownUser_NoOperators_Fails()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), "inconnu", Password);
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Operator_WrongPassword_Fails()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo,
|
||||
new[] { Operator("bootstrap", Password, DiagnosticRoles.Admin) },
|
||||
"bootstrap",
|
||||
"wrong-password-123");
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
// -------------------------------------------------- priorité DB (§36) ----
|
||||
[Fact]
|
||||
public async Task DbIsAuthoritative_DisabledDbUser_DoesNotFallbackToOperator()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("dup", Password, enabled: false));
|
||||
|
||||
// Même username existe aussi en configuration (bootstrap) avec un mot de passe valide :
|
||||
// la DB doit rester autoritaire → échec, PAS de fallback.
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo,
|
||||
new[] { Operator("dup", Password, DiagnosticRoles.Admin) },
|
||||
"dup",
|
||||
Password);
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
// -------------------------------------------------- cas limites ----
|
||||
[Theory]
|
||||
[InlineData("", "password-123")]
|
||||
[InlineData(" ", "password-123")]
|
||||
[InlineData("alice", "")]
|
||||
[InlineData("alice", " ")]
|
||||
public async Task BlankInput_Fails(string username, string password)
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("alice", Password));
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), username, password);
|
||||
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DbLookup_CaseInsensitive()
|
||||
{
|
||||
var repo = new FakeDiagnosticUserRepository();
|
||||
repo.Seed(DbUser("Alice.Dupont", Password, DiagnosticRoles.Support));
|
||||
|
||||
DiagnosticLoginResult result = await DiagnosticUserAuthenticator.AuthenticateAsync(
|
||||
repo, Array.Empty<DiagnosticOperator>(), "alice.dupont", Password);
|
||||
|
||||
Assert.True(result.Success);
|
||||
Assert.Equal("Alice.Dupont", result.Username);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user