using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.IdentityModel.Tokens; using Tiku.Application.Security; using Tiku.Domain.Tenancy; namespace Tiku.IntegrationTests.Api; public sealed class SecurityFoundationTests { private static readonly JwtOptions JwtOptions = new() { Issuer = "tiku-backend", Audience = "tiku-api", SigningKey = "development-only-tiku-signing-key-change-before-production" }; [Fact] public async Task Authenticated_policy_returns_unauthorized_without_token() { await using var factory = CreateFactory(); using var client = factory.CreateClient(); var response = await client.GetAsync("/api/_security/authenticated"); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } [Fact] public async Task Tenant_admin_policy_returns_forbidden_for_non_admin_member() { await using var factory = CreateFactory(); using var client = factory.CreateClient(); client.DefaultRequestHeaders.Authorization = new( "Bearer", CreateToken([ new Claim(TikuClaimTypes.UserId, Guid.NewGuid().ToString()), new Claim(TikuClaimTypes.TenantId, Guid.NewGuid().ToString()), new Claim(TikuClaimTypes.TenantRole, TenantRole.Student.ToString()) ])); var response = await client.GetAsync("/api/_security/tenant-admin"); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } [Fact] public async Task Controller_pipeline_loads_authenticated_current_user() { var userId = Guid.NewGuid(); await using var factory = CreateFactory(); using var client = factory.CreateClient(); client.DefaultRequestHeaders.Authorization = new( "Bearer", CreateToken([ new Claim(TikuClaimTypes.UserId, userId.ToString()) ])); var response = await client.GetAsync("/api/_security/authenticated"); var body = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains(userId.ToString(), body, StringComparison.OrdinalIgnoreCase); Assert.Contains("true", body, StringComparison.OrdinalIgnoreCase); } private static WebApplicationFactory CreateFactory() { return new WebApplicationFactory(); } private static string CreateToken(IEnumerable claims) { var credentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtOptions.SigningKey)), SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( JwtOptions.Issuer, JwtOptions.Audience, claims, expires: DateTime.UtcNow.AddMinutes(5), signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(token); } }