From 85eda2056add9e2fde10126febf68dadad9658b6 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Mon, 5 Oct 2026 17:51:18 +0200 Subject: [PATCH] fix(server): tighten role backfill anti-join to the role level The 20260907170000_backfill_project_roles anti-join matches on projectId only: any project owning at least one ProjectRole row (a single custom role suffices) never receives its missing system roles. Rework the migration as a per-role anti-join on name + position + oidcGroup + permissions so each absent system role is inserted individually while existing rows of any kind stay untouched. Signed-off-by: William Phetsinorath Change-Id: I079f57414d86c055d257834492eaadc46a6a6964 --- .../migration.sql | 37 +++++++++++++++++++ .../migration.sql | 37 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 apps/server-nestjs/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql create mode 100644 apps/server/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql diff --git a/apps/server-nestjs/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql b/apps/server-nestjs/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql new file mode 100644 index 0000000000..013e336c08 --- /dev/null +++ b/apps/server-nestjs/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql @@ -0,0 +1,37 @@ +-- Tighten the 20260907170000_backfill_project_roles anti-join to the role +-- level: the original matches on projectId only, so any project owning at +-- least one ProjectRole row never receives its missing system roles. +-- Mirrors generateProjectCreateInput (apps/server-nestjs/src/modules/project/project.utils.ts): +-- Administrateur = MANAGE = 2 +-- DevOps = MANAGE_ENVIRONMENTS | MANAGE_REPOSITORIES | REPLAY_HOOKS +-- | SEE_SECRETS | LIST_ENVIRONMENTS | LIST_REPOSITORIES = 984 +-- Développeur = MANAGE_REPOSITORIES | LIST_ENVIRONMENTS | LIST_REPOSITORIES = 784 +-- Lecture seule = LIST_ENVIRONMENTS | LIST_REPOSITORIES = 768 +-- Anti-join on projectId + name + position + oidcGroup + permissions makes +-- the INSERT idempotent: each absent system role is inserted individually, +-- existing rows of any kind are untouched, a retry is a no-op. +INSERT INTO "ProjectRole" ("id", "name", "permissions", "position", "oidcGroup", "type", "projectId") +SELECT + gen_random_uuid(), + r."name", + r."permissions", + r."position", + '/' || p."slug" || r."groupSuffix", + 'system:managed', + p."id" +FROM "Project" p +CROSS JOIN (VALUES + ('Administrateur', 2::bigint, 0, '/console/admin'), + ('DevOps', 984::bigint, 1, '/console/devops'), + ('Développeur', 784::bigint, 2, '/console/developer'), + ('Lecture seule', 768::bigint, 3, '/console/readonly') +) AS r("name", "permissions", "position", "groupSuffix") +WHERE NOT EXISTS ( + SELECT 1 + FROM "ProjectRole" existing + WHERE existing."projectId" = p."id" + AND existing."name" = r."name" + AND existing."position" = r."position" + AND existing."oidcGroup" = '/' || p."slug" || r."groupSuffix" + AND existing."permissions" = r."permissions" +); diff --git a/apps/server/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql b/apps/server/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql new file mode 100644 index 0000000000..013e336c08 --- /dev/null +++ b/apps/server/src/prisma/migrations/20261005155118_tighten_role_backfill/migration.sql @@ -0,0 +1,37 @@ +-- Tighten the 20260907170000_backfill_project_roles anti-join to the role +-- level: the original matches on projectId only, so any project owning at +-- least one ProjectRole row never receives its missing system roles. +-- Mirrors generateProjectCreateInput (apps/server-nestjs/src/modules/project/project.utils.ts): +-- Administrateur = MANAGE = 2 +-- DevOps = MANAGE_ENVIRONMENTS | MANAGE_REPOSITORIES | REPLAY_HOOKS +-- | SEE_SECRETS | LIST_ENVIRONMENTS | LIST_REPOSITORIES = 984 +-- Développeur = MANAGE_REPOSITORIES | LIST_ENVIRONMENTS | LIST_REPOSITORIES = 784 +-- Lecture seule = LIST_ENVIRONMENTS | LIST_REPOSITORIES = 768 +-- Anti-join on projectId + name + position + oidcGroup + permissions makes +-- the INSERT idempotent: each absent system role is inserted individually, +-- existing rows of any kind are untouched, a retry is a no-op. +INSERT INTO "ProjectRole" ("id", "name", "permissions", "position", "oidcGroup", "type", "projectId") +SELECT + gen_random_uuid(), + r."name", + r."permissions", + r."position", + '/' || p."slug" || r."groupSuffix", + 'system:managed', + p."id" +FROM "Project" p +CROSS JOIN (VALUES + ('Administrateur', 2::bigint, 0, '/console/admin'), + ('DevOps', 984::bigint, 1, '/console/devops'), + ('Développeur', 784::bigint, 2, '/console/developer'), + ('Lecture seule', 768::bigint, 3, '/console/readonly') +) AS r("name", "permissions", "position", "groupSuffix") +WHERE NOT EXISTS ( + SELECT 1 + FROM "ProjectRole" existing + WHERE existing."projectId" = p."id" + AND existing."name" = r."name" + AND existing."position" = r."position" + AND existing."oidcGroup" = '/' || p."slug" || r."groupSuffix" + AND existing."permissions" = r."permissions" +);