From abc75962e0ccbec2df5c9ca934694f19406b73bc Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Thu, 18 Jun 2026 09:50:21 +0200 Subject: [PATCH] fix(load-seed): write storage.role_grants instead of access_grants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D-Prep (migration 20260801000002_drop_access_grants) drops storage.access_grants and replaces it with storage.role_grants — one row per role assignment instead of N rows per permission bundle. The load seeder still spoke the old per-permission shape and failed every nightly with `relation "storage.access_grants" does not exist`. Both seeder call sites already grant the read bundle (single permission), which maps cleanly to the `viewer` role; switching them to the new schema is a one-row INSERT with the role name. The conflict key drops `permission` since uniqueness is now per (subject, resource). --- src/bin/load-seed.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/bin/load-seed.rs b/src/bin/load-seed.rs index 34208b99..eca0c6e7 100644 --- a/src/bin/load-seed.rs +++ b/src/bin/load-seed.rs @@ -255,25 +255,26 @@ async fn main() -> Result<(), Box> { .await?; println!("[load-seed] inserting grants…"); - // Grant the grantee read on shared_subtree.root. + // Grant the grantee the viewer role on shared_subtree.root — the read + // bundle the share_cascade_rebac scenario exercises. insert_grant( &pool, "user", grantee.id, "folder", shared_subtree.root, - "read", + "viewer", admin.id, ) .await?; - // Grant the outermost group read on group_subtree.root. + // Grant the outermost group the viewer role on group_subtree.root. insert_grant( &pool, "group", nested_groups.root, "folder", group_subtree.root, - "read", + "viewer", admin.id, ) .await?; @@ -711,20 +712,29 @@ async fn insert_grant( subject_id: Uuid, resource_type: &str, resource_id: Uuid, - permission: &str, + role: &str, granted_by: Uuid, ) -> Result<(), sqlx::Error> { + // D-Prep replaced `storage.access_grants` (one row per Permission) with + // `storage.role_grants` (one row per role assignment; the role expands to + // a permission bundle in-code at engine read time). The seeder now writes + // role names ('viewer'/'editor'/etc.) instead of individual permissions. + // + // The `role` column was promoted from TEXT to the `storage.grant_role` + // enum by migration 20260801000000_role_grants_enum, so the cast on $5 + // is required — sqlx binds Rust &str as TEXT, which postgres won't + // implicitly coerce into the enum. sqlx::query( - "INSERT INTO storage.access_grants - (subject_type, subject_id, resource_type, resource_id, permission, granted_by) - VALUES ($1, $2, $3, $4, $5, $6) - ON CONFLICT (subject_type, subject_id, resource_type, resource_id, permission) DO NOTHING", + "INSERT INTO storage.role_grants + (subject_type, subject_id, resource_type, resource_id, role, granted_by) + VALUES ($1, $2, $3, $4, $5::storage.grant_role, $6) + ON CONFLICT (subject_type, subject_id, resource_type, resource_id) DO NOTHING", ) .bind(subject_type) .bind(subject_id) .bind(resource_type) .bind(resource_id) - .bind(permission) + .bind(role) .bind(granted_by) .execute(pool) .await?;