30 lines
1.3 KiB
Python
30 lines
1.3 KiB
Python
"""Account activity and the shared transaction lock for channel ownership."""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
from datetime import datetime, timedelta
|
|
|
|
from sqlalchemy import func, select, text, update
|
|
|
|
from .models import HockeyWebSession, MatchParticipant
|
|
|
|
ACCOUNT_IDLE_SECONDS = 2 * 60 * 60
|
|
|
|
|
|
def lock_account(session, user_id: str) -> None:
|
|
if session.get_bind().dialect.name == "postgresql":
|
|
key = int.from_bytes(hashlib.sha256(("hockey-channel:" + user_id).encode()).digest()[:8], "big", signed=True)
|
|
session.execute(text("SELECT pg_advisory_xact_lock(:key)"), {"key": key})
|
|
else:
|
|
session.execute(update(MatchParticipant).where(MatchParticipant.user_id == user_id)
|
|
.values(last_seen_at=MatchParticipant.last_seen_at))
|
|
|
|
|
|
def account_is_idle(session, user_id: str, now: datetime) -> bool:
|
|
# All browser logins of this account count, including the last action before
|
|
# logout. Polling, OperatorSession timestamps and Agent traffic never count.
|
|
latest = session.scalar(select(func.max(HockeyWebSession.last_activity_at)).where(
|
|
HockeyWebSession.wfl_user_id == user_id))
|
|
# Header-based development/test auth has no persisted browser logins.
|
|
return latest is not None and latest <= now - timedelta(seconds=ACCOUNT_IDLE_SECONDS)
|