87 lines
2.3 KiB
Python
87 lines
2.3 KiB
Python
|
|
"""Enable RLS on all remaining tenant tables.
|
||
|
|
|
||
|
|
Revision ID: 0064
|
||
|
|
Revises: 0063
|
||
|
|
Create Date: 2026-07-29
|
||
|
|
|
||
|
|
Currently RLS is only on contacts. This migration enables RLS on all
|
||
|
|
tenant-scoped tables that have a tenant_id column but no RLS yet.
|
||
|
|
|
||
|
|
System tables (users, tenants, groups, roles) are excluded — they need
|
||
|
|
special handling for the login bootstrap process.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from alembic import op
|
||
|
|
|
||
|
|
revision = "0064"
|
||
|
|
down_revision = "0063"
|
||
|
|
branch_labels = None
|
||
|
|
depends_on = None
|
||
|
|
|
||
|
|
# Tables that should have RLS (tenant-scoped data)
|
||
|
|
TENANT_TABLES = [
|
||
|
|
"addresses",
|
||
|
|
"attachments",
|
||
|
|
"bank_accounts",
|
||
|
|
"contact_folders",
|
||
|
|
"contact_merge_history",
|
||
|
|
"workflows",
|
||
|
|
"sequences",
|
||
|
|
"saved_filters",
|
||
|
|
"saved_views",
|
||
|
|
"webhooks",
|
||
|
|
"custom_field_definitions",
|
||
|
|
"notifications",
|
||
|
|
"ai_conversations",
|
||
|
|
"contact_persons",
|
||
|
|
"tags",
|
||
|
|
"entity_links",
|
||
|
|
"dms_files",
|
||
|
|
"dms_folders",
|
||
|
|
"calendar_events",
|
||
|
|
"calendars",
|
||
|
|
"tasks",
|
||
|
|
"task_lists",
|
||
|
|
"mail_messages",
|
||
|
|
"mail_accounts",
|
||
|
|
"mail_folders",
|
||
|
|
"conversations",
|
||
|
|
"conversation_messages",
|
||
|
|
"conversation_participants",
|
||
|
|
"audit_log",
|
||
|
|
"permission_delegations",
|
||
|
|
"guest_invitations",
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def upgrade() -> None:
|
||
|
|
for table in TENANT_TABLES:
|
||
|
|
# Enable RLS if not already enabled
|
||
|
|
op.execute(f"""
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (
|
||
|
|
SELECT 1 FROM pg_class c
|
||
|
|
WHERE c.relname = '{table}'
|
||
|
|
AND c.relrowsecurity = true
|
||
|
|
) AND EXISTS (
|
||
|
|
SELECT 1 FROM information_schema.columns
|
||
|
|
WHERE table_name = '{table}'
|
||
|
|
AND column_name = 'tenant_id'
|
||
|
|
) THEN
|
||
|
|
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
|
||
|
|
|
||
|
|
CREATE POLICY {table}_tenant_isolation ON {table}
|
||
|
|
FOR ALL
|
||
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid)
|
||
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
||
|
|
END IF;
|
||
|
|
END $$;
|
||
|
|
""")
|
||
|
|
|
||
|
|
|
||
|
|
def downgrade() -> None:
|
||
|
|
for table in TENANT_TABLES:
|
||
|
|
op.execute(f"DROP POLICY IF EXISTS {table}_tenant_isolation ON {table}")
|
||
|
|
op.execute(f"ALTER TABLE {table} DISABLE ROW LEVEL SECURITY")
|