fix(load-seed): write storage.role_grants instead of access_grants

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).
This commit is contained in:
Edouard Vanbelle
2026-06-18 09:50:21 +02:00
parent fd467b2d3b
commit abc75962e0
+20 -10
View File
@@ -255,25 +255,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?; .await?;
println!("[load-seed] inserting grants…"); 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( insert_grant(
&pool, &pool,
"user", "user",
grantee.id, grantee.id,
"folder", "folder",
shared_subtree.root, shared_subtree.root,
"read", "viewer",
admin.id, admin.id,
) )
.await?; .await?;
// Grant the outermost group read on group_subtree.root. // Grant the outermost group the viewer role on group_subtree.root.
insert_grant( insert_grant(
&pool, &pool,
"group", "group",
nested_groups.root, nested_groups.root,
"folder", "folder",
group_subtree.root, group_subtree.root,
"read", "viewer",
admin.id, admin.id,
) )
.await?; .await?;
@@ -711,20 +712,29 @@ async fn insert_grant(
subject_id: Uuid, subject_id: Uuid,
resource_type: &str, resource_type: &str,
resource_id: Uuid, resource_id: Uuid,
permission: &str, role: &str,
granted_by: Uuid, granted_by: Uuid,
) -> Result<(), sqlx::Error> { ) -> 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( sqlx::query(
"INSERT INTO storage.access_grants "INSERT INTO storage.role_grants
(subject_type, subject_id, resource_type, resource_id, permission, granted_by) (subject_type, subject_id, resource_type, resource_id, role, granted_by)
VALUES ($1, $2, $3, $4, $5, $6) VALUES ($1, $2, $3, $4, $5::storage.grant_role, $6)
ON CONFLICT (subject_type, subject_id, resource_type, resource_id, permission) DO NOTHING", ON CONFLICT (subject_type, subject_id, resource_type, resource_id) DO NOTHING",
) )
.bind(subject_type) .bind(subject_type)
.bind(subject_id) .bind(subject_id)
.bind(resource_type) .bind(resource_type)
.bind(resource_id) .bind(resource_id)
.bind(permission) .bind(role)
.bind(granted_by) .bind(granted_by)
.execute(pool) .execute(pool)
.await?; .await?;