"""Granulares Rechte-System (Roadmap Phase 6): additiv neben dem bestehenden 4-Rollen-System (mitarbeiter/materialverantwortlicher/leitungsverantwortlicher/ administration bleiben unverändert, gleiches enum-basiertes benutzer_rolle). Neu: berechtigung (Katalog einzelner Rechte, z.B. "material.erstellen"), rolle (vom Admin frei anlegbare Rollen wie "Materialwart"/"Helfer"), rolle_berechtigung (M:N), benutzer_rolle_zuordnung (M:N Benutzer<->neue Rolle - bewusst eigene Tabelle statt Wiederverwendung von benutzer_rolle, da dessen rolle-Spalte ein fester ENUM ist, keine FK auf frei anlegbare Zeilen). Ein Benutzer kann künftig BEIDES gleichzeitig haben: eine feste Rolle (steuert weiterhin alle bisherigen require_roles()-Endpunkte unverändert) und/oder eine oder mehrere frei konfigurierte Rollen (steuert die neuen, granular geprüften Endpunkte über require_permission()). Revision ID: 0017_granulare_rollen Revises: 0016_dokument Create Date: 2026-09-05 """ from typing import Sequence, Union from alembic import op revision: str = "0017_granulare_rollen" down_revision: Union[str, None] = "0016_dokument" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None # Katalog-Startbestand: deckt die vom Nutzer genannten Beispiel-Rollen # (Materialwart, Helfer) ab. Weitere Berechtigungen können später ergänzt # werden, ohne bestehende Zeilen zu ändern (additiv). BERECHTIGUNGEN = [ ("material.ansehen", "Material ansehen"), ("material.erstellen", "Material erstellen"), ("material.bearbeiten", "Material bearbeiten"), ("lagerbewegung.durchfuehren", "Lagerbewegungen durchführen"), ("mangel.lesen", "Mängel ansehen"), ("mangel.melden", "Mängel melden"), ("mangel.bearbeiten", "Mängel bearbeiten/erledigen"), ("pruefung.durchfuehren", "Prüfungen durchführen"), ] def upgrade() -> None: op.execute( """ CREATE TABLE berechtigung ( id SERIAL PRIMARY KEY, schluessel TEXT NOT NULL UNIQUE, beschreibung TEXT NOT NULL ) """ ) op.execute( """ CREATE TABLE rolle ( id SERIAL PRIMARY KEY, name TEXT NOT NULL UNIQUE, beschreibung TEXT ) """ ) op.execute( """ CREATE TABLE rolle_berechtigung ( rolle_id INTEGER NOT NULL REFERENCES rolle(id) ON DELETE CASCADE, berechtigung_id INTEGER NOT NULL REFERENCES berechtigung(id) ON DELETE CASCADE, PRIMARY KEY (rolle_id, berechtigung_id) ) """ ) op.execute( """ CREATE TABLE benutzer_rolle_zuordnung ( benutzer_id INTEGER NOT NULL REFERENCES benutzer(id) ON DELETE CASCADE, rolle_id INTEGER NOT NULL REFERENCES rolle(id) ON DELETE CASCADE, PRIMARY KEY (benutzer_id, rolle_id) ) """ ) for schluessel, beschreibung in BERECHTIGUNGEN: op.execute( "INSERT INTO berechtigung (schluessel, beschreibung) VALUES " f"('{schluessel}', '{beschreibung}')" ) def downgrade() -> None: op.execute("DROP TABLE benutzer_rolle_zuordnung") op.execute("DROP TABLE rolle_berechtigung") op.execute("DROP TABLE rolle") op.execute("DROP TABLE berechtigung")