diff --git a/app/api/admin.py b/app/api/admin.py index c501b25..fb640bf 100644 --- a/app/api/admin.py +++ b/app/api/admin.py @@ -66,34 +66,50 @@ def _current_user_or_none(request: Request): return authenticate(request.headers, client_host, token) -@router.get("/admin/login") -async def admin_app_login(request: Request): - """Admin-Selbstanmeldung in die App — Ersatz fuer einen verlorenen Zugangslink. - - Liegt hinter Authelia (nginx: Location /api/admin/ bzw. /admin-login mit - auth_request + error_page-Redirect). Erkennt den SSO-Admin via Remote-User, - mintet ein frisches Capability-Token, setzt es als va_token-Cookie und leitet - in die App (/). Damit meldet sich der Admin allein mit seinem Authelia-Passwort - an, ohne persoenlichen Link. - - Jeder Aufruf rotiert das Token (alte Links DIESES Admin-Nutzers werden - ungueltig) — fuer eine Recovery-Funktion korrekt und ein Sicherheitsplus: - jede SSO-Anmeldung erzeugt eine frische Capability. - """ - user = _current_user_or_none(request) - if not is_admin_user(user): - raise HTTPException(status_code=403, detail="Admin privileges required") +def _issue_login_cookie(request: Request, user, action: str): + """Mintet ein frisches Capability-Token fuer `user`, setzt es als va_token-Cookie + und leitet in die App (/). Gemeinsame Basis fuer /api/login und /api/admin/login. + Jeder Aufruf rotiert das Token (ein Token pro Nutzer).""" result = get_store().reset_token(user.id) if result is None: - raise HTTPException(status_code=404, detail="Admin-Nutzer nicht gefunden.") + raise HTTPException(status_code=404, detail="Nutzer nicht gefunden.") _, raw_token = result - log_admin_action(request, "admin_app_login", user_id=user.id) + log_admin_action(request, action, user_id=user.id) resp = RedirectResponse("/", status_code=303) resp.set_cookie(CAPABILITY_COOKIE, raw_token, httponly=True, secure=True, samesite="lax", max_age=31536000) return resp +@router.get("/login") +async def app_login(request: Request): + """Anmeldung in die App mit Benutzername/Passwort (1FA) statt persoenlichem Link. + + Liegt hinter Authelia (nginx: Location /login, one_factor). Erkennt den per + Remote-User weitergereichten Authelia-Nutzer (Admin ODER normaler Nutzer), + mintet das va_token-Cookie und leitet in die App. Senioren nutzen weiter ihren + Link; dieser Weg ist der Passwort-Einstieg fuer Betreuer/Admins. + """ + user = _current_user_or_none(request) + if user is None: + raise HTTPException(status_code=403, detail="Authentication required") + return _issue_login_cookie(request, user, "app_login") + + +@router.get("/admin/login") +async def admin_app_login(request: Request): + """Admin-Selbstanmeldung in die App — Ersatz fuer einen verlorenen Zugangslink. + + Wie /api/login, aber zusaetzlich auf Admins beschraenkt (ADMIN_USERS). Liegt + hinter Authelia (nginx: Location /admin-login). Mintet das va_token-Cookie und + leitet in die App (/). + """ + user = _current_user_or_none(request) + if not is_admin_user(user): + raise HTTPException(status_code=403, detail="Admin privileges required") + return _issue_login_cookie(request, user, "admin_app_login") + + @router.post("/admin/users", response_model=UserCreated, dependencies=[Depends(require_admin)]) async def create_user(payload: UserCreate): """Legt einen Nutzer an und gibt das Bearer-Token EINMALIG zurueck.""" diff --git a/app/main.py b/app/main.py index 914c9f2..656a04e 100644 --- a/app/main.py +++ b/app/main.py @@ -75,11 +75,14 @@ _NO_ACCESS_PAGE = """
+p{color:#475569;line-height:1.6;margin:.5rem 0}.emoji{font-size:2.5rem} +.btn{display:inline-block;margin-top:1.25rem;background:#2563eb;color:#fff;text-decoration:none; +font-weight:600;font-size:1.05rem;padding:.8rem 1.4rem;border-radius:.75rem}Bitte öffne die App über deinen persönlichen Zugangslink.
-Wenn du keinen Link hast, wende dich an deine Betreuungsperson.
+Senioren: bitte die App über den persönlichen Zugangslink öffnen.
+Betreuer und Admins können sich mit Benutzername und Passwort anmelden:
+Mit Benutzername & Passwort anmelden