62 lines
2.0 KiB
Python
62 lines
2.0 KiB
Python
"""create the least-privilege plugin DB role (SPEC §11.1 RLS enforcement)
|
|||
|
|
|
||
|
|
Plugin table access runs as ``webmail_plugin`` — a NOLOGIN, NOSUPERUSER,
|
||
|
|
NOBYPASSRLS role that plugin sessions ``SET LOCAL ROLE`` into per transaction.
|
||
|
|
Row-Level Security is skipped for superusers, table owners, and BYPASSRLS roles,
|
||
|
|
so per-user isolation on ``plg_*`` tables is only real when access runs as this
|
||
|
|
restricted role. The app role is granted membership so it can SET ROLE into it
|
||
|
|
(the dev role happens to be superuser, but this keeps prod parity where it isn't).
|
||
|
|
|
||
|
|
Per-table SELECT/INSERT/UPDATE/DELETE grants + policies are added by each plugin's
|
||
|
|
own migration via ``app.plugins.rls.enable_rls`` (§11.1); this only provisions the
|
||
|
|
role and schema usage.
|
||
|
|
|
||
|
|
Revision ID: 0014
|
||
|
|
Revises: 0013
|
||
|
|
Create Date: 2026-07-26
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from collections.abc import Sequence
|
||
|
|
|
||
|
|
from alembic import op
|
||
|
|
|
||
|
|
revision: str = "0014"
|
||
|
|
down_revision: str | None = "0013"
|
||
|
|
branch_labels: str | Sequence[str] | None = None
|
||
|
|
depends_on: str | Sequence[str] | None = None
|
||
|
|
|
||
|
|
|
||
|
|
def upgrade() -> None:
|
||
|
|
op.execute(
|
||
|
|
"""
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'webmail_plugin') THEN
|
||
|
|
CREATE ROLE webmail_plugin NOLOGIN NOSUPERUSER NOBYPASSRLS NOCREATEDB NOCREATEROLE;
|
||
|
|
END IF;
|
||
|
|
END
|
||
|
|
$$;
|
||
|
|
"""
|
||
|
|
)
|
||
|
|
op.execute("GRANT USAGE ON SCHEMA public TO webmail_plugin")
|
||
|
|
# Let the connecting application role drop into the restricted role via SET ROLE.
|
||
|
|
op.execute("GRANT webmail_plugin TO CURRENT_USER")
|
||
|
|
|
||
|
|
|
||
|
|
def downgrade() -> None:
|
||
|
|
op.execute(
|
||
|
|
"""
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'webmail_plugin') THEN
|
||
|
|
EXECUTE 'REVOKE webmail_plugin FROM CURRENT_USER';
|
||
|
|
EXECUTE 'DROP OWNED BY webmail_plugin';
|
||
|
|
EXECUTE 'DROP ROLE webmail_plugin';
|
||
|
|
END IF;
|
||
|
|
END
|
||
|
|
$$;
|
||
|
|
"""
|
||
|
|
)
|