diff --git a/alembic/versions/0109_disable_rls_on_login_related_tables.py b/alembic/versions/0109_disable_rls_on_login_related_tables.py new file mode 100644 index 0000000..294d1ea --- /dev/null +++ b/alembic/versions/0109_disable_rls_on_login_related_tables.py @@ -0,0 +1,66 @@ +"""Disable RLS on login-related tables (roles, permissions). + +⚠️ KI / AGENT HINWEIS — BITTE VOR ÄNDERUNGEN LESEN ⚠️ + +Migration 0108 hat RLS auf alle Tabellen mit tenant_id aktiviert, darunter auch +`roles` und `permissions`. Diese Tabellen werden vom Login-Flow gelesen um die +Benutzerrolle zu bestimmen. Mit RLS auf diesen Tabellen und keinem tenant_id +Kontext beim Login → Query gibt nichts zurück → Login schlägt fehl mit 401. + +Diese Tabellen MÜSSEN ohne RLS bleiben, genau wie users, sessions, tenants, +user_tenants, password_reset_tokens (siehe 0108 LOGIN_TABLES). + +Lektion: RLS darf NICHT auf Tabellen aktiviert werden die vom Login-Flow +gelesen werden, wenn der Login-Flow noch keinen tenant_id Kontext hat. + +Revision ID: 0109 +Revises: 0108 +""" + +from __future__ import annotations + +from alembic import op + +revision = "0109" +down_revision = "0108" +branch_labels = None +depends_on = None + + +# ⚠️ LOGIN-RELATED TABLES — KEIN RLS! Diese Tabellen werden vom Login-Flow +# gelesen bevor ein tenant_id Kontext existiert. RLS blockiert den Login. +# Siehe auch 0108 LOGIN_TABLES und 0085 AUTH_TABLES. +LOGIN_RELATED_TABLES = [ + "roles", + "permissions", +] + + +def _exec(sql: str) -> None: + op.execute(sql) + + +def upgrade() -> None: + # Disable RLS on login-related tables + for table in LOGIN_RELATED_TABLES: + _exec( + f"DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables " + f"WHERE table_schema = 'public' AND table_name = '{table}') THEN " + f"DROP POLICY IF EXISTS tenant_isolation ON public.{table}; " + f"DROP POLICY IF EXISTS {table}_tenant_isolation ON public.{table}; " + f"ALTER TABLE public.{table} DISABLE ROW LEVEL SECURITY; " + f"END IF; END $$" + ) + + # Ensure crm_auth can read roles and permissions (needed for login flow) + for table in LOGIN_RELATED_TABLES: + _exec( + f"DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables " + f"WHERE table_schema = 'public' AND table_name = '{table}') THEN " + f"GRANT SELECT ON public.{table} TO crm_auth; " + f"END IF; END $$" + ) + + +def downgrade() -> None: + pass