Files
leocrm/alembic/versions/0015_rls_policies.py
T

65 lines
1.7 KiB
Python

"""Enable Row Level Security with tenant isolation policies on core tables.
Revision ID: 0015_rls_policies
Revises: 0014_currency_unique_fix
Create Date: 2026-07-04
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
revision: str = "0015_rls_policies"
down_revision: Union[str, None] = "0014_currency_unique_fix"
branch_labels: Union[str, Sequence[str], None] = None
dends_on: Union[str, Sequence[str], None] = None
# Tables with tenant_id column that get RLS
RLS_TABLES = [
"companies",
"contacts",
"company_contacts",
"currencies",
"tax_rates",
"sequences",
"system_settings",
"attachments",
"addresses",
"users",
"roles",
"sessions",
"audit_log",
"deletion_log",
"notifications",
"ai_conversations",
"ai_messages",
"workflows",
"workflow_instances",
"workflow_step_history",
"password_reset_tokens",
]
# Tables without tenant_id — no RLS (plugins, plugin_migrations are global)
NO_RLS_TABLES = ["plugins", "plugin_migrations"]
def upgrade() -> None:
for table_name in RLS_TABLES:
# Enable RLS on the table
op.execute(f"ALTER TABLE {table_name} ENABLE ROW LEVEL SECURITY")
# Create tenant isolation policy
# USING clause: tenant_id must match the session variable set by the app
op.execute(
f"CREATE POLICY tenant_isolation ON {table_name} "
f"USING (tenant_id = current_setting('app.current_tenant_id')::uuid)"
)
def downgrade() -> None:
for table_name in reversed(RLS_TABLES):
op.execute(f"DROP POLICY IF EXISTS tenant_isolation ON {table_name}")
op.execute(f"ALTER TABLE {table_name} DISABLE ROW LEVEL SECURITY")