from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import require_roles from app.db.session import get_db from app.models.auth import Benutzer, RolleTyp from app.models.permission import Berechtigung, BenutzerRolleZuordnung, Rolle, RolleBerechtigung from app.schemas.permission import BerechtigungRead, RolleCreate, RolleRead, RolleUpdate router = APIRouter() _admin_only = require_roles(RolleTyp.administration) async def _rolle_read(db: AsyncSession, rolle: Rolle) -> RolleRead: ids = ( await db.execute(select(RolleBerechtigung.berechtigung_id).where(RolleBerechtigung.rolle_id == rolle.id)) ).scalars().all() return RolleRead(id=rolle.id, name=rolle.name, beschreibung=rolle.beschreibung, berechtigung_ids=list(ids)) @router.get("/berechtigungen", response_model=list[BerechtigungRead]) async def liste_berechtigungen(db: AsyncSession = Depends(get_db), _=Depends(_admin_only)) -> list[Berechtigung]: result = await db.execute(select(Berechtigung)) return list(result.scalars().all()) @router.get("/rollen", response_model=list[RolleRead]) async def liste_rollen(db: AsyncSession = Depends(get_db), _=Depends(_admin_only)) -> list[RolleRead]: result = await db.execute(select(Rolle)) return [await _rolle_read(db, r) for r in result.scalars().all()] @router.post("/rollen", response_model=RolleRead, status_code=status.HTTP_201_CREATED) async def erstelle_rolle( payload: RolleCreate, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> RolleRead: rolle = Rolle(name=payload.name, beschreibung=payload.beschreibung) db.add(rolle) try: await db.flush() except IntegrityError as exc: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Rollenname bereits vergeben") from exc return await _rolle_read(db, rolle) @router.patch("/rollen/{rolle_id}", response_model=RolleRead) async def aendere_rolle( rolle_id: int, payload: RolleUpdate, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> RolleRead: rolle = await db.get(Rolle, rolle_id) if rolle is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Rolle nicht gefunden") for feld, wert in payload.model_dump(exclude_unset=True).items(): setattr(rolle, feld, wert) try: await db.flush() except IntegrityError as exc: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Rollenname bereits vergeben") from exc return await _rolle_read(db, rolle) @router.delete("/rollen/{rolle_id}", status_code=status.HTTP_204_NO_CONTENT) async def loesche_rolle(rolle_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only)) -> None: rolle = await db.get(Rolle, rolle_id) if rolle is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Rolle nicht gefunden") await db.delete(rolle) await db.flush() @router.put("/rollen/{rolle_id}/berechtigungen/{berechtigung_id}", status_code=status.HTTP_204_NO_CONTENT) async def setze_rollen_berechtigung( rolle_id: int, berechtigung_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> None: if await db.get(Rolle, rolle_id) is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Rolle nicht gefunden") if await db.get(Berechtigung, berechtigung_id) is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Berechtigung nicht gefunden") bestehend = await db.get(RolleBerechtigung, {"rolle_id": rolle_id, "berechtigung_id": berechtigung_id}) if bestehend is None: db.add(RolleBerechtigung(rolle_id=rolle_id, berechtigung_id=berechtigung_id)) await db.flush() @router.delete("/rollen/{rolle_id}/berechtigungen/{berechtigung_id}", status_code=status.HTTP_204_NO_CONTENT) async def entferne_rollen_berechtigung( rolle_id: int, berechtigung_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> None: bestehend = await db.get(RolleBerechtigung, {"rolle_id": rolle_id, "berechtigung_id": berechtigung_id}) if bestehend is not None: await db.delete(bestehend) await db.flush() @router.put("/benutzer/{benutzer_id}/rollen/{rolle_id}", status_code=status.HTTP_204_NO_CONTENT) async def weise_rolle_zu( benutzer_id: int, rolle_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> None: if await db.get(Benutzer, benutzer_id) is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Benutzer nicht gefunden") if await db.get(Rolle, rolle_id) is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Rolle nicht gefunden") bestehend = await db.get(BenutzerRolleZuordnung, {"benutzer_id": benutzer_id, "rolle_id": rolle_id}) if bestehend is None: db.add(BenutzerRolleZuordnung(benutzer_id=benutzer_id, rolle_id=rolle_id)) await db.flush() @router.delete("/benutzer/{benutzer_id}/rollen/{rolle_id}", status_code=status.HTTP_204_NO_CONTENT) async def entferne_rollen_zuordnung( benutzer_id: int, rolle_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> None: bestehend = await db.get(BenutzerRolleZuordnung, {"benutzer_id": benutzer_id, "rolle_id": rolle_id}) if bestehend is not None: await db.delete(bestehend) await db.flush() @router.get("/benutzer/{benutzer_id}/rollen", response_model=list[int]) async def liste_rollen_zuordnung( benutzer_id: int, db: AsyncSession = Depends(get_db), _=Depends(_admin_only) ) -> list[int]: result = await db.execute( select(BenutzerRolleZuordnung.rolle_id).where(BenutzerRolleZuordnung.benutzer_id == benutzer_id) ) return list(result.scalars().all())