kundenplattform/auth.py
Thomas Stallinger ed38f103f2 feat: modulare Struktur für Admin-Bereich (Schritt 1)
app.py auf APIRouter-Module umgestellt (db.py, templating.py,
anode_client.py, auth.py, routers/geraete.py + vier Admin-Router als
Platzhalter). benutzer.ist_admin (Migration 0002) + require_admin-
Dependency (403 ohne Admin-Flag). Admin-Nav-Link im Header nur für
Admin-Konten sichtbar. create_user.py um --admin-Flag erweitert.

Die vier Admin-Bereiche (Technik/Datenbank, Geräte, Flows, Kunden) sind
als Platzhalterseiten unter /admin/... verdrahtet und bekommen in den
nächsten Schritten echte Funktionalität.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:51:46 +02:00

74 lines
2.1 KiB
Python

import bcrypt
from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import RedirectResponse
from db import get_database_connection
from templating import templates
router = APIRouter()
def get_current_user(request: Request) -> dict:
user = request.session.get("user")
if user is None:
raise HTTPException(status_code=303, headers={"Location": "/login"})
return user
def require_admin(user: dict = Depends(get_current_user)) -> dict:
if not user.get("ist_admin"):
raise HTTPException(status_code=403, detail="Kein Admin-Zugriff.")
return user
@router.get("/login")
def login_form(request: Request):
if request.session.get("user") is not None:
return RedirectResponse("/geraete", status_code=303)
return templates.TemplateResponse(request, "login.html", {"error": None})
@router.post("/login")
def login_submit(request: Request, email: str = Form(...), password: str = Form(...)):
with get_database_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, organization_id, password_hash, ist_admin FROM benutzer WHERE email = %s",
(email,),
)
row = cur.fetchone()
if row is None:
return templates.TemplateResponse(
request, "login.html", {"error": "E-Mail oder Passwort ist falsch."}, status_code=401
)
# psycopg liefert TEXT-Spalten hier als bytes zurück, nicht als str.
stored_hash = row[2] if isinstance(row[2], bytes) else row[2].encode("utf-8")
if not bcrypt.checkpw(password.encode("utf-8"), stored_hash):
return templates.TemplateResponse(
request, "login.html", {"error": "E-Mail oder Passwort ist falsch."}, status_code=401
)
request.session["user"] = {
"id": str(row[0]),
"organization_id": str(row[1]),
"email": email,
"ist_admin": row[3],
}
return RedirectResponse("/geraete", status_code=303)
@router.get("/logout")
def logout(request: Request):
request.session.clear()
return RedirectResponse("/login", status_code=303)