using System.Net; using System.Net.Http.Json; using ItWorx.KnowledgeHub.Contracts.Admin; using ItWorx.KnowledgeHub.Contracts.Auth; using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure; namespace ItWorx.KnowledgeHub.IntegrationTests.Authorization; [Collection(PostgresTestGroup.Name)] public sealed class RoleAuthorizationTests(PostgresContainerFixture postgres) : OwnerSetupTestBase(postgres) { [Fact] public async Task Unauthenticated_requests_to_admin_endpoints_are_rejected() { using var client = Factory.CreateClient(); using var response = await client.GetAsync("/api/v1/admin/users"); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task Reader_role_cannot_reach_administrator_only_endpoints() { using var adminClient = Factory.CreateClient(); await SetupHelper.LoginAsync(adminClient, OwnerEmail, OwnerPassword); const string readerEmail = "lezer@itworx.test"; const string readerPassword = "reader-strong-password"; await CreateUserAsync(adminClient, readerEmail, readerPassword, "Reader"); using var readerClient = Factory.CreateClient(); await SetupHelper.LoginAsync(readerClient, readerEmail, readerPassword); using var response = await readerClient.GetAsync("/api/v1/admin/users"); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] public async Task Editor_role_cannot_reach_administrator_only_endpoints() { using var adminClient = Factory.CreateClient(); await SetupHelper.LoginAsync(adminClient, OwnerEmail, OwnerPassword); const string editorEmail = "redacteur@itworx.test"; const string editorPassword = "editor-strong-password"; await CreateUserAsync(adminClient, editorEmail, editorPassword, "Editor"); using var editorClient = Factory.CreateClient(); await SetupHelper.LoginAsync(editorClient, editorEmail, editorPassword); using var response = await editorClient.GetAsync("/api/v1/admin/users"); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] public async Task Administrator_role_can_reach_administrator_only_endpoints() { using var adminClient = Factory.CreateClient(); await SetupHelper.LoginAsync(adminClient, OwnerEmail, OwnerPassword); using var response = await adminClient.GetAsync("/api/v1/admin/users"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact] public async Task Disabling_a_user_immediately_invalidates_their_existing_session_not_only_future_logins() { using var adminClient = Factory.CreateClient(); await SetupHelper.LoginAsync(adminClient, OwnerEmail, OwnerPassword); const string email = "tijdelijk@itworx.test"; const string password = "public-fixture"; var created = await CreateUserAsync(adminClient, email, password, "Editor"); using var targetClient = Factory.CreateClient(); await SetupHelper.LoginAsync(targetClient, email, password); var whileEnabled = await targetClient.GetAsync("/api/v1/auth/session"); Assert.Equal(HttpStatusCode.OK, whileEnabled.StatusCode); var disableResponse = await SetupHelper.PatchWithCsrfAsync( adminClient, $"/api/v1/admin/users/{created.Id}", new UpdateUserRequest(Role: null, Enabled: false)); Assert.Equal(HttpStatusCode.OK, disableResponse.StatusCode); // The already-issued session cookie must stop working immediately — this is // checked on the very next request, not after any polling delay, because the // security-stamp validation happens on every request (ValidationInterval = // TimeSpan.Zero). var afterDisable = await targetClient.GetFromJsonAsync("/api/v1/auth/session"); Assert.NotNull(afterDisable); Assert.False(afterDisable.Authenticated); } [Fact] public async Task Administrator_cannot_demote_the_last_enabled_administrator() { using var adminClient = Factory.CreateClient(); await SetupHelper.LoginAsync(adminClient, OwnerEmail, OwnerPassword); var session = await adminClient.GetFromJsonAsync("/api/v1/auth/session"); Assert.NotNull(session); var response = await SetupHelper.PatchWithCsrfAsync( adminClient, $"/api/v1/admin/users/{session.UserId}", new UpdateUserRequest(Role: "Reader", Enabled: null)); Assert.Equal(HttpStatusCode.Conflict, response.StatusCode); } private static async Task CreateUserAsync( HttpClient adminClient, string email, string password, string role) { var response = await SetupHelper.PostWithCsrfAsync( adminClient, "/api/v1/admin/users", new CreateUserRequest($"Test {role}", email, password, role)); Assert.Equal(HttpStatusCode.OK, response.StatusCode); var payload = await response.Content.ReadFromJsonAsync(); Assert.NotNull(payload); return payload; } }