import sqlmodel from .apikey import apikey from .password import password from .builder import builder from .code_key import code_key from .code_key import code_key_manager class user(sqlmodel.SQLModel, table = True): """ This represents user in database. All users has own unique nick and api key. Nick and password is used to login, api key is long, random and used to access database via API. """ id: int = sqlmodel.Field(default = None, primary_key = True) nick: str = sqlmodel.Field(index = True, unique = True) password: str = sqlmodel.Field(index = False) apikey: str = sqlmodel.Field(index = True, unique = True) code_key: str = sqlmodel.Field(index = False) def key(self, password: str) -> code_key_manager | None: """ This return crypto key wrapper, to manage, recrypt, encrypt, decrypt and other with key. It require password, and when code key is not set, return None. Parameters: password (str): Password used to decrypt crypto key Returns: (code_key_manager): Crypto key wrapper """ if self.code_key is None: return None return code_key(password = password, crypted_key = self.code_key) @property def in_database(self) -> bool: """ True when user exists in database. """ return self.id is not None @property def ready(self) -> bool: """ True when all fields are filled. """ if self.nick is None: return False if self.password is None: return False if self.apikey is None: return False if self.code_key is None: return False return True def __str__(self) -> str: """ This function dump user to string, very usefull for debug. Returns: (str): User as string """ result = "" result = result + "User " if self.id is not None: result = result + "(" + str(self.id) + ")" result = result + "\n" result = result + "Nick: " + self.nick + "\n" result = result + "Password: " + self.password + "\n" result = result + "API key: " + self.apikey + "\n" result = result + "Code KEY: " + self.code_key + "\n" return result class user_builder(builder, target_type = user): """ This class is responsible for building new user. """ def __init__(self, target: user | None = None) -> None: """ This create new user builder. It can be initialized by already created user. When None create empty user. Parameters: target(user | None): Target to initialize builder with """ super().__init__(target) self._target.apikey = apikey() @property def nick(self) -> str | None: """ return: str | None - Current nick of the user in builder """ return self._target.nick @property def password(self) -> bool: """ return: bool - True when password is set, false if not """ return self._target.password is not None @nick.setter def nick(self, target: str) -> None: """ target: str - New nick for the user """ self._target.nick = target.upper() def set_password( self, password: str, old_password: str | None = None ) -> None: """ This function set password to user. When only password is given, then it try to init user, which not have password and crypto key yet. User which already set password, and crypto key, must being updated with also old password. Parameters: password (str): New password to set old_password (str | None) = None: Old password, require to recrypt """ if old_password is None: self.__init_password(password) return self.__change_password(old_password, password) def __init_password(self, target: str) -> None: """ This function initialize user with new password. User can not already have both password, and crypto key. When user already have crypto key or password, then Exception is raised, to protect crypto key to not being overwrited. Parameters: target (str): New password to set """ if self._target.password is not None: raise Exception("Password and code key is already set.") if self._target.code_key is not None: raise Exception("Password and code key is already set.") self._target.password = password(target).result self._target.code_key = code_key(password = target) def __change_password(self, old_password: str, new_password: str) -> None: """ This change password, when user already have password and code key. It recrypt crypto key, to could use new password for secret decrypting. Parameters: old_password (str): Old password new_password (str): New password to set """ if old_password == new_password: raise Exception("New password is same as old password.") key = self._target.key(old_password) if key is None or self._target.password is None: raise Exception("User crypto key is not initialized yet.") self._target.code_key = key.recrypt(new_password).encrypted self._target.password = password(new_password).result