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
}