feat: restrict admin access to club operations and rollout test environment
This commit is contained in:
@@ -26,7 +26,7 @@ public class SeedDataService
|
||||
|
||||
using var transaction = await context.Database.BeginTransactionAsync();
|
||||
|
||||
// Enable RLS on all tenant tables
|
||||
// Enable RLS on all tenant tables (Must be table owner, which 'workclub' is)
|
||||
await context.Database.ExecuteSqlRawAsync(@"
|
||||
ALTER TABLE clubs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE clubs FORCE ROW LEVEL SECURITY;
|
||||
@@ -62,22 +62,6 @@ public class SeedDataService
|
||||
");
|
||||
|
||||
// Create admin bypass policies (idempotent)
|
||||
await context.Database.ExecuteSqlRawAsync(@"
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_admin') THEN
|
||||
CREATE ROLE app_admin;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
GRANT app_admin TO app;
|
||||
GRANT USAGE ON SCHEMA public TO app_admin;
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO app_admin;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO app_admin;
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON TABLES TO app_admin;
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE app IN SCHEMA public GRANT ALL ON SEQUENCES TO app_admin;
|
||||
");
|
||||
|
||||
await context.Database.ExecuteSqlRawAsync(@"
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='clubs' AND policyname='bypass_rls_policy') THEN
|
||||
@@ -140,31 +124,7 @@ public class SeedDataService
|
||||
{
|
||||
var members = new List<Member>
|
||||
{
|
||||
// admin@test.com: Admin in Club 1, Member in Club 2
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tennisClub.TenantId,
|
||||
ExternalUserId = "admin-user-id",
|
||||
DisplayName = "Admin User",
|
||||
Email = "admin@test.com",
|
||||
Role = ClubRole.Admin,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
new Member
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = cyclingClub.TenantId,
|
||||
ExternalUserId = "admin-user-id",
|
||||
DisplayName = "Admin User",
|
||||
Email = "admin@test.com",
|
||||
Role = ClubRole.Member,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
|
||||
// manager@test.com: Manager in Club 1
|
||||
new Member
|
||||
{
|
||||
@@ -235,8 +195,7 @@ public class SeedDataService
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Get admin member IDs for work item creation
|
||||
var adminMembers = context.Members.Where(m => m.Email == "admin@test.com").ToList();
|
||||
|
||||
var managerMember = context.Members.First(m => m.Email == "manager@test.com");
|
||||
var member1Members = context.Members.Where(m => m.Email == "member1@test.com").ToList();
|
||||
var member2Member = context.Members.First(m => m.Email == "member2@test.com");
|
||||
@@ -255,7 +214,7 @@ public class SeedDataService
|
||||
Description = "Resurface main court",
|
||||
Status = WorkItemStatus.Open,
|
||||
AssigneeId = null,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(14),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -269,7 +228,7 @@ public class SeedDataService
|
||||
Description = "Purchase new tennis rackets and balls",
|
||||
Status = WorkItemStatus.Assigned,
|
||||
AssigneeId = managerMember.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(7),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -283,7 +242,7 @@ public class SeedDataService
|
||||
Description = "Organize annual summer tournament",
|
||||
Status = WorkItemStatus.InProgress,
|
||||
AssigneeId = member1Members.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(30),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -297,7 +256,7 @@ public class SeedDataService
|
||||
Description = "Update and review club rules handbook",
|
||||
Status = WorkItemStatus.Review,
|
||||
AssigneeId = member2Member.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(21),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -311,7 +270,7 @@ public class SeedDataService
|
||||
Description = "Update club website with new photos",
|
||||
Status = WorkItemStatus.Done,
|
||||
AssigneeId = managerMember.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
ClubId = tennisClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(-5),
|
||||
CreatedAt = DateTimeOffset.UtcNow.AddDays(-10),
|
||||
@@ -326,7 +285,7 @@ public class SeedDataService
|
||||
Description = "Create new cycling routes for summer",
|
||||
Status = WorkItemStatus.Open,
|
||||
AssigneeId = null,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(21),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -340,7 +299,7 @@ public class SeedDataService
|
||||
Description = "Organize safety and maintenance training",
|
||||
Status = WorkItemStatus.Assigned,
|
||||
AssigneeId = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(14),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -353,8 +312,8 @@ public class SeedDataService
|
||||
Title = "Group ride coordination",
|
||||
Description = "Schedule and coordinate weekly group rides",
|
||||
Status = WorkItemStatus.InProgress,
|
||||
AssigneeId = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
AssigneeId = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
ClubId = cyclingClub.Id,
|
||||
DueDate = DateTimeOffset.UtcNow.AddDays(7),
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
@@ -384,7 +343,7 @@ public class SeedDataService
|
||||
EndTime = now.AddDays(-1).Date.ToLocalTime().AddHours(12),
|
||||
Capacity = 2,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
@@ -399,7 +358,7 @@ public class SeedDataService
|
||||
EndTime = now.Date.ToLocalTime().AddHours(18),
|
||||
Capacity = 3,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
@@ -414,7 +373,7 @@ public class SeedDataService
|
||||
EndTime = now.AddDays(7).Date.ToLocalTime().AddHours(17),
|
||||
Capacity = 5,
|
||||
ClubId = tennisClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == tennisClub.Id).Id,
|
||||
CreatedById = managerMember.Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
@@ -430,7 +389,7 @@ public class SeedDataService
|
||||
EndTime = now.Date.ToLocalTime().AddHours(9),
|
||||
Capacity = 10,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
},
|
||||
@@ -445,7 +404,7 @@ public class SeedDataService
|
||||
EndTime = now.AddDays(7).Date.ToLocalTime().AddHours(14),
|
||||
Capacity = 4,
|
||||
ClubId = cyclingClub.Id,
|
||||
CreatedById = adminMembers.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedById = member1Members.First(m => m.ClubId == cyclingClub.Id).Id,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user