fix(replica): re-apply extra user schema grants after schema migration - #136
Open
julianam-w wants to merge 2 commits into
Open
fix(replica): re-apply extra user schema grants after schema migration#136julianam-w wants to merge 2 commits into
julianam-w wants to merge 2 commits into
Conversation
The restore init script grants each extra user its declared schemas while the temporary postgres is up, filtered by a JOIN on pg_namespace. A persistent_schemas schema does not exist in the restored snapshot at that point, so the filter matches nothing and the grants are silently skipped; where it does exist, the operator drops it before the migration Job rewrites the canonical copy with --no-privileges, taking the schema grant and the schema-scoped pg_default_acl row with it. Either way the extra user ended up with no USAGE on the schema it was configured to read. Re-apply the grants from the operator once redaction and the migration have settled and before the Service selector moves, so no client sees the restore ungranted. Skipped when the connected role is no longer a superuser: on a restore already locked read-only nothing has rewritten a schema since init, so init's own grants stand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fake_first_name() draws from anon's dictionary, which contains the names the fixture starts with, so a correctly masked row can land on a name another row began with. The assertion checked whether any original name appeared anywhere in the column, so that legitimate outcome failed the test: Eve masked to Alice and tripped the check on row 1's original. Compare each row against its own original instead, tolerating a single self-collision, and assert the CASE-WHEN branch directly - a single name must come back without a space. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Extra users scoped to a
persistentSchemasschema were left with noUSAGEon it — the configured grants silently never took effect.The grants only ever ran in the restore's init script, filtered by
JOIN pg_namespace n ON n.nspname = trim(s.name). For apersistentSchemasreplica that filter matches nothing:dbtdoesn't exist in the restored snapshot yet, it arrives later via the migration Job. And where the schema does exist in the snapshot, the operator drops it before the migration writes the canonical copy —DROP SCHEMA … CASCADEtakes every grant on it, plus the schema-scopedpg_default_aclrow behindALTER DEFAULT PRIVILEGES … IN SCHEMA, and the migration then recreates it with--no-privileges.Either path ends with the extra user unable to read the schema it was configured for, with nothing in the logs.
Fix
Re-apply the grants from the operator (per AGENTS.md: prefer the operator over job-side SQL), once redaction and the migration have settled and before the Service selector moves — so no client ever sees the restore ungranted.
postgres::schema_read_grants(pure, testable),grant_schema_read_on,is_superuser_on,connectable_databases_onreplica::reconcile_extra_user_grants, called fromreconcileahead of the switchover blockThe init-script pass stays: it's the only pass for read-only restores, where the analytics credential holds
pg_read_all_datarather thanSUPERUSERand so can't grant on schemas it doesn't own. The operator step probes for that and skips — on a restore already locked read-only nothing has rewritten a schema since init, so init's grants stand.Users with an empty
schemaslist are deliberately ungranted and stay that way.Tests
Six unit tests: generated SQL, identifier quoting (role and schema names come from the CRD), read-only-ness of the grants, and the user-list gating.
New integration test
tests/extra_user_grants.rs(+ matrix entry) — needs CI, doesn't run locally. It asserts, on the restore the migration wrote:USAGEon the migrated schemaSELECTthe migrated rowspg_default_aclhalf)publicstays ungranted, so the grant is still scoped to what was declaredNote
cargo checkcan't build this repo on Windows —reqwest::ClientBuilder::unix_socket,tokio::signal::unix, and twocanopy/verification.rstest assertions. All pre-existing onmain; those files are untouched here.🤖 Generated with Claude Code