Skip to content

fix: only treat default privileges FOR ROLE the applying role as auto-granting (#696) - #704

Merged
tianzhou merged 2 commits into
mainfrom
fix/issue-696-default-privilege-role
Nov 8, 2026
Merged

tianzhou merged 2 commits into
mainfrom
fix/issue-696-default-privilege-role

Conversation

@tianzhou

@tianzhou tianzhou commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A default privilege only fires for objects created by its FOR ROLE owner, but the diff treated every default as if it would fire for the tables a migration creates. With ALTER DEFAULT PRIVILEGES FOR ROLE other_role ... GRANT SELECT ON TABLES TO reader:

  • dump omitted the explicit GRANT SELECT ON TABLE t TO reader on every covered table,
  • apply as a role other than other_role then produced tables without the grant (and emitted a REVOKE for the grant it assumed would be auto-created),
  • a re-plan reported No changes detected while reader had lost access.

The plan cannot know which role will run it: apply may run as a different role than plan, a saved plan may be executed by anyone, and a dump has no apply role at all. So the diff is now role-independent in both directions:

  • Explicit grants on new tables are always emitted, even when an active default would also grant them. A redundant GRANT is a no-op when the default did fire; a missing one was a silent privilege loss. This is the only behavior change from main.
  • The conservative REVOKEs for privileges a default would auto-grant on a new table but the desired state does not include (Default privileges + selective REVOKE requires two apply cycles to converge #253) are computed against every active default, as before. The REVOKE is a no-op when the default did not fire and withholds the access when it did, so a plan or dump stays correct under whichever role restores it.

The "don't REVOKE an existing grant that a desired default covers" rule from #250 is unchanged.

Fixes #696

Test plan

  • New case testdata/diff/default_privilege/issue_696_for_role_other_owner: a FOR ROLE app_owner default, plus a new table with an explicit grant and one without. Before: no GRANT and a bogus-looking REVOKE. Now: GRANT SELECT ON TABLE accounts TO app_reader plus the conservative REVOKE SELECT ON TABLE audit_log FROM app_reader; apply as the test role and re-plan are idempotent.
  • New unit test TestDumpKeepsExplicitGrantsAndWithheldDefaults (internal/diff): a dump keeps both the explicit grant and the REVOKE for a withheld grant. Fails on main (the GRANT is missing).
  • default_privilege/add_table_privilege and privilege/revoke_default_privilege now also list the explicit grants on their new tables; all other cases in both categories are unchanged.
  • Verified against the reproduction in Default privileges FOR ROLE drop explicit table grants: apply as another role revokes them and plan reports no changes #696 end to end: dump keeps the grant, apply as postgres grants it, has_table_privilege is true, re-plan is clean.
PGSCHEMA_TEST_FILTER="default_privilege/" go test ./cmd -run TestPlanAndApply
PGSCHEMA_TEST_FILTER="privilege/" go test ./cmd -run TestPlanAndApply
go test ./internal/diff

🤖 Generated with Claude Code

…-granting (#696)

A default privilege fires only for objects created by its FOR ROLE owner, and
apply creates objects as the role it connects with. The diff treated every
default privilege as active for new tables, so with ALTER DEFAULT PRIVILEGES
FOR ROLE other_role the dump omitted the explicit table grants, apply emitted
a REVOKE for the grant it assumed would appear, and a re-plan saw no drift.

The inspector now records the connected role in the IR metadata, and the
active-default shortcut (skip covered GRANTs on new tables, REVOKE uncovered
auto-grants) only considers defaults whose owner is that role. A dump has no
apply role and therefore emits every explicit grant, like pg_dump.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 8, 2026 08:49
@greptile-apps

greptile-apps Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 1/5

[High impact] The change affects privilege plans for tables created by pgschema.

Fix the two paths that can grant unintended table access before merging.

Findings

  1. P1 Security Dumps restore withheld access ▶
  2. P1 Security Saved plans grant unexpected access ▶

Summary

Records current_user in Metadata.CurrentRole and limits assumed automatic table grants to defaults owned by that role.

  • Migration plans match default grants to the role creating each table.

Reviews (1) · Last reviewed commit: "fix: only treat default privileges FOR R..." · Reviewed by Greptile

Comment thread internal/diff/diff.go Resolved
Comment thread internal/diff/diff.go Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical privilege-handling issues can omit required grants or revokes when saved plans execute under a different role and restore explicitly revoked access when dumps are reloaded.

2 open issues
What changed in this PR

This PR addresses #696 by making pgschema’s default-privilege handling depend on the inspected role, preserving explicit grants when another role owns the defaults.

Changes:

  • Records the connected role in IR metadata.
  • Filters automatic-grant assumptions by role and retains explicit grants in dumps.
  • Adds regression fixtures covering new tables with and without explicit grants under another role’s defaults.
File Description
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​plan.txt Expected human-readable migration plan.
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​plan.sql Expected executable plan SQL.
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​plan.json Expected serialized migration plan.
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​old.sql Establishes another role’s default privileges.
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​new.sql Adds tables with and without explicit SELECT grants.
testdata/​diff/​default_privilege/​issue_696_for_role_other_owner/​diff.sql Expects the explicit GRANT without an unnecessary REVOKE.
ir/​ir.go Adds current-role metadata.
ir/​inspector.go Captures current_user during inspection.
internal/​diff/​diff.go Restricts active defaults to the inspected role.

Review effort: Balanced

Comment thread internal/diff/diff.go Resolved
Comment thread internal/diff/diff.go Resolved
…default privileges (#696)

Review follow-up: scoping the default-privilege shortcut to the planning
role made saved plans role-dependent and dropped the conservative REVOKEs
from dumps, which would restore withheld access when a dump is reloaded as
the default's owner.

The diff is now role-independent: explicit grants on new tables are always
emitted (a no-op when the default also fired), and the REVOKEs for
auto-granted privileges the desired state omits are computed against every
active default as before. IR.Metadata.CurrentRole is removed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are identified for the changes preserving explicit table grants while retaining conservative REVOKEs.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

@tianzhou
tianzhou merged commit 1c4b052 into main Nov 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default privileges FOR ROLE drop explicit table grants: apply as another role revokes them and plan reports no changes

2 participants