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?;
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?;