diff --git a/backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs b/backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs index ca88bcd..84d2cce 100644 --- a/backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs +++ b/backend/WorkClub.Api/Auth/ClubRoleClaimsTransformation.cs @@ -54,13 +54,36 @@ public class ClubRoleClaimsTransformation : IClaimsTransformation return Task.FromResult(principal); } - // --- NEW: Skip DB role lookup if user is a global admin --- - var realmAccess = principal.FindFirst("realm_access")?.Value; - if (!string.IsNullOrEmpty(realmAccess) && realmAccess.Contains("\"admin\"", StringComparison.OrdinalIgnoreCase)) + // --- NEW: Skip DB role lookup if user is a global admin --- + var realmAccess = principal.FindFirst("realm_access")?.Value; + if (!string.IsNullOrEmpty(realmAccess) && IsAdminUser(realmAccess)) + { + return Task.FromResult(principal); + } + // --------------------------------------------------------- + + static bool IsAdminUser(string realmAccess) + { + try + { + using var doc = System.Text.Json.JsonDocument.Parse(realmAccess); + if (doc.RootElement.TryGetProperty("roles", out var rolesElement) && + rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array) { - return Task.FromResult(principal); + foreach (var role in rolesElement.EnumerateArray()) + { + if (role.GetString()?.Equals("admin", StringComparison.OrdinalIgnoreCase) == true) + return true; + } } - // --------------------------------------------------------- + } + catch + { + // If JSON parsing fails, fallback to string contains check + return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase); + } + return false; + } // Look up the user's role in the database for the requested tenant _httpContextAccessor.HttpContext!.Items["TenantId"] = tenantId; diff --git a/backend/WorkClub.Api/Middleware/TenantValidationMiddleware.cs b/backend/WorkClub.Api/Middleware/TenantValidationMiddleware.cs index 0371586..0740b22 100644 --- a/backend/WorkClub.Api/Middleware/TenantValidationMiddleware.cs +++ b/backend/WorkClub.Api/Middleware/TenantValidationMiddleware.cs @@ -44,13 +44,36 @@ 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; - } + // NEW: Skip check if user is a global admin + var realmAccess = context.User.FindFirst("realm_access")?.Value; + if (!string.IsNullOrEmpty(realmAccess) && IsAdminUser(realmAccess)) + { + await _next(context); + return; + } + + static bool IsAdminUser(string realmAccess) + { + try + { + using var doc = System.Text.Json.JsonDocument.Parse(realmAccess); + if (doc.RootElement.TryGetProperty("roles", out var rolesElement) && + rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array) + { + foreach (var role in rolesElement.EnumerateArray()) + { + if (role.GetString()?.Equals("admin", StringComparison.OrdinalIgnoreCase) == true) + return true; + } + } + } + catch + { + // If JSON parsing fails, fallback to string contains check + return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase); + } + return false; + } context.Response.StatusCode = StatusCodes.Status403Forbidden; await context.Response.WriteAsJsonAsync(new { error = "User does not have clubs claim" }); diff --git a/backend/WorkClub.Api/Program.cs b/backend/WorkClub.Api/Program.cs index 9e85016..4a686e9 100644 --- a/backend/WorkClub.Api/Program.cs +++ b/backend/WorkClub.Api/Program.cs @@ -53,7 +53,29 @@ builder.Services.AddAuthorizationBuilder() .AddPolicy("RequireGlobalAdmin", policy => policy.RequireAssertion(context => { var realmAccess = context.User.FindFirst("realm_access")?.Value; - return realmAccess != null && realmAccess.Contains("\"admin\""); + if (string.IsNullOrEmpty(realmAccess)) + return false; + + try + { + using var doc = System.Text.Json.JsonDocument.Parse(realmAccess); + if (doc.RootElement.TryGetProperty("roles", out var rolesElement) && + rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array) + { + foreach (var role in rolesElement.EnumerateArray()) + { + if (role.GetString() == "admin") + return true; + } + } + } + catch + { + // If JSON parsing fails, fallback to string contains check + return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase); + } + + return false; })) .AddPolicy("RequireManager", policy => policy.RequireRole("Manager")) .AddPolicy("RequireMember", policy => policy.RequireRole("Manager", "Member"))