Fix RLS permissions and JWT validation for admin club creation #5
@@ -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
Reference in New Issue
Block a user