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 reachable_event_codes: Optional[dict[str, list[str]]] = ( None # ID list of reachable modules ) reachable_app_codes: Optional[dict[str, list[str]]] = ( None # ID list of reachable modules ) 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 reachable_event_codes: Optional[dict[str, list[str]]] = ( None # ID list of reachable modules ) reachable_app_codes: Optional[dict[str, list[str]]] = ( None # ID list of reachable modules ) 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 process_redis_object(cls, redis_object: Dict[str, Any]) -> TokenDictType: """ Process Redis object and return appropriate token object. """ if not redis_object.get("selected_company"): redis_object["selected_company"] = None if not redis_object.get("selected_occupant"): redis_object["selected_occupant"] = None 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.process_redis_object(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.process_redis_object(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, tokens: Union[TokenDictType, List[TokenDictType]] ): if isinstance(tokens, TokenDictType): if application_codes := tokens.reachable_app_codes.get(page_url, None): return application_codes elif isinstance(tokens, list): retrieved_event_apps = [] for token in tokens: if application_codes := token.reachable_app_codes.get(page_url, None): retrieved_event_apps.append(application_codes) return retrieved_event_apps raise ValueError("Invalid token type or no application codes found.") @classmethod def retrieve_event_codes( cls, endpoint_code: str, tokens: Union[TokenDictType, List[TokenDictType]] ): if isinstance(tokens, TokenDictType): if event_codes := tokens.reachable_event_codes.get(endpoint_code, None): return event_codes elif isinstance(tokens, List): retrieved_event_codes = [] for token in tokens: if isinstance(token, TokenDictType): if event_codes := token.reachable_event_codes.get( endpoint_code, None ): retrieved_event_codes.append(event_codes) return retrieved_event_codes raise ValueError("Invalid token type or no event codes found.")