From 1de7e49554f2e98460ee0f1a54d042f920c62eb9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 14:10:53 +0000 Subject: [PATCH] fix(rbac): scope role_permissions unique index per property Cloud tenant bootstrap was silently skipping permission grants once any property had seeded the same system roles, because the unique index was only (role_id, permission_key). New tenants then had zero grants and housekeeping.manage (Generate Stayovers) failed closed. Co-authored-by: telivity-otaip --- .../src/migrations/0016_role_permissions_per_property.sql | 8 ++++++++ packages/database/src/push-schema.ts | 2 +- packages/database/src/schema/rbac.ts | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 packages/database/src/migrations/0016_role_permissions_per_property.sql diff --git a/packages/database/src/migrations/0016_role_permissions_per_property.sql b/packages/database/src/migrations/0016_role_permissions_per_property.sql new file mode 100644 index 00000000..37744ae8 --- /dev/null +++ b/packages/database/src/migrations/0016_role_permissions_per_property.sql @@ -0,0 +1,8 @@ +-- role_permissions must be unique per property. The old unique index +-- (role_id, permission_key) made Cloud tenant bootstrap silently skip +-- permission grants once any other property had seeded the same system role. + +DROP INDEX IF EXISTS role_permissions_role_perm_unique; + +CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique + ON role_permissions (property_id, role_id, permission_key); diff --git a/packages/database/src/push-schema.ts b/packages/database/src/push-schema.ts index 6878971a..83d6f358 100644 --- a/packages/database/src/push-schema.ts +++ b/packages/database/src/push-schema.ts @@ -1020,7 +1020,7 @@ async function main() { permission_key varchar(100) NOT NULL, created_at timestamptz NOT NULL DEFAULT now() )`, - `CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique ON role_permissions (role_id, permission_key)`, + `CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_perm_unique ON role_permissions (property_id, role_id, permission_key)`, `CREATE TABLE IF NOT EXISTS user_roles ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), property_id uuid NOT NULL REFERENCES properties(id), diff --git a/packages/database/src/schema/rbac.ts b/packages/database/src/schema/rbac.ts index 0cff1a41..da6e9207 100644 --- a/packages/database/src/schema/rbac.ts +++ b/packages/database/src/schema/rbac.ts @@ -93,7 +93,12 @@ export const rolePermissions = pgTable( createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), }, (t) => ({ - rolePermUnique: uniqueIndex('role_permissions_role_perm_unique').on(t.roleId, t.permissionKey), + // Grants are property-scoped even when role_id points at a global system role. + rolePermUnique: uniqueIndex('role_permissions_role_perm_unique').on( + t.propertyId, + t.roleId, + t.permissionKey, + ), }), );