using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Identity; using Microsoft.IdentityModel.Tokens; using Xunit; using IamService.Domain.AggregatesModel.UserAggregate; namespace IamService.FunctionalTests.Controllers; /// /// EN: Functional tests for Authorization Policies. /// VI: Functional tests cho Authorization Policies. /// /// /// EN: These tests require additional authentication configuration in CustomWebApplicationFactory /// to properly inject role claims into the test JWT. The tests verify that: /// - RequireSuperAdmin: Only SuperAdmin can access PAM endpoints /// - RequireAdmin: Admin/SuperAdmin can access management endpoints /// - RequireAuditor: Auditor/Admin/SuperAdmin can access audit endpoints /// - OwnerOrAdmin: User can access own profile or Admin can access any /// /// TODO: Configure test authentication to properly inject role claims. /// Current unit tests in IamService.UnitTests cover authorization handler logic. /// VI: Các tests này yêu cầu cấu hình thêm authentication trong CustomWebApplicationFactory /// để inject role claims vào test JWT. Unit tests trong IamService.UnitTests đã cover /// logic authorization handler. /// [Collection("Sequential")] public class AuthorizationPolicyTests : IClassFixture { private readonly HttpClient _client; private readonly CustomWebApplicationFactory _factory; public AuthorizationPolicyTests(CustomWebApplicationFactory factory) { _factory = factory; _client = factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); } #region Token Generation Helpers /// /// EN: Generate a test JWT token with specified roles. /// VI: Tạo test JWT token với các roles được chỉ định. /// /// /// EN: Creates an unsigned JWT that the CustomWebApplicationFactory accepts. /// The test factory is configured to skip signature validation. /// VI: Tạo unsigned JWT mà CustomWebApplicationFactory chấp nhận. /// Test factory được cấu hình để bỏ qua signature validation. /// private string GenerateTestToken(Guid userId, string email, params string[] roles) { var claims = new List { new("sub", userId.ToString()), new("email", email), new("name", "Test User"), new("iss", "http://localhost"), new("aud", "api"), new("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()), new("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds().ToString()) }; // Add role claims foreach (var role in roles) { claims.Add(new Claim("role", role)); } // Create unsigned token descriptor var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddHours(1), Issuer = "http://localhost", Audience = "api" }; // Generate JWT without signature (for test purposes) var handler = new JwtSecurityTokenHandler(); var token = handler.CreateJwtSecurityToken(tokenDescriptor); return handler.WriteToken(token); } private HttpRequestMessage CreateRequest(HttpMethod method, string url, string token) { var request = new HttpRequestMessage(method, url); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); return request; } #endregion #region RequireAdmin Policy Tests [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetUsers_WithoutAdminRole_ShouldReturn403() { // Arrange - User with no admin role var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User"); var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetUsers_WithAdminRole_ShouldReturn200() { // Arrange - User with Admin role var token = GenerateTestToken(Guid.NewGuid(), "admin@example.com", "Admin"); var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetUsers_WithSuperAdminRole_ShouldReturn200() { // Arrange - SuperAdmin can access Admin endpoints var token = GenerateTestToken(Guid.NewGuid(), "superadmin@example.com", "SuperAdmin"); var request = CreateRequest(HttpMethod.Get, "/api/v1/users", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } #endregion #region RequireSuperAdmin Policy Tests (PAM) [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetActivePrivilegedAccess_WithAdminRole_ShouldReturn403() { // Arrange - Admin cannot access SuperAdmin endpoints var userId = Guid.NewGuid(); var token = GenerateTestToken(userId, "admin@example.com", "Admin"); var request = CreateRequest(HttpMethod.Get, $"/api/v1/privileged-access/active?userId={userId}", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetActivePrivilegedAccess_WithSuperAdminRole_ShouldReturn200() { // Arrange - Only SuperAdmin can access PAM var userId = Guid.NewGuid(); var token = GenerateTestToken(userId, "superadmin@example.com", "SuperAdmin"); var request = CreateRequest(HttpMethod.Get, $"/api/v1/privileged-access/active?userId={userId}", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } #endregion #region RequireAuditor Policy Tests [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetAuditLogs_WithUserRole_ShouldReturn403() { // Arrange - Regular user cannot access audit logs var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User"); var request = CreateRequest(HttpMethod.Get, "/api/v1/audit/logs", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetAuditLogs_WithAuditorRole_ShouldReturn200() { // Arrange - Auditor can access audit logs var token = GenerateTestToken(Guid.NewGuid(), "auditor@example.com", "Auditor"); var request = CreateRequest(HttpMethod.Get, "/api/v1/audit/logs", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GetComplianceReports_WithAdminRole_ShouldReturn200() { // Arrange - Admin can access compliance (auditor policy allows admin) var token = GenerateTestToken(Guid.NewGuid(), "admin@example.com", "Admin"); var request = CreateRequest(HttpMethod.Get, "/api/v1/compliance/reports", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } #endregion #region Controller-Level Policy Tests [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task RolesController_WithUserRole_ShouldReturn403() { // Arrange - User cannot access roles management var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User"); var request = CreateRequest(HttpMethod.Get, "/api/v1/roles", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task OrganizationsController_WithUserRole_ShouldReturn403() { // Arrange - User cannot access organizations management var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User"); var orgId = Guid.NewGuid(); var request = CreateRequest(HttpMethod.Get, $"/api/v1/organizations/{orgId}", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact(Skip = "Requires test authentication configuration for role claims")] public async Task GroupsController_WithUserRole_ShouldReturn403() { // Arrange - User cannot access groups management var token = GenerateTestToken(Guid.NewGuid(), "user@example.com", "User"); var groupId = Guid.NewGuid(); var request = CreateRequest(HttpMethod.Get, $"/api/v1/groups/{groupId}", token); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } #endregion #region Edge Cases [Fact] public async Task AnyEndpoint_WithoutToken_ShouldReturn401() { // Arrange - No token provided var response = await _client.GetAsync("/api/v1/users"); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task AnyEndpoint_WithInvalidToken_ShouldReturn401() { // Arrange - Invalid token var request = CreateRequest(HttpMethod.Get, "/api/v1/users", "invalid-token"); // Act var response = await _client.SendAsync(request); // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } #endregion }