"""Disable RLS on all system/auth/config tables needed at startup and login. These tables are read before a tenant context is set (startup, login, plugin activation). RLS must be disabled on them to allow unprivileged (crm_api) access without tenant context. Revision ID: 0079 Revises: 0078 """ from alembic import op from sqlalchemy import text revision = "0079" down_revision = "0078" branch_labels = None depends_on = None # All tables that need to be read WITHOUT tenant context SYSTEM_TABLES = [ # Auth tables "user_tenants", "sessions", "password_reset_tokens", "api_tokens", "user_groups", "user_preferences", # RBAC tables "roles", "groups", "permissions", # Config tables "system_settings", "currencies", "tax_rates", "sequences", "saved_filters", "saved_views", "webhooks", "notification_preferences", # Plugin tables "tenant_plugin_activation", # Workspace tables (needed for workspace context before tenant filter) "workspaces", "workspace_modules", "workspace_users", "workspace_widgets", ] def upgrade() -> None: conn = op.get_bind() for table in SYSTEM_TABLES: exists = conn.execute( text(f"SELECT 1 FROM information_schema.tables WHERE table_name = '{table}'") ).fetchone() is not None if not exists: continue # Drop all RLS policies on this table policies = conn.execute(text( f"SELECT policyname FROM pg_policies WHERE tablename = '{table}'" )).fetchall() for (policyname,) in policies: conn.execute(text(f"DROP POLICY IF EXISTS {policyname} ON {table}")) conn.execute(text(f"ALTER TABLE {table} DISABLE ROW LEVEL SECURITY")) print(f" Disabled RLS on {table}") def downgrade() -> None: # Re-enabling RLS on system tables would break startup with crm_api # This is intentionally a no-op pass