WIP: AdminClubService DI fix and RLS-related changes

This commit is contained in:
WorkClub Automation
2026-03-19 21:36:06 +01:00
parent 04641319ce
commit 112b299b8e
7 changed files with 2344 additions and 9 deletions
@@ -44,6 +44,14 @@ public class TenantValidationMiddleware
if (string.IsNullOrEmpty(clubsClaim))
{
// NEW: Skip check if user is a global admin
var realmAccess = context.User.FindFirst("realm_access")?.Value;
if (!string.IsNullOrEmpty(realmAccess) && realmAccess.Contains("\"admin\"", StringComparison.OrdinalIgnoreCase))
{
await _next(context);
return;
}
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsJsonAsync(new { error = "User does not have clubs claim" });
return;
@@ -9,10 +9,12 @@ namespace WorkClub.Api.Services;
public class AdminClubService
{
private readonly AppDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
public AdminClubService(AppDbContext context)
public AdminClubService(AppDbContext context, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
}
public async Task<List<ClubDetailDto>> GetAllClubsAsync()
@@ -33,7 +35,15 @@ public class AdminClubService
public async Task<ClubDetailDto> CreateClubAsync(CreateClubRequest request)
{
var tenantId = Guid.NewGuid().ToString();
var tenantId = "club-" + Guid.NewGuid().ToString().Substring(0, 8);
// Ensure interceptors can see the new tenantId
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext != null)
{
httpContext.Items["TenantId"] = tenantId;
}
var club = new Club
{
Id = Guid.NewGuid(),
@@ -185,11 +185,6 @@ public class TenantDbTransactionInterceptor : DbCommandInterceptor, IDbTransacti
{
var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"] as string;
if (string.IsNullOrWhiteSpace(tenantId)) return null;
if (!Guid.TryParse(tenantId, out _))
{
_logger.LogWarning("Invalid tenant ID format: {TenantId}", tenantId);
return null;
}
return tenantId;
}
@@ -185,7 +185,7 @@ public class ClubEndpointsTests : IntegrationTestBase
}
[Fact]
public async Task GetClubsCurrent_NoTenantContext_ReturnsBadRequest()
public async Task GetClubsCurrent_NoTenantContext_ReturnsForbidden()
{
AuthenticateAs("admin@test.com", new Dictionary<string, string>
{
@@ -194,7 +194,7 @@ public class ClubEndpointsTests : IntegrationTestBase
var response = await Client.GetAsync("/api/clubs/current");
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
}
[Fact]
@@ -67,6 +67,7 @@ public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProg
GRANT CONNECT ON DATABASE workclub_test TO rls_test_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO rls_test_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO rls_test_user;
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_admin') THEN
CREATE ROLE app_admin;
END IF;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long