52 lines
1.3 KiB
Python
52 lines
1.3 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["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",),
|
||
|
|
)
|