Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Row-level security policies

pgevolve manages Postgres RLS declaratively. Tables carry:

  • rls_enabled: boolALTER TABLE t ENABLE/DISABLE ROW LEVEL SECURITY.
  • rls_forced: boolALTER TABLE t FORCE/NO FORCE ROW LEVEL SECURITY (applies even to the table owner).
  • policies: Vec<Policy> — embedded; policies can't exist orphan.

Source surface

CREATE POLICY author_only ON app.docs
    AS PERMISSIVE              -- (default)
    FOR ALL                    -- (default)
    TO public                  -- (default)
    USING (author = current_user);

ALTER TABLE app.docs ENABLE ROW LEVEL SECURITY;
ALTER TABLE app.docs FORCE ROW LEVEL SECURITY;

ALTER POLICY and DROP POLICY are rejected in source — both come from the diff against the catalog.

Tests: tier-1: crates/pgevolve-core/src/ir/policy.rs::tests, parse/builder/policy_stmt.rs::tests, diff/policies.rs::tests; tier-2: crates/pgevolve-core/tests/catalog_policies.rs; tier-C: objects/policies/simple-permissive-policy, policy-with-check, policy-with-roles, restrictive-policy, enable-rls, disable-rls, force-rls-toggle, drop-policy-on-source-removal.

Command-kind changes recreate

PG's ALTER POLICY can change roles, USING, and WITH CHECK but NOT the command kind. If source changes a policy from FOR SELECT to FOR INSERT, pgevolve emits DROP POLICY + CREATE POLICY as two separate plan steps.

Tests: tier-1: crates/pgevolve-core/src/plan/rewrite/policies.rs::tests; tier-C: objects/policies/alter-policy-command-recreates, alter-policy-roles.

Cross-cluster role validation

Policy TO clauses reference roles. The v0.3.1 cross-cluster lint grant-references-unknown-role extends to policy roles when [cluster].project is set in pgevolve.toml.

FORCE without policies = denial

PG's behavior: FORCE ROW LEVEL SECURITY with no policies defined denies every row, including for the table owner. Almost always a configuration mistake. The force-rls-without-policies lint warns on this state.

Tests: tier-1: crates/pgevolve-core/src/lint/rules/force_rls_without_policies.rs::tests; tier-C: objects/policies/lint.

WITH CHECK validity

WITH CHECK is invalid on FOR SELECT and FOR DELETE policies (PG rejects). The source parser pre-empts with a clear error.

Tests: tier-1: crates/pgevolve-core/src/parse/builder/policy_stmt.rs::tests.

Out of scope

  • ALTER POLICY ... RENAME TO ... in source — rejected. Operators can drop+create.
  • SECURITY LABEL — not planned.
  • leakproof / security_barrier on views — future.