import pytest from tests.conftest import auth_header, login async def _benutzer_ohne_feste_rolle(db_session, login_name: str): from app.core.security import hash_password from app.models.auth import Benutzer benutzer = Benutzer( name="Helfer Ohne Feste Rolle", login=login_name, passwort_hash=hash_password("test-passwort-123"), aktiv=True, ) db_session.add(benutzer) await db_session.flush() return benutzer @pytest.mark.asyncio async def test_custom_rolle_gewaehrt_zugriff_ohne_feste_rolle(client, db_session, admin_user): """Kern des granularen Rechte-Systems: ein Benutzer OHNE jede feste RolleTyp-Zuordnung darf trotzdem etwas tun, wenn ihm eine custom Rolle mit passender Berechtigung zugewiesen wurde (Nutzer-Beispiel: "Materialwart" darf Material erstellen).""" helfer = await _benutzer_ohne_feste_rolle(db_session, "materialwart1") admin_token = await login(client, "admin1") ohne_rolle = await client.post( "/api/v1/materialien", json={"name": "Verband", "einheit": "Stück", "materialtyp": "standard"}, headers=auth_header(await login(client, "materialwart1")), ) assert ohne_rolle.status_code == 403 rolle = await client.post( "/api/v1/rollen", json={"name": "Materialwart"}, headers=auth_header(admin_token) ) rolle_id = rolle.json()["id"] berechtigungen = await client.get("/api/v1/berechtigungen", headers=auth_header(admin_token)) material_erstellen = next(b for b in berechtigungen.json() if b["schluessel"] == "material.erstellen") await client.put( f"/api/v1/rollen/{rolle_id}/berechtigungen/{material_erstellen['id']}", headers=auth_header(admin_token) ) await client.put(f"/api/v1/benutzer/{helfer.id}/rollen/{rolle_id}", headers=auth_header(admin_token)) helfer_token = await login(client, "materialwart1") mit_rolle = await client.post( "/api/v1/materialien", json={"name": "Verband", "einheit": "Stück", "materialtyp": "standard"}, headers=auth_header(helfer_token), ) assert mit_rolle.status_code == 201 @pytest.mark.asyncio async def test_legacy_admin_funktioniert_weiterhin_ohne_custom_rolle(client, admin_user): """Regressionstest: bestehende feste Rollen funktionieren unverändert, das granulare System ist rein additiv.""" token = await login(client, "admin1") response = await client.post( "/api/v1/materialien", json={"name": "Schere", "einheit": "Stück", "materialtyp": "standard"}, headers=auth_header(token), ) assert response.status_code == 201 @pytest.mark.asyncio async def test_nur_admin_darf_rollen_verwalten(client, mitarbeiter_user): token = await login(client, "mitarbeiter1") response = await client.post("/api/v1/rollen", json={"name": "X"}, headers=auth_header(token)) assert response.status_code == 403 @pytest.mark.asyncio async def test_rolle_ohne_berechtigung_bleibt_wirkungslos(client, db_session, admin_user): helfer = await _benutzer_ohne_feste_rolle(db_session, "leerrolle1") admin_token = await login(client, "admin1") rolle = await client.post("/api/v1/rollen", json={"name": "Leer"}, headers=auth_header(admin_token)) rolle_id = rolle.json()["id"] await client.put(f"/api/v1/benutzer/{helfer.id}/rollen/{rolle_id}", headers=auth_header(admin_token)) helfer_token = await login(client, "leerrolle1") response = await client.post( "/api/v1/materialien", json={"name": "X", "einheit": "Stück", "materialtyp": "standard"}, headers=auth_header(helfer_token), ) assert response.status_code == 403 @pytest.mark.asyncio async def test_berechtigung_entfernen_wirkt_sofort(client, db_session, admin_user): helfer = await _benutzer_ohne_feste_rolle(db_session, "materialwart2") admin_token = await login(client, "admin1") rolle = await client.post("/api/v1/rollen", json={"name": "Materialwart2"}, headers=auth_header(admin_token)) rolle_id = rolle.json()["id"] berechtigungen = await client.get("/api/v1/berechtigungen", headers=auth_header(admin_token)) material_erstellen = next(b for b in berechtigungen.json() if b["schluessel"] == "material.erstellen") await client.put( f"/api/v1/rollen/{rolle_id}/berechtigungen/{material_erstellen['id']}", headers=auth_header(admin_token) ) await client.put(f"/api/v1/benutzer/{helfer.id}/rollen/{rolle_id}", headers=auth_header(admin_token)) await client.delete( f"/api/v1/rollen/{rolle_id}/berechtigungen/{material_erstellen['id']}", headers=auth_header(admin_token) ) helfer_token = await login(client, "materialwart2") response = await client.post( "/api/v1/materialien", json={"name": "X", "einheit": "Stück", "materialtyp": "standard"}, headers=auth_header(helfer_token), ) assert response.status_code == 403