fix(security): disable RLS on roles/permissions — blocks login (0109)

This commit is contained in:
Agent Zero
2026-08-06 01:05:33 +02:00
parent 6631615bef
commit 85fcb90b32
@@ -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