user.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import sqlmodel
  2. from .apikey import apikey
  3. from .password import password
  4. from .builder import builder
  5. from .code_key import code_key
  6. from .code_key import code_key_manager
  7. from .secret_coder import bad_password
  8. class user(sqlmodel.SQLModel, table = True):
  9. """
  10. This represents user in database. All users has own unique nick and api
  11. key. Nick and password is used to login, api key is long, random and
  12. used to access database via API.
  13. """
  14. id: int = sqlmodel.Field(default = None, primary_key = True)
  15. nick: str = sqlmodel.Field(index = True, unique = True)
  16. password: str = sqlmodel.Field(index = False)
  17. apikey: str = sqlmodel.Field(index = True, unique = True)
  18. code_key: str = sqlmodel.Field(index = False)
  19. def key(self, password: str) -> code_key_manager | None:
  20. """
  21. This return crypto key wrapper, to manage, recrypt, encrypt, decrypt
  22. and other with key. It require password, and when code key is not
  23. set, return None.
  24. Parameters:
  25. password (str): Password used to decrypt crypto key
  26. Returns:
  27. (code_key_manager): Crypto key wrapper
  28. """
  29. if self.code_key is None:
  30. return None
  31. return code_key(password = password, crypted_key = self.code_key)
  32. @property
  33. def in_database(self) -> bool:
  34. """ True when user exists in database. """
  35. return self.id is not None
  36. @property
  37. def ready(self) -> bool:
  38. """ True when all fields are filled. """
  39. if self.nick is None:
  40. return False
  41. if self.password is None:
  42. return False
  43. if self.apikey is None:
  44. return False
  45. if self.code_key is None:
  46. return False
  47. return True
  48. def __str__(self) -> str:
  49. """
  50. This function dump user to string, very usefull for debug.
  51. Returns:
  52. (str): User as string
  53. """
  54. result = ""
  55. result = result + "User "
  56. if self.id is not None:
  57. result = result + "(" + str(self.id) + ")"
  58. result = result + "\n"
  59. result = result + "Nick: " + self.nick + "\n"
  60. result = result + "Password: " + self.password + "\n"
  61. result = result + "API key: " + self.apikey + "\n"
  62. result = result + "Code KEY: " + self.code_key + "\n"
  63. return result
  64. class user_builder(builder, target_type = user):
  65. """
  66. This class is responsible for building new user.
  67. """
  68. def __init__(self, target: user | None = None) -> None:
  69. """
  70. This create new user builder. It can be initialized by already
  71. created user. When None create empty user.
  72. Parameters:
  73. target(user | None): Target to initialize builder with
  74. """
  75. super().__init__(target)
  76. if target is None:
  77. self.refresh_apikey()
  78. def refresh_apikey(self) -> None:
  79. """
  80. This function refresh apikey of the user. It could be useable to
  81. logout all of the devices which use user account.
  82. """
  83. self._target.apikey = apikey()
  84. @property
  85. def nick(self) -> str | None:
  86. """
  87. return: str | None - Current nick of the user in builder
  88. """
  89. return self._target.nick
  90. @property
  91. def password(self) -> bool:
  92. """
  93. return: bool - True when password is set, false if not
  94. """
  95. return self._target.password is not None
  96. @nick.setter
  97. def nick(self, target: str) -> None:
  98. """
  99. target: str - New nick for the user
  100. """
  101. self._target.nick = target.upper()
  102. def check_password(self, target: str) -> bool:
  103. """
  104. This function check that given password is correct with current user
  105. password. It is usefull when trying to have second factor, password +
  106. ApiKey.
  107. Parameters:
  108. target (str): Password to check
  109. Returns:
  110. (bool): True when given password is correct, False when not
  111. """
  112. return password(target).validate(self._target.password)
  113. def set_password(
  114. self,
  115. password: str,
  116. old_password: str | None = None
  117. ) -> bool:
  118. """
  119. This function set password to user. When only password is given, then
  120. it try to init user, which not have password and crypto key yet. User
  121. which already set password, and crypto key, must being updated with
  122. also old password.
  123. Parameters:
  124. password (str): New password to set
  125. old_password (str | None) = None: Old password, require to recrypt
  126. Returns:
  127. (bool): True when changed success, False when old password is bad
  128. """
  129. if old_password is None:
  130. self.__init_password(password)
  131. return True
  132. return self.__change_password(old_password, password)
  133. def __init_password(self, target: str) -> None:
  134. """
  135. This function initialize user with new password. User can not already
  136. have both password, and crypto key. When user already have crypto key
  137. or password, then Exception is raised, to protect crypto key to not
  138. being overwrited.
  139. Parameters:
  140. target (str): New password to set
  141. """
  142. if self._target.password is not None:
  143. raise Exception("Password and code key is already set.")
  144. if self._target.code_key is not None:
  145. raise Exception("Password and code key is already set.")
  146. self._target.password = password(target).result
  147. self._target.code_key = code_key(password = target)
  148. def __change_password(self, old_password: str, new_password: str) -> bool:
  149. """
  150. This change password, when user already have password and code key. It
  151. recrypt crypto key, to could use new password for secret decrypting.
  152. Parameters:
  153. old_password (str): Old password
  154. new_password (str): New password to set
  155. Returns:
  156. (bool): True when changed success, False when old password is bad
  157. """
  158. if old_password == new_password:
  159. return True
  160. try:
  161. key = self._target.key(old_password)
  162. if key is None or self._target.password is None:
  163. raise Exception("User crypto key is not initialized yet.")
  164. self._target.code_key = key.recrypt(new_password).encrypted
  165. self._target.password = password(new_password).result
  166. return True
  167. except bad_password:
  168. return False