fix: apply the service_role grant migration in one transaction
WHY THIS MATTERS
This migration REVOKEs every privilege service_role holds on the
application's tables and sequences, then GRANTs back the narrower set the
backend actually needs. On a live deployment those two steps were not
atomic: CI (and the documented upgrade path) apply migrations with
psql --set ON_ERROR_STOP=1 --file <migration>
and plain psql, without the -1/--single-transaction flag, runs each SQL
statement in its own autocommitted transaction. That means there was a
real moment - after `revoke all ... from service_role` committed and
before the following `grant select, insert, update, delete` committed -
when service_role had ZERO privileges on every table. Any backend query
racing through that window fails with "permission denied", i.e. a brief
production outage caused by a security-hardening migration.
WHAT IS AUTOCOMMIT VS. AN EXPLICIT TRANSACTION
PostgreSQL always runs statements inside transactions. If you do not open
one yourself, each statement gets its own ("autocommit"), and its effects
become visible to every other session the instant it completes:
revoke all privileges on all tables ... ; -- visible immediately!
-- <-- other sessions now see service_role with no privileges
grant select, insert, update, delete ... ; -- visible only now
Wrapping the statements in `begin; ... commit;` changes when other
sessions see the effects: nothing is visible until COMMIT, and then
everything is visible at once. DDL and privilege changes are fully
transactional in PostgreSQL (unlike some other databases), so this is a
supported and standard pattern:
begin;
revoke all privileges on all tables ... ;
grant select, insert, update, delete ... ;
commit;
Concurrent queries either see the old grants (before commit) or the final
narrowed grants (after commit) - never the empty in-between state.
HOW THE FIX WORKS
The migration file now opens with `begin;` and ends with `commit;`. The
revoke+regrant pairs for tables and for sequences all sit inside that one
transaction, so applying the file with plain psql is atomic. A bonus:
with ON_ERROR_STOP=1, a failure partway through now rolls the whole file
back instead of leaving service_role stripped of privileges with no
re-grant applied.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | 2f667486 |
| Stats | 1 file changed , +12 |
| Part of | Add schema drift checks for database upgrades |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-317c5559.md
from inside the repo you want the change in.