import enum from typing import Optional, Union, Dict, Any, List from pydantic import BaseModel from Controllers.Redis.database import RedisActions class UserType(enum.Enum): employee = 1 occupant = 2 class Credentials(BaseModel): person_id: int person_name: str class ApplicationToken(BaseModel): # Application Token Object -> is the main object for the user user_type: int = UserType.occupant.value credential_token: str = "" user_uu_id: str user_id: int person_id: int person_uu_id: str request: Optional[dict] = None # Request Info of Client expires_at: Optional[float] = None # Expiry timestamp class OccupantToken(BaseModel): # Selection of the occupant type for a build part is made by the user living_space_id: int # Internal use living_space_uu_id: str # Outer use occupant_type_id: int occupant_type_uu_id: str occupant_type: str build_id: int build_uuid: str build_part_id: int build_part_uuid: str responsible_company_id: Optional[int] = None responsible_company_uuid: Optional[str] = None responsible_employee_id: Optional[int] = None responsible_employee_uuid: Optional[str] = None # ID list of reachable event codes as "endpoint_code": ["UUID", "UUID"] reachable_event_codes: Optional[dict[str, str]] = None # ID list of reachable applications as "page_url": ["UUID", "UUID"] reachable_app_codes: Optional[dict[str, str]] = None class CompanyToken(BaseModel): # Selection of the company for an employee is made by the user company_id: int company_uu_id: str department_id: int # ID list of departments department_uu_id: str # ID list of departments duty_id: int duty_uu_id: str staff_id: int staff_uu_id: str employee_id: int employee_uu_id: str bulk_duties_id: int # ID list of reachable event codes as "endpoint_code": ["UUID", "UUID"] reachable_event_codes: Optional[dict[str, str]] = None # ID list of reachable applications as "page_url": ["UUID", "UUID"] reachable_app_codes: Optional[dict[str, str]] = None class OccupantTokenObject(ApplicationToken): # Occupant Token Object -> Requires selection of the occupant type for a specific build part available_occupants: dict = None selected_occupant: Optional[OccupantToken] = None # Selected Occupant Type @property def is_employee(self) -> bool: return False @property def is_occupant(self) -> bool: return True class EmployeeTokenObject(ApplicationToken): # Full hierarchy Employee[staff_id] -> Staff -> Duty -> Department -> Company companies_id_list: List[int] # List of company objects companies_uu_id_list: List[str] # List of company objects duty_id_list: List[int] # List of duty objects duty_uu_id_list: List[str] # List of duty objects selected_company: Optional[CompanyToken] = None # Selected Company Object @property def is_employee(self) -> bool: return True @property def is_occupant(self) -> bool: return False TokenDictType = Union[EmployeeTokenObject, OccupantTokenObject] class TokenProvider: AUTH_TOKEN: str = "AUTH_TOKEN" @classmethod def convert_redis_object_to_token( cls, redis_object: Dict[str, Any] ) -> TokenDictType: """ Process Redis object and return appropriate token object. """ if redis_object.get("user_type") == UserType.employee.value: return EmployeeTokenObject(**redis_object) elif redis_object.get("user_type") == UserType.occupant.value: return OccupantTokenObject(**redis_object) raise ValueError("Invalid user type") @classmethod def get_dict_from_redis( cls, token: Optional[str] = None, user_uu_id: Optional[str] = None ) -> Union[TokenDictType, List[TokenDictType]]: """ Retrieve token object from Redis using token and user_uu_id """ token_to_use, user_uu_id_to_use = token or "*", user_uu_id or "*" list_of_token_dict, auth_key_list = [], [ cls.AUTH_TOKEN, token_to_use, user_uu_id_to_use, ] if token: result = RedisActions.get_json(list_keys=auth_key_list, limit=1) if first_record := result.first: return cls.convert_redis_object_to_token(first_record) elif user_uu_id: result = RedisActions.get_json(list_keys=auth_key_list) if all_records := result.all: for all_record in all_records: list_of_token_dict.append( cls.convert_redis_object_to_token(all_record) ) return list_of_token_dict raise ValueError( "Token not found in Redis. Please check the token or user_uu_id." ) @classmethod def retrieve_application_codes(cls, page_url: str, token: TokenDictType): """ Retrieve application code from the token object or list of token objects. """ if isinstance(token, EmployeeTokenObject): if application_codes := token.selected_company.reachable_app_codes.get( page_url, None ): return application_codes elif isinstance(token, OccupantTokenObject): if application_codes := token.selected_occupant.reachable_app_codes.get( page_url, None ): return application_codes raise ValueError("Invalid token type or no application code found.") @classmethod def retrieve_event_codes(cls, endpoint_code: str, token: TokenDictType) -> str: """ Retrieve event code from the token object or list of token objects. """ if isinstance(token, EmployeeTokenObject): if event_codes := token.selected_company.reachable_event_codes.get( endpoint_code, None ): return event_codes elif isinstance(token, OccupantTokenObject): if event_codes := token.selected_occupant.reachable_event_codes.get( endpoint_code, None ): return event_codes raise ValueError("Invalid token type or no event code found.")