"""The session-refresh job: roles rebuilt for live sessions, disabled, gone and roleless users signed out.""" from __future__ import annotations import uuid from datetime import datetime from typing import Any from zoneinfo import ZoneInfo import pytest from fakedir import FakeDirectory from pshelpers import app_conn, needs_pg from packetstation.auth import directory as userdir from packetstation.auth import sessions from packetstation.feed.config import FeedSettings from packetstation.settings import Settings from packetstation.worker import runs from packetstation.worker.jobs import ad_reconcile, session_refresh from packetstation.worker.scheduler import Scheduler def run(settings: Settings, d: FakeDirectory, name: str = "session-refresh") -> dict[str, Any]: spec = session_refresh.SPEC if name == "session-refresh" else ad_reconcile.SPEC sch = Scheduler(settings, build="pytest", jobs={name: spec}) sch.stack.cache["directory"] = d return runs.run_job(sch, name, "schedule") def session_for(settings: Settings, conn: Any, user_id: uuid.UUID, roles: list[str]) -> uuid.UUID: _, row = sessions.create( conn, settings, settings.session_key(), user_id=user_id, kind="password", roles=roles, groups=[] ) conn.execute("UPDATE sessions SET roles_checked_at = now() - interval '1 hour' WHERE id = %s", (row["id"],)) sid: uuid.UUID = row["id"] return sid def test_cadence_follows_the_last_run(monkeypatch: pytest.MonkeyPatch) -> None: now = datetime(2026, 10, 21, 10, tzinfo=ZoneInfo("America/Denver")) monkeypatch.setattr(session_refresh, "_minutes", 15) assert session_refresh.cadence(FeedSettings(), now) == 900 session_refresh._set_minutes(30) assert session_refresh.cadence(FeedSettings(), now) == 1800 @needs_pg def test_session_refresh(pg_settings: Settings, monkeypatch: pytest.MonkeyPatch) -> None: d = FakeDirectory() monkeypatch.setattr("packetstation.worker.jobs.raise_event", lambda *a, **k: None) monkeypatch.setattr("packetstation.worker.jobs.resolve_event", lambda *a, **k: None) assert run(pg_settings, d, "ad-reconcile")["status"] == "ok" # Nobody signed in: nothing to do row = run(pg_settings, d) assert row["status"] == "ok" and row["counts"]["users"] == 0 g = {s: d.user(s).guid for s in ("clerk.clara", "wh.wade", "qc.quinn", "lead.lou", "sup.wes")} with app_conn(pg_settings) as conn: sid = {s: session_for(pg_settings, conn, uid, ["clerical"]) for s, uid in g.items()} conn.execute("UPDATE sessions SET revoked_at = now() WHERE id = %s", (sid["sup.wes"],)) d.user("lead.lou").groups = ["PS-03-WeldParts-Supervisor"] # promoted d.user("clerk.clara").disabled = True d.users.remove(d.user("wh.wade")) d.user("qc.quinn").groups = [] # AD down: degraded, nothing ended or changed d.down = True row = run(pg_settings, d) assert row["status"] == "degraded" and row["counts"]["ad"] == "unavailable" with app_conn(pg_settings) as conn: assert conn.execute("SELECT count(*) FROM sessions WHERE revoked_at IS NULL").fetchone()[0] == 4 d.down = False row = run(pg_settings, d) assert row["status"] == "ok", row["errors"] c = row["counts"] assert c["users"] == 4 and c["refreshed"] == 1 and c["deactivated"] == 2 and c["roles_lost"] == 1 assert c["sessions_ended"] == 3 and c["roles_changed"] == 2 # Lou promoted, Quinn lost QC with app_conn(pg_settings) as conn: state = dict(conn.execute("SELECT id, revoked_reason FROM sessions").fetchall()) assert state[sid["clerk.clara"]] == "ad_disabled" and state[sid["wh.wade"]] == "ad_gone" assert state[sid["qc.quinn"]] == "roles_lost" and state[sid["lead.lou"]] is None snap, fresh = conn.execute( "SELECT roles_snapshot, roles_checked_at > now() - interval '1 minute' FROM sessions WHERE id = %s", (sid["lead.lou"],), ).fetchone() assert snap["roles"] == ["shop:03:supervisor"] and snap["groups"] == ["PS-03-WeldParts-Supervisor"] and fresh assert userdir.roles_of(conn, g["lead.lou"]) == ["shop:03:supervisor"] active = dict(conn.execute("SELECT id, active FROM users").fetchall()) assert active[g["clerk.clara"]] is False and active[g["wh.wade"]] is False and active[g["qc.quinn"]] is True ends = conn.execute( "SELECT detail->>'reason' FROM audit_events WHERE action = 'auth.session_ended'" " AND detail->>'by' = 'session-refresh' ORDER BY 1" ).fetchall() assert [e[0] for e in ends] == ["ad_disabled", "ad_gone", "roles_lost"]