feat: enforce confirmed offline idle deadlines (Closes #523)
This commit is contained in:
1 parent
100d38cc5c
commit
b1890a29ff
8 files changed
+255
-34
No files matched your search
@@ -30,6 +30,7 @@ class Session:
|
||||
session_id: str
|
||||
csrf: str
|
||||
expires_at: int
|
||||
idle_expires_at: int | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -174,7 +175,14 @@ def verify_session_with_reason(
|
||||
return SessionVerification(None, "session_idle")
|
||||
if status != "active":
|
||||
return SessionVerification(None, "session_revoked")
|
||||
return SessionVerification(session)
|
||||
return SessionVerification(
|
||||
Session(
|
||||
session_id=session.session_id,
|
||||
csrf=session.csrf,
|
||||
expires_at=session.expires_at,
|
||||
idle_expires_at=getattr(status, "idle_expires_at", None),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def verify_session(value: str | None, now: int | None = None) -> Session | None:
|
||||
|
||||
+5
-1
@@ -1512,6 +1512,7 @@ async def session_status(request: Request):
|
||||
}
|
||||
if session is not None:
|
||||
payload["expires_at"] = session.expires_at
|
||||
payload["idle_expires_at"] = session.idle_expires_at
|
||||
return payload
|
||||
|
||||
|
||||
@@ -1534,7 +1535,10 @@ async def record_session_activity(request: Request):
|
||||
)
|
||||
return {
|
||||
"active": True,
|
||||
"idle_expires_at": int(time.time()) + dashboard_auth.idle_timeout_seconds(),
|
||||
"idle_expires_at": min(
|
||||
session.expires_at,
|
||||
int(time.time()) + dashboard_auth.idle_timeout_seconds(),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
+16
-4
@@ -12,6 +12,17 @@ class SessionStoreError(RuntimeError):
|
||||
"""Raised when session state cannot be read or changed safely."""
|
||||
|
||||
|
||||
class SessionStatus(str):
|
||||
"""String-compatible status carrying the server-confirmed idle deadline."""
|
||||
|
||||
idle_expires_at: int | None
|
||||
|
||||
def __new__(cls, value: str, idle_expires_at: int | None = None):
|
||||
instance = super().__new__(cls, value)
|
||||
instance.idle_expires_at = idle_expires_at
|
||||
return instance
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ActiveDevice:
|
||||
management_id: str
|
||||
@@ -180,10 +191,11 @@ class SessionStore:
|
||||
except (OSError, sqlite3.Error) as exc:
|
||||
raise SessionStoreError("Session registry is temporarily unavailable") from exc
|
||||
if row is None or row[0] != expires_at or expires_at <= now:
|
||||
return "revoked"
|
||||
if row[1] + max(1, idle_timeout_seconds) <= now:
|
||||
return "idle"
|
||||
return "active"
|
||||
return SessionStatus("revoked")
|
||||
idle_expires_at = min(row[0], row[1] + max(1, idle_timeout_seconds))
|
||||
if idle_expires_at <= now:
|
||||
return SessionStatus("idle", idle_expires_at)
|
||||
return SessionStatus("active", idle_expires_at)
|
||||
|
||||
def touch(
|
||||
self, session_id: str, expires_at: int, *, idle_timeout_seconds: int
|
||||
|
||||
Reference in new issue
Block a user