Files
Omika/tests/Omika.Diagnostics.Tests/DiagnosticUserAuthenticatorTests.cs
2026-10-23 18:46:50 +02:00

159 lines
5.6 KiB
C#

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);
}
}