from typing import Optional, Literal, Self from datetime import datetime, UTC from pydantic import BaseModel, HttpUrl, Field from enum import Enum from simple_openid_connect.data import IdToken class HttpProblemType(Enum): """ Statically known HTTP problem types using the [type URI scheme](https://datatracker.ietf.org/doc/rfc4151/) """ UNAUTHORIZED = "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" FORBIDDEN_TO_OPERATE = "type:noc@hamburg.ccc.de,2026,FORBIDDEN_TO_OPERATE" LOCK_NOT_FOUND = "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND" class HttpProblemDetail(BaseModel): """ API Error modeled after [RFC9475](https://www.rfc-editor.org/rfc/rfc9457.html). """ status: int type: HttpProblemType title: str detail: str instance: Optional[HttpUrl] class ApiUser(BaseModel): is_anonymous: bool is_ccchh_user: bool is_token_user: bool may_operate_locks: bool username: str guaranteed_session_until: Optional[datetime] raw_id_token: Optional[str] = Field(exclude=True) @classmethod def from_id_token(cls, id_token: IdToken, raw_id_token: str) -> Self: return cls( is_anonymous=False, is_ccchh_user=True, is_token_user=False, may_operate_locks="intern@" in getattr(id_token, "ccchh-roles", []), username=id_token.preferred_username, guaranteed_session_until=datetime.fromtimestamp(id_token.exp, UTC), raw_id_token=raw_id_token, ) class LockStatus(BaseModel): is_unreachable: bool is_low_battery: bool is_error_jammed: bool lock_target_level: Literal["locked", "unlocked", "open"] lock_state: Literal["unknown", "locked", "unlocked"] activity_state: Literal["unknown", "locking", "unlocking", "stable"] class Lock(BaseModel): name: str id: str status: LockStatus class LockOperation(BaseModel): desired_state: Literal["open", "closed"]