from __future__ import annotations from contextvars import ContextVar, Token from dataclasses import asdict, dataclass @dataclass(frozen=True, slots=True) class IdentityContext: user_id: int username: str fullname: str tenant_uuid: str roles: tuple[str, ...] = () expires_at: int | None = None auth_kind: str = "user" license_type: str | None = None license_activated_at: int | None = None license_expires_at: int | None = None activation_id: str | None = None def to_dict(self) -> dict: data = asdict(self) data["roles"] = list(self.roles) return data _identity: ContextVar[IdentityContext | None] = ContextVar("aps_identity", default=None) def bind_identity(identity: IdentityContext) -> Token: return _identity.set(identity) def reset_identity(token: Token) -> None: _identity.reset(token) def get_identity(*, required: bool = False) -> IdentityContext: identity = _identity.get() if identity is not None: return identity if required: raise RuntimeError("authenticated identity is required") # Direct domain tests and maintenance scripts do not run inside an HTTP request. return IdentityContext( user_id=0, username="system", fullname="System", tenant_uuid="platform", roles=("system",), )