From 220c5db5b9ff558c6b9eba006ded8b6c816724e5 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 15 May 2026 07:33:07 -0400 Subject: [PATCH] webapp: LAPS clear actually removes the password from session JSON Previous /imaging//laps clear path used update_session() to re-feed state minus laps_password. But update_session MERGES payload into existing state - it cannot delete a key the existing state already has. The laps_password persisted on disk across the "clear" POST, then came back into the page on next reload. Fix: bypass update_session for the clear case. Read the session JSON directly, pop laps_password, write via atomic tempfile-rename. Same write pattern update_session uses for consistency. Co-Authored-By: Claude Opus 4.7 (1M context) --- webapp/app.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/webapp/app.py b/webapp/app.py index 0bbf747..c17ab03 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -15,6 +15,7 @@ This file is the route surface; most logic lives in ``services/``: import json import os import shutil +import tempfile from datetime import datetime from pathlib import Path @@ -506,13 +507,29 @@ def imaging_set_laps(serial): if not isinstance(pw, str): return {"ok": False, "error": "password must be string"}, 400 if pw == "": - # Clear by direct read-modify-write since update_session skips empty values. - state = imaging_status.get_session(serial) or {} - if "laps_password" in state: - state.pop("laps_password", None) - # Re-feed everything (minus laps_password) through update_session. - state["serial"] = serial - imaging_status.update_session(state) + # Clear by direct file write. update_session() merges payload INTO + # existing state and skips empty values, so it cannot remove a key. + # Pop the laps_password key directly from the session JSON and + # write the result atomically. + path = imaging_status._path_for(serial) + if os.path.isfile(path): + try: + with open(path, "r") as f: + state = json.load(f) + except (json.JSONDecodeError, OSError): + state = {} + if "laps_password" in state: + state.pop("laps_password", None) + state["last_updated"] = imaging_status._now_iso() + fd, tmp = tempfile.mkstemp(dir=config.IMAGING_DIR, prefix=".tmp-", suffix=".json") + try: + with os.fdopen(fd, "w") as f: + json.dump(state, f, indent=2) + os.replace(tmp, path) + except Exception: + try: os.unlink(tmp) + except OSError: pass + raise return {"ok": True, "cleared": True} imaging_status.update_session({"serial": serial, "laps_password": pw}) return {"ok": True}