Source code for ace.core.jwt_user

from __future__ import annotations
from typing import Any, List, Optional


[docs] class JwtUser: """ Represents the authenticated user extracted from a decoded JWT Bearer token. All standard claims from the Equisoft/Centralize token are exposed as typed properties. Any extra or custom claim is also accessible via ``get()`` or the subscript operator, so the class stays useful even when the token schema evolves. Typical usage inside a script (``self`` is any ``BaseRule`` subclass):: user = self.jwt_user if user is None: # Request was made without a Bearer token ... # Typed shorthand properties print(user.application_user_id) # int – applicationUserId claim print(user.user_id) # int – userId claim print(user.email) # str – email claim print(user.name) # str – name claim print(user.org) # str – org claim print(user.roles) # list – roles claim print(user.divisions) # list – divisions claim print(user.language_id) # int – languageId claim print(user.application_uuid) # str – applicationUUID claim print(user.subject) # str – sub claim print(user.is_carbon) # bool – isCarbon claim # Access any claim by its original JWT key name value = user.get("someCustomClaim", default="fallback") # Dict-style access value = user["someCustomClaim"] # Membership test if "someCustomClaim" in user: ... # Full payload as a plain dict payload = user.as_dict() """
[docs] def __init__(self, payload: dict): """ :param payload: The decoded JWT payload dict (as returned by ``jwt.decode()``). """ self._payload: dict = payload if isinstance(payload, dict) else {}
# ------------------------------------------------------------------ # Equisoft / Centralize application claims # ------------------------------------------------------------------ @property def application_user_id(self) -> Optional[int]: """``applicationUserId`` claim – primary user identifier used by ACE scripts.""" return self._payload.get("applicationUserId") @property def user_id(self) -> Optional[int]: """``userId`` claim.""" return self._payload.get("userId") @property def name(self) -> Optional[str]: """``name`` claim – display name of the authenticated user.""" return self._payload.get("name") @property def email(self) -> Optional[str]: """``email`` claim.""" return self._payload.get("email") @property def org(self) -> Optional[str]: """``org`` claim – organisation name.""" return self._payload.get("org") @property def roles(self) -> List[Any]: """``roles`` claim – list of role identifiers assigned to the user.""" return self._payload.get("roles", []) @property def divisions(self) -> List[str]: """``divisions`` claim – list of division names the user belongs to.""" return self._payload.get("divisions", []) @property def language_id(self) -> Optional[int]: """``languageId`` claim.""" return self._payload.get("languageId") @property def application_uuid(self) -> Optional[str]: """``applicationUUID`` claim.""" return self._payload.get("applicationUUID") @property def is_carbon(self) -> bool: """``isCarbon`` claim.""" return bool(self._payload.get("isCarbon", False)) @property def is_commission(self) -> bool: """``isCommission`` claim.""" return bool(self._payload.get("isCommission", False)) @property def audience(self) -> list: """``aud`` claim – list of intended audiences for this token.""" aud = self._payload.get("aud", []) return aud if isinstance(aud, list) else [aud] # ------------------------------------------------------------------ # Standard JWT registered claims (RFC 7519) # ------------------------------------------------------------------ @property def subject(self) -> Optional[str]: """``sub`` claim – UUID that uniquely identifies the user in the IdP.""" return self._payload.get("sub") @property def issuer(self) -> Optional[str]: """``iss`` claim – token issuer URL.""" return self._payload.get("iss") @property def issued_at(self) -> Optional[int]: """``iat`` claim – Unix timestamp when the token was issued.""" return self._payload.get("iat") @property def expires_at(self) -> Optional[int]: """``exp`` claim – Unix timestamp when the token expires.""" return self._payload.get("exp") @property def not_before(self) -> Optional[int]: """``nbf`` claim – Unix timestamp before which the token is not valid.""" return self._payload.get("nbf") @property def jwt_id(self) -> Optional[str]: """``jti`` claim – unique identifier for this specific token.""" return self._payload.get("jti") # ------------------------------------------------------------------ # Generic / escape-hatch accessors # ------------------------------------------------------------------
[docs] def get(self, claim: str, default: Any = None) -> Any: """ Return the value of *any* claim by its original JWT key name. :param claim: The raw claim key as it appears in the JWT payload (e.g. ``"applicationUserId"``, ``"someCustomClaim"``). :param default: Value to return when the claim is absent. """ return self._payload.get(claim, default)
[docs] def __getitem__(self, claim: str) -> Any: """Allow dict-style access: ``user["applicationUserId"]``.""" return self._payload[claim]
[docs] def __contains__(self, claim: str) -> bool: """Support ``"claim" in user`` membership checks.""" return claim in self._payload
[docs] def as_dict(self) -> dict: """Return a copy of the full decoded JWT payload.""" return dict(self._payload)
def __repr__(self) -> str: return ( f"JwtUser(name={self.name!r}, email={self.email!r}, " f"application_user_id={self.application_user_id!r}, org={self.org!r})" ) # ------------------------------------------------------------------ # Factory helpers # ------------------------------------------------------------------
[docs] @classmethod def from_token(cls, token: str) -> Optional["JwtUser"]: """ Decode a raw JWT string (without signature verification) and return a ``JwtUser`` instance, or ``None`` if decoding fails. :param token: The raw Bearer token string (without the ``Bearer `` prefix). """ try: import jwt # PyJWT payload = jwt.decode( token, options={ "verify_signature": False, "verify_aud": False, "verify_exp": False, "verify_nbf": False, }, ) return cls(payload) except Exception: return None
[docs] @classmethod def from_payload(cls, payload: dict) -> "JwtUser": """Build a ``JwtUser`` directly from an already-decoded payload dict.""" return cls(payload)