"""Owner session routes — backup-key download and restore-on-new-browser.""" from __future__ import annotations from fastapi import APIRouter, Form, HTTPException, Request from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse import imptune.config as cfg from imptune.db.models import Owner from imptune.services.session import COOKIE_NAME, cookie_kwargs, is_same_origin from imptune.templating import templates router = APIRouter(prefix="/session") def _require_cookie_sessions() -> None: """404 these routes in single-user mode — keys have no meaning without a cookie. Restoring one could not re-point anything, and downloading one would hand out the shared owner's bearer key, which turns into a live credential the moment the deployment is switched back to a cookie-scoped mode. """ if cfg.SINGLE_USER: raise HTTPException(status_code=404, detail="Session keys are disabled in single-user mode") @router.get("/key/download") def download_key(request: Request) -> PlainTextResponse: """Mark the current owner permanent and hand back its key as a backup file.""" _require_cookie_sessions() owner: Owner = request.state.owner if not owner.is_permanent: owner.is_permanent = True owner.save() return PlainTextResponse( content=owner.key, headers={"Content-Disposition": 'attachment; filename="imptune-backup-key.txt"'}, ) @router.get("/restore", response_class=HTMLResponse) def restore_page(request: Request, error: str = "") -> HTMLResponse: _require_cookie_sessions() return templates.TemplateResponse( request=request, name="session_restore.html", context={"error": error}, ) @router.post("/restore") def restore_session(request: Request, key: str = Form(...)): """Re-associate this browser with a previously downloaded backup key.""" _require_cookie_sessions() if not is_same_origin(request): return templates.TemplateResponse( request=request, name="session_restore.html", context={"error": "Request rejected — please submit this form directly from this site."}, status_code=403, ) owner = Owner.get_or_none(Owner.key == key.strip()) if owner is None: return templates.TemplateResponse( request=request, name="session_restore.html", context={"error": "Key not found."}, status_code=404, ) response = RedirectResponse(url="/", status_code=303) response.set_cookie(COOKIE_NAME, owner.key, **cookie_kwargs()) return response