fix(backend): move RLS initialization before app_admin role assignment

Reorganize SeedDataService to establish RLS policies before granting
app_admin role to prevent permission issues. Remove --no-restore flag
from Dockerfile.dev to ensure proper build.

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
WorkClub Automation
2026-03-05 19:22:29 +01:00
parent 3b7db39cc2
commit 65e80ee334
2 changed files with 66 additions and 1 deletions

View File

@@ -28,4 +28,4 @@ COPY . .
EXPOSE 8080
# Hot reload: dotnet watch monitors file changes in mounted volumes
ENTRYPOINT ["dotnet", "watch", "run", "--project", "WorkClub.Api/WorkClub.Api.csproj", "--no-restore"]
ENTRYPOINT ["dotnet", "watch", "run", "--project", "WorkClub.Api/WorkClub.Api.csproj"]

View File

@@ -21,6 +21,68 @@ public class SeedDataService
{
using var scope = _serviceScopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await context.Database.MigrateAsync();
using var transaction = await context.Database.BeginTransactionAsync();
// Enable RLS on all tenant tables
await context.Database.ExecuteSqlRawAsync(@"
ALTER TABLE clubs ENABLE ROW LEVEL SECURITY;
ALTER TABLE clubs FORCE ROW LEVEL SECURITY;
ALTER TABLE members ENABLE ROW LEVEL SECURITY;
ALTER TABLE members FORCE ROW LEVEL SECURITY;
ALTER TABLE work_items ENABLE ROW LEVEL SECURITY;
ALTER TABLE work_items FORCE ROW LEVEL SECURITY;
ALTER TABLE shifts ENABLE ROW LEVEL SECURITY;
ALTER TABLE shifts FORCE ROW LEVEL SECURITY;
ALTER TABLE shift_signups ENABLE ROW LEVEL SECURITY;
ALTER TABLE shift_signups FORCE ROW LEVEL SECURITY;
");
// Create tenant isolation policies (idempotent)
await context.Database.ExecuteSqlRawAsync(@"
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='clubs' AND policyname='tenant_isolation_policy') THEN
CREATE POLICY tenant_isolation_policy ON clubs FOR ALL USING ((""TenantId"")::text = current_setting('app.current_tenant_id', true));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='members' AND policyname='tenant_isolation_policy') THEN
CREATE POLICY tenant_isolation_policy ON members FOR ALL USING ((""TenantId"")::text = current_setting('app.current_tenant_id', true));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='work_items' AND policyname='tenant_isolation_policy') THEN
CREATE POLICY tenant_isolation_policy ON work_items FOR ALL USING ((""TenantId"")::text = current_setting('app.current_tenant_id', true));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='shifts' AND policyname='tenant_isolation_policy') THEN
CREATE POLICY tenant_isolation_policy ON shifts FOR ALL USING ((""TenantId"")::text = current_setting('app.current_tenant_id', true));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='shift_signups' AND policyname='tenant_isolation_policy') THEN
CREATE POLICY tenant_isolation_policy ON shift_signups FOR ALL USING (""ShiftId"" IN (SELECT ""Id"" FROM shifts WHERE (""TenantId"")::text = current_setting('app.current_tenant_id', true)));
END IF;
END $$;
");
// Create admin bypass policies (idempotent)
await context.Database.ExecuteSqlRawAsync(@"
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='clubs' AND policyname='bypass_rls_policy') THEN
CREATE POLICY bypass_rls_policy ON clubs FOR ALL TO app_admin USING (true);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='members' AND policyname='bypass_rls_policy') THEN
CREATE POLICY bypass_rls_policy ON members FOR ALL TO app_admin USING (true);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='work_items' AND policyname='bypass_rls_policy') THEN
CREATE POLICY bypass_rls_policy ON work_items FOR ALL TO app_admin USING (true);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='shifts' AND policyname='bypass_rls_policy') THEN
CREATE POLICY bypass_rls_policy ON shifts FOR ALL TO app_admin USING (true);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE tablename='shift_signups' AND policyname='bypass_rls_policy') THEN
CREATE POLICY bypass_rls_policy ON shift_signups FOR ALL TO app_admin USING (true);
END IF;
END $$;
");
await context.Database.ExecuteSqlRawAsync("SET LOCAL ROLE app_admin");
// Seed clubs
if (!context.Clubs.Any())
@@ -437,6 +499,9 @@ public class SeedDataService
await context.SaveChangesAsync();
}
}
await context.Database.ExecuteSqlRawAsync("RESET ROLE");
await transaction.CommitAsync();
}
private static string GenerateDeterministicGuid(string input)