apikey_factory.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import os
  2. import functools
  3. from .apikey import apikey
  4. from .apikey_validator import apikey_validator
  5. from .apikey_exceptions import apikey_syntax_error
  6. class apikey_factory:
  7. """
  8. Factory for the API keys. That is useable to generating new random API
  9. keys, importing API keys from string, with validation, generating
  10. validators, and much more.
  11. """
  12. def __init__(self, prefix: str = "key") -> None:
  13. """
  14. That create new API keys factory, it require prefix, that mean static
  15. string which is pushed always before random part of the string. It
  16. also set API keys size, which is default 256.
  17. Parameters
  18. ----------
  19. prefix : str (default: "key")
  20. Prefix for the API keys.
  21. """
  22. self.__size = 256
  23. self.__prefix = prefix
  24. def set_size(self, size: int) -> object:
  25. """
  26. That change size of the API keys. Default API key size is 256, but
  27. it could be changed to other custom size. Size must be greater than
  28. prefix with prefix separator.
  29. Parameters
  30. ----------
  31. size : int
  32. Size of the API keys.
  33. Raises
  34. ------
  35. RuntimeError
  36. When new size is too small.
  37. Returns
  38. -------
  39. apikey_factory
  40. Self for chain loading.
  41. """
  42. if size <= len(self.prefix + self.prefix_separator):
  43. raise RuntimeError("Size of the API keys must be bigger.")
  44. self.__size = size
  45. return self
  46. def set_prefix(self, prefix: str) -> object:
  47. """
  48. That change prefix of the API keys generating by the factory.
  49. It could not countain any white space, and cound not being empty.
  50. Size of the API key also must being greater than prefix with
  51. separator character.
  52. Parameters
  53. ----------
  54. prefix : str
  55. New prefix to set.
  56. Raises
  57. ------
  58. ValueError
  59. When prefix is empty.
  60. ValueError
  61. When prefix contain white characters.
  62. RuntimeError
  63. When prefix is too long.
  64. Returns
  65. -------
  66. apikey_factory
  67. Self to chain loading.
  68. """
  69. prefix = prefix.strip()
  70. if len(prefix) == 0:
  71. raise ValueError("Prefix can not being empty.")
  72. for letter in prefix:
  73. if letter.isspace():
  74. raise ValueError("Prefix can not contain space.")
  75. if len(prefix + self.prefix_separator) >= self.size:
  76. raise RuntimeError("Prefix must be shorten")
  77. self.__prefix = prefix
  78. return self
  79. @property
  80. def prefix_separator(self) -> str:
  81. """
  82. That return prefix separator.
  83. Returns
  84. -------
  85. str
  86. Prefix separator.
  87. """
  88. return "_"
  89. @property
  90. def prefix(self) -> str:
  91. """
  92. That return current prefix separator.
  93. Returns
  94. -------
  95. str
  96. Current prefix separator.
  97. """
  98. return self.__prefix
  99. @property
  100. def size(self) -> int:
  101. """
  102. That return current API key size.
  103. Returns
  104. -------
  105. int
  106. Current API key size.
  107. """
  108. return self.__size
  109. @functools.lru_cache
  110. def __get_random_token_size(self, token_size: int) -> int:
  111. """
  112. That return size in bytes of random part of the API key.
  113. Parameters
  114. ----------
  115. token_size : int
  116. How much character have token.
  117. Returns
  118. -------
  119. int
  120. Size of the random token in bytes.
  121. """
  122. random_size = token_size / 2 + token_size % 2
  123. random_size = int(random_size)
  124. return random_size
  125. @functools.lru_cache
  126. def __get_token_size(self, full_size: int, prefix_size: int) -> int:
  127. """
  128. That return how much characters would have token.
  129. Parameters
  130. ----------
  131. full_size : int
  132. Full size of the API key.
  133. prefix_size : int
  134. Size of the API key prefix.
  135. Returns
  136. -------
  137. int
  138. Random token size.
  139. """
  140. return full_size - prefix_size - len(self.prefix_separator)
  141. def __get_random(self) -> str:
  142. """
  143. That return new random part of the API key.
  144. Returns
  145. -------
  146. str
  147. New random part of the API key.
  148. """
  149. token_size = self.__get_token_size(self.size, len(self.prefix))
  150. random_size = self.__get_random_token_size(token_size)
  151. return os.urandom(random_size).hex()[0:token_size]
  152. def __get_new_token(self) -> str:
  153. """
  154. That return new API key token.
  155. Returns
  156. -------
  157. str
  158. That return new API key token string.
  159. """
  160. return self.prefix + self.prefix_separator + self.__get_random()
  161. def generate(self) -> apikey:
  162. """
  163. That generate new random API key. Result is API key object, with
  164. parameters like size, prefix etc from the factory.
  165. Returns
  166. -------
  167. apikey
  168. New random API key.
  169. """
  170. return apikey(
  171. self.__get_new_token(),
  172. self.size,
  173. self.prefix,
  174. self.prefix_separator
  175. )
  176. def load(self, token: str) -> apikey:
  177. """
  178. That import API key from its string form to the API key object. It
  179. also validate it, and raise error when API key is not valid.
  180. Parameters
  181. ----------
  182. token : str
  183. API key as string to import.
  184. Raises
  185. ------
  186. apikey_syntax_error
  187. When API key is not valid.
  188. Returns
  189. -------
  190. apikey
  191. API key in its object form.
  192. """
  193. if not self.get_validator().validate(token):
  194. raise apikey_syntax_error()
  195. return apikey(
  196. token,
  197. self.size,
  198. self.prefix,
  199. self.prefix_separator
  200. )
  201. def get_validator(self) -> apikey_validator:
  202. """
  203. That generate API keys validator.
  204. Returns
  205. -------
  206. apikey_validator
  207. New API key validator with parameters from factory.
  208. """
  209. return self.__get_validator_cache(
  210. self.size,
  211. self.prefix,
  212. self.prefix_separator
  213. )
  214. @functools.lru_cache
  215. def __get_validator_cache(
  216. self,
  217. size: int,
  218. prefix: str,
  219. prefix_separator: str
  220. ) -> apikey_validator:
  221. """
  222. That is cache for API keys validator.
  223. Parameters
  224. ----------
  225. size : int
  226. Size of the API keys.
  227. prefix : str
  228. Prefix of the API keys.
  229. prefix_separator : str
  230. Prefix separator character.
  231. Returns
  232. -------
  233. apikey_validator
  234. New API key validator.
  235. """
  236. return apikey_validator(
  237. size,
  238. prefix,
  239. prefix_separator
  240. )