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:
+20
-10
@@ -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?;
|
||||||
|
|||||||
Reference in New Issue
Block a user