mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-15 18:55:48 -07:00
Assembly Management Rework - Stage 3 Complete
This commit is contained in:
187
Data/Engine/services/auth/dev_mode.py
Normal file
187
Data/Engine/services/auth/dev_mode.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# ======================================================
|
||||
# Data\Engine\services\auth\dev_mode.py
|
||||
# Description: Manages in-memory Dev Mode state for operator sessions with TTL enforcement.
|
||||
#
|
||||
# API Endpoints (if applicable): None
|
||||
# ======================================================
|
||||
|
||||
"""Server-side Dev Mode state tracking for operator sessions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as _dt
|
||||
import logging
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
|
||||
def _utcnow() -> _dt.datetime:
|
||||
return _dt.datetime.utcnow().replace(microsecond=0)
|
||||
|
||||
|
||||
def _normalize_username(value: str) -> str:
|
||||
return (value or "").strip().lower()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DevModeEntry:
|
||||
"""Represents a Dev Mode grant for an operator."""
|
||||
|
||||
username: str
|
||||
role: str
|
||||
session_token: str
|
||||
enabled_at: _dt.datetime
|
||||
expires_at: _dt.datetime
|
||||
|
||||
def is_expired(self, *, now: Optional[_dt.datetime] = None) -> bool:
|
||||
reference = now or _utcnow()
|
||||
return reference >= self.expires_at
|
||||
|
||||
|
||||
class DevModeManager:
|
||||
"""Tracks Dev Mode enablement for operator sessions."""
|
||||
|
||||
def __init__(self, *, logger: Optional[logging.Logger] = None, default_ttl_seconds: int = 900) -> None:
|
||||
ttl = int(default_ttl_seconds or 0)
|
||||
if ttl < 60:
|
||||
ttl = 60
|
||||
self._default_ttl = ttl
|
||||
self._logger = logger or logging.getLogger(__name__)
|
||||
self._entries: Dict[Tuple[str, str], DevModeEntry] = {}
|
||||
self._lock = threading.RLock()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Public API
|
||||
# ------------------------------------------------------------------
|
||||
def enable(
|
||||
self,
|
||||
*,
|
||||
username: str,
|
||||
role: str,
|
||||
session_token: str,
|
||||
ttl_seconds: Optional[int] = None,
|
||||
) -> DevModeEntry:
|
||||
"""Enable Dev Mode for the specified operator session."""
|
||||
|
||||
normalized = _normalize_username(username)
|
||||
if not normalized:
|
||||
raise ValueError("username required for Dev Mode enablement")
|
||||
token = (session_token or "").strip()
|
||||
if not token:
|
||||
raise ValueError("session token required for Dev Mode enablement")
|
||||
|
||||
ttl = ttl_seconds if ttl_seconds and ttl_seconds > 0 else self._default_ttl
|
||||
if ttl < 60:
|
||||
ttl = 60
|
||||
now = _utcnow()
|
||||
entry = DevModeEntry(
|
||||
username=normalized,
|
||||
role=(role or "User").strip() or "User",
|
||||
session_token=token,
|
||||
enabled_at=now,
|
||||
expires_at=now + _dt.timedelta(seconds=ttl),
|
||||
)
|
||||
|
||||
with self._lock:
|
||||
self._prune_locked(now=now)
|
||||
self._entries[(normalized, token)] = entry
|
||||
|
||||
self._logger.info(
|
||||
"Dev Mode enabled for user='%s' role='%s' session_suffix='%s' ttl_seconds=%s",
|
||||
normalized,
|
||||
entry.role,
|
||||
token[-6:],
|
||||
ttl,
|
||||
)
|
||||
return entry
|
||||
|
||||
def disable(self, *, username: str, session_token: str) -> bool:
|
||||
"""Disable Dev Mode for the specified operator session."""
|
||||
|
||||
normalized = _normalize_username(username)
|
||||
token = (session_token or "").strip()
|
||||
if not normalized or not token:
|
||||
return False
|
||||
|
||||
removed = False
|
||||
with self._lock:
|
||||
removed = self._entries.pop((normalized, token), None) is not None
|
||||
|
||||
if removed:
|
||||
self._logger.info(
|
||||
"Dev Mode disabled for user='%s' session_suffix='%s'",
|
||||
normalized,
|
||||
token[-6:],
|
||||
)
|
||||
return removed
|
||||
|
||||
def is_enabled(self, *, username: str, session_token: str) -> bool:
|
||||
"""Return whether Dev Mode is currently enabled for the operator session."""
|
||||
|
||||
normalized = _normalize_username(username)
|
||||
token = (session_token or "").strip()
|
||||
if not normalized or not token:
|
||||
return False
|
||||
|
||||
now = _utcnow()
|
||||
with self._lock:
|
||||
entry = self._entries.get((normalized, token))
|
||||
if not entry:
|
||||
self._prune_locked(now=now)
|
||||
return False
|
||||
|
||||
if entry.is_expired(now=now):
|
||||
self._entries.pop((normalized, token), None)
|
||||
self._logger.info(
|
||||
"Dev Mode expired for user='%s' session_suffix='%s'",
|
||||
normalized,
|
||||
token[-6:],
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def describe(self) -> Dict[str, Dict[str, str]]:
|
||||
"""Return a serialisable snapshot of active Dev Mode entries."""
|
||||
|
||||
snapshot: Dict[str, Dict[str, str]] = {}
|
||||
now = _utcnow()
|
||||
with self._lock:
|
||||
self._prune_locked(now=now)
|
||||
for entry in self._entries.values():
|
||||
key = f"{entry.username}:{entry.session_token[-6:]}"
|
||||
snapshot[key] = {
|
||||
"username": entry.username,
|
||||
"role": entry.role,
|
||||
"enabled_at": entry.enabled_at.isoformat(),
|
||||
"expires_at": entry.expires_at.isoformat(),
|
||||
}
|
||||
return snapshot
|
||||
|
||||
def clean_expired(self) -> None:
|
||||
"""Remove expired Dev Mode entries."""
|
||||
|
||||
with self._lock:
|
||||
self._prune_locked(now=_utcnow())
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal helpers
|
||||
# ------------------------------------------------------------------
|
||||
def _prune_locked(self, *, now: Optional[_dt.datetime] = None) -> None:
|
||||
reference = now or _utcnow()
|
||||
expired_keys = [
|
||||
key for key, entry in self._entries.items() if entry.is_expired(now=reference)
|
||||
]
|
||||
for key in expired_keys:
|
||||
entry = self._entries.pop(key, None)
|
||||
if entry:
|
||||
self._logger.info(
|
||||
"Dev Mode expired for user='%s' session_suffix='%s' (prune)",
|
||||
entry.username,
|
||||
entry.session_token[-6:],
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["DevModeManager", "DevModeEntry"]
|
||||
|
||||
Reference in New Issue
Block a user