from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import require_roles from app.core.security import hash_password from app.db.session import get_db from app.models.auth import Benutzer, BenutzerRolle, RolleTyp from app.schemas.benutzer import BenutzerCreate, BenutzerRead, BenutzerUpdate router = APIRouter() _admin_only = require_roles(RolleTyp.administration) # Lesend auch für Material-/Leitungsverantwortliche (Rollen-Vorgabe): brauchen # die Liste u.a. für Kontrollverantwortung/Zuständigkeit-Auswahl im Frontend - # Anlegen/Ändern bleibt admin-only. _lesend = require_roles(RolleTyp.administration, RolleTyp.materialverantwortlicher, RolleTyp.leitungsverantwortlicher) @router.get("/benutzer", response_model=list[BenutzerRead]) async def liste_benutzer(db: AsyncSession = Depends(get_db), _=Depends(_lesend)) -> list[BenutzerRead]: result = await db.execute(select(Benutzer)) return [BenutzerRead.from_orm_benutzer(b) for b in result.scalars().all()] @router.post("/benutzer", response_model=BenutzerRead, status_code=status.HTTP_201_CREATED) async def erstelle_benutzer( payload: BenutzerCreate, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> BenutzerRead: result = await db.execute(select(Benutzer).where(Benutzer.login == payload.login)) if result.scalar_one_or_none() is not None: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Login bereits vergeben") benutzer = Benutzer( name=payload.name, login=payload.login, email=payload.email, passwort_hash=hash_password(payload.passwort), aktiv=True, einheit_id=payload.einheit_id, ) db.add(benutzer) await db.flush() for rolle in payload.rollen: db.add(BenutzerRolle(benutzer_id=benutzer.id, rolle=rolle)) await db.flush() await db.refresh(benutzer, attribute_names=["rollen"]) return BenutzerRead.from_orm_benutzer(benutzer) @router.patch("/benutzer/{benutzer_id}", response_model=BenutzerRead) async def aendere_benutzer( benutzer_id: int, payload: BenutzerUpdate, db: AsyncSession = Depends(get_db), _=Depends(_admin_only), ) -> BenutzerRead: benutzer = await db.get(Benutzer, benutzer_id) if benutzer is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Benutzer nicht gefunden") if payload.name is not None: benutzer.name = payload.name if payload.email is not None: benutzer.email = payload.email if payload.aktiv is not None: benutzer.aktiv = payload.aktiv if "einheit_id" in payload.model_fields_set: # explizit gesetzt (auch null = Einheit lösen) unterscheiden von "nicht # im Request enthalten" - anders als die übrigen Felder hier, da null # ein gültiger, gewollter Zielzustand ist (Bugfix vor Auslieferung). benutzer.einheit_id = payload.einheit_id if payload.rollen is not None: result = await db.execute(select(BenutzerRolle).where(BenutzerRolle.benutzer_id == benutzer_id)) for bestehende in result.scalars().all(): await db.delete(bestehende) await db.flush() for rolle in payload.rollen: db.add(BenutzerRolle(benutzer_id=benutzer_id, rolle=rolle)) await db.flush() await db.refresh(benutzer, attribute_names=["rollen"]) return BenutzerRead.from_orm_benutzer(benutzer)