* fix: před stahováním ověřit přihlášení přes islogged Server při vypršelé session nevrací chybu, ale tiše odpoví jako anonymnímu uživateli (jen přístupnost A). Plugin proto před stahováním volá /api/user/islogged; při 'nologged' se jednou znovu přihlásí z uložených údajů, a když to nejde, varuje v liště, že stahuje anonymně. - amcr_tools: _check_islogged, _ensure_logged_in, volání v load_amcr_data - smoke test: 7 offline scénářů stavu přihlášení - README; changelog v2.2.0 v metadata.txt - openspec/changes/fix-session-expiry-islogged: návrh, spec, design, úkoly Closes #72. Připraveno s pomocí AI (Claude), ručně zkontrolováno. * feat: odebrání přihlašovacích údajů uživatele i odhlásí Dosud zůstala přihlášená session v paměti až do restartu QGIS, takže se po „Odebrat uložené přihlašovací údaje“ dál stahovalo jako přihlášený. Nově se session odhlásí na serveru (GET /api/user/logout) a zahodí; když server neodpoví, zahodí se aspoň lokálně. - amcr_tools: logout_from_api; amcr_dialog: volání v _forget_credentials - smoke test: odhlášení, chyba sítě, bez session - README, changelog v2.2.0, OpenSpec (požadavek + úkoly 2b; 3.3 ověřeno) Připraveno s pomocí AI (Claude), ručně zkontrolováno. * chore: archivovat OpenSpec změnu fix-session-expiry-islogged Všechny úkoly hotové (2b.2 – odhlášení – ověřeno ručně v QGIS), archivováno s --skip-specs (stupeň change-tracked, bez openspec/specs/). Připraveno s pomocí AI (Claude), ručně zkontrolováno.
4.0 KiB
Design
Context
- The session lives only in memory (
amcr_tools.AMCR_SESSION, arequests.Sessionwith theJSESSIONIDcookie)._get_session()logs in from stored credentials only when no session object exists, so after QGIS start the first download always logs in; an expired session object is reused forever. - All data requests of a download go through
_api_get_json()(main query pages and PIAN batches)._is_auth_error()there reacts to HTTP 401 or error text – neither occurs on expiry (see proposal.md – Why). - Server behaviour (verified 2026-10-02, live API):
GET /api/user/islogged→{"remaining": <s>}when logged in,{"error": "nologged"}otherwise, both HTTP 200. It does not extend the session; anysearch/querydoes. - Codelists (
amcr_codelists.py) use plainrequests.getwithout the session – unaffected by login state.
Goals / Non-Goals
Goals:
- One check at the start of
load_amcr_data, before the first data request. - Reuse existing login code (
login_to_api,LoginDialog.get_credentials). - Testable offline: the check takes its HTTP behaviour from the session object so the smoke test can inject a fake.
Non-Goals:
- Checking before every page / PIAN batch (a download takes seconds to minutes and every data request renews the sliding timeout).
- Refactoring session handling into a class.
Decisions
-
New helper
_ensure_logged_in() -> strinamcr_tools.py, returning one of"anonymous"(no session, no credentials – nothing to check),"logged_in","relogged","fallback"(expected login, ended anonymous),"unknown"(check failed, proceeding). Flow: get session via_get_session()(logs in if needed); if none and no credentials →anonymous; if none but credentials → login failed →fallback; otherwise callislogged;remaining→logged_in;nologged→ drop session, re-login once, verify again →reloggedorfallback; exception / non-JSON →unknown. Alternative: re-login unconditionally before each download – simpler, but one POST with the password per download and no way to distinguish a real failure; rejected. Alternative: compareremainingwith a local timestamp of last request – fragile (server timeout may change); rejected. -
Caller decides UI.
load_amcr_datapushes the message bar warning onfallback; the helper only logs (keeps it free ofifacefor the test). -
Interpretation of the response: logged in iff the body is a dict with key
remaining. Anything else with anerrorkey → not logged in. Unknown shape →unknown(do not trigger a re-login loop on a format change). -
Keep
_is_auth_erroras a fallback, with a comment that the current server never triggers it; removing it brings no benefit and it still covers a possible future 401. -
Never log the response of
islogged?wantsUser=true– we do not use that parameter at all; onlyremaining(number) is logged. -
Logout on credential removal – new
logout_from_api()inamcr_tools.pycalled fromLoginDialog._forget_credentials. The local session is dropped first and unconditionally; the server call is best effort (a failure is logged and reported in the dialog text). Without it, the in-memory session would keep downloading logged-in data until QGIS restarts even though the user believes he is "forgotten".
Risks / Trade-offs
- [Extra request per download] → only for logged-in / credential users; cost ~100 ms.
- [Session expires during a very long download] → practically impossible: each page request renews the 1 h sliding timeout.
- [Re-login prompts for the QGIS master password] →
get_credentials()is already called on first download after start; behaviour unchanged. - [
isloggedendpoint changes shape] →unknown, logged warning, download proceeds as today (no regression).
Migration Plan
Plain plugin update; no settings or data migration. Rollback = previous release.