53 lines
1.4 KiB
Python
53 lines
1.4 KiB
Python
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["user_id"] = str(self.user_id)
|
|
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",),
|
|
)
|