fix: inherit scoped function ownership for Alembic migrations

This commit is contained in:
2026-10-12 23:06:38 +08:00
parent 5061084d4c
commit 09af862957
2 changed files with 10 additions and 7 deletions
+5 -1
View File
@@ -66,7 +66,11 @@ refuses unexpected existing authority. There is no application table DDL or
automatic account bootstrap: the migration login owns the database without
global superuser/create-role/create-db/bypass-RLS rights; Backend Alembic remains
the only schema/grant migration entry. Runtime logins cannot grant/set group
authority. See [PostgreSQL role membership](https://www.postgresql.org/docs/18/role-membership.html)
authority. The migration login inherits the narrow NOLOGIN function-owner role
and can SET that role, so unchanged Alembic can grant/rename its functions after
ownership transfer. Membership has no ADMIN option, adds no global role-creation
privilege, and is never given to runtime logins. See
[PostgreSQL role membership](https://www.postgresql.org/docs/18/role-membership.html)
and [function ownership](https://www.postgresql.org/docs/18/sql-alterfunction.html).
Always bootstrap validation from a separately trusted tool copy and an out-of-band
+5 -6
View File
@@ -114,16 +114,13 @@ async def _provision(operator: Path, endpoint_path: Path) -> dict:
)
]
expected = (
[]
if not login
else [(MEMBERSHIP[name], False, name != "kf_migrator", name == "kf_migrator")]
[] if not login else [(MEMBERSHIP[name], False, True, name == "kf_migrator")]
)
if memberships and memberships != expected:
raise DeliveryError("existing DB role membership differs")
for login, group in MEMBERSHIP.items():
inherit = "FALSE" if login == "kf_migrator" else "TRUE"
set_role = "TRUE" if login == "kf_migrator" else "FALSE"
for option, value in (("ADMIN", "FALSE"), ("INHERIT", inherit), ("SET", set_role)):
for option, value in (("ADMIN", "FALSE"), ("INHERIT", "TRUE"), ("SET", set_role)):
# Each command uses the documented single membership-option
# form; all three changes are in the same transaction.
await db.execute(f"GRANT {group} TO {login} WITH {option} {value}")
@@ -164,7 +161,9 @@ def provision(operator: Path, endpoint_path: Path) -> dict:
Pre-created NOLOGIN groups allow the unchanged Backend Alembic migrations
to run under a separate non-superuser database owner. Only this migration
login can SET the authority-function owner; ordinary logins inherit their
login inherits and can SET the narrow authority-function owner, allowing
unchanged Alembic to grant/rename its already owned functions without global
superuser/create-role privileges. Ordinary logins inherit their
own one group and cannot grant it. Unexpected existing authority is refused.
"""
if os.geteuid() != 0: