Fix: Admin club management 500 error - JWT clubs claim format
- Fix clubs attribute in Keycloak to contain only UUIDs (removed role names) - Add defensive error handling in ClubService.GetMyClubsAsync() - Add logging for debugging club retrieval issues - Return empty list instead of 500 error on failures Fixes: Admin users can now manage clubs without contact admin error
This commit is contained in:
@@ -12,32 +12,44 @@ public class ClubService
|
||||
private readonly AppDbContext _context;
|
||||
private readonly ITenantProvider _tenantProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<ClubService> _logger;
|
||||
|
||||
public ClubService(
|
||||
AppDbContext context,
|
||||
ITenantProvider tenantProvider,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<ClubService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_tenantProvider = tenantProvider;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<ClubListDto>> GetMyClubsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var clubsClaim = _httpContextAccessor.HttpContext?.User.FindFirst("clubs")?.Value;
|
||||
_logger.LogInformation("GetMyClubsAsync: Clubs claim value: {ClubsClaim}", clubsClaim);
|
||||
|
||||
if (string.IsNullOrEmpty(clubsClaim))
|
||||
{
|
||||
_logger.LogWarning("GetMyClubsAsync: No clubs claim found for user");
|
||||
return new List<ClubListDto>();
|
||||
}
|
||||
|
||||
// Parse UUIDs from comma-separated claim, filtering out non-UUID values (like role names)
|
||||
var tenantIds = clubsClaim.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(t => t.Trim())
|
||||
.Where(t => !string.IsNullOrEmpty(t) && Guid.TryParse(t, out _))
|
||||
.ToList();
|
||||
|
||||
_logger.LogInformation("GetMyClubsAsync: Parsed {Count} valid tenant IDs from claim", tenantIds.Count);
|
||||
|
||||
if (tenantIds.Count == 0)
|
||||
{
|
||||
_logger.LogWarning("GetMyClubsAsync: No valid tenant IDs found in clubs claim: {ClubsClaim}", clubsClaim);
|
||||
return new List<ClubListDto>();
|
||||
}
|
||||
|
||||
@@ -45,13 +57,16 @@ public class ClubService
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
|
||||
foreach (var tenantId in tenantIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
await using var transaction = await connection.BeginTransactionAsync();
|
||||
|
||||
// Set RLS context
|
||||
// Set RLS context - tenantId is already validated as a valid GUID
|
||||
// Use direct string since SET LOCAL doesn't support parameters
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
@@ -120,11 +135,27 @@ public class ClubService
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "GetMyClubsAsync: Error processing tenant {TenantId}", tenantId);
|
||||
// Continue with next tenant instead of failing entirely
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("GetMyClubsAsync: Returning {Count} clubs", clubDtos.Count);
|
||||
return clubDtos;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "GetMyClubsAsync: Unexpected error getting user clubs");
|
||||
// Return empty list instead of throwing to prevent 500 error
|
||||
return new List<ClubListDto>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ClubDetailDto?> GetCurrentClubAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tenantId = _tenantProvider.GetTenantId();
|
||||
|
||||
@@ -143,4 +174,10 @@ public class ClubService
|
||||
club.UpdatedAt
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "GetCurrentClubAsync: Error getting current club");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ data:
|
||||
],
|
||||
"attributes": {
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,Admin,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda,Member"
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -174,7 +174,7 @@ data:
|
||||
],
|
||||
"attributes": {
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,Manager"
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -196,7 +196,7 @@ data:
|
||||
],
|
||||
"attributes": {
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,Member,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda,Member"
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -218,7 +218,7 @@ data:
|
||||
],
|
||||
"attributes": {
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,Member"
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -240,7 +240,7 @@ data:
|
||||
],
|
||||
"attributes": {
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,Viewer"
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"serviceAccountsEnabled": false,
|
||||
"serviceAccountsEnabled": false,
|
||||
"authorizationServicesEnabled": false,
|
||||
"protocol": "openid-connect",
|
||||
"redirectUris": [
|
||||
@@ -162,7 +162,9 @@
|
||||
"firstName": "Admin",
|
||||
"lastName": "User",
|
||||
"attributes": {
|
||||
"clubs": []
|
||||
"clubs": [
|
||||
"64e05b5e-ef45-81d7-f2e8-3d14bd197383,3b4afcfa-1352-8fc7-b497-8ab52a0d5fda"
|
||||
]
|
||||
},
|
||||
"credentials": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user