"""Anmelden und Abmelden.""" from typing import Annotated from fastapi import APIRouter, Form, Request from fastapi.responses import Response from app import web from app.core.errors import Invalid from app.identity import service as identity def safe_next(target: str) -> str: """Nur relative Ziele innerhalb der Anwendung (kein Open Redirect).""" return target if target.startswith("/") and not target.startswith("//") else "/workspaces" def router() -> APIRouter: r = APIRouter() @r.get("/login") def login_page(request: Request, next: str = "/workspaces") -> Response: if web.current(request): return web.redirect(safe_next(next)) return web.render(request, "login.html", {"next_url": safe_next(next), "username": ""}) @r.post("/login") def login(request: Request, username: Annotated[str, Form()], password: Annotated[str, Form()], next: Annotated[str, Form()] = "/workspaces") -> Response: settings = web.settings(request) with web.db(request).session() as session: token = identity.login(session, settings, username, password) if token is None: return web.render(request, "login.html", {"next_url": safe_next(next), "username": username, "error": "Nutzername oder Passwort falsch."}, 401) response = web.redirect(safe_next(next)) response.set_cookie(web.COOKIE, token, httponly=True, samesite="lax", secure=settings.cookie_secure, max_age=settings.session_hours * 3600) return response @r.get("/password") def password_page(request: Request, principal: web.User) -> Response: return web.render(request, "password.html", {}) @r.post("/password") def change_password(request: Request, principal: web.Checked, current: Annotated[str, Form()], new: Annotated[str, Form()], repeat: Annotated[str, Form()]) -> Response: try: with web.db(request).session() as session: identity.change_password(session, principal, current, new, repeat) except Invalid as e: return web.render(request, "password.html", {"error": str(e)}, 400) return web.redirect("/workspaces") @r.post("/logout") def logout(request: Request, principal: web.Checked) -> Response: with web.db(request).session() as session: identity.logout(session, principal) response = web.redirect("/login") response.delete_cookie(web.COOKIE) return response return r