user.py 7.0 KB

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