A multipurpose python flask API server and administration SPA
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
6.6 KiB

  1. """Service to handle user_token operations."""
  2. import uuid
  3. from datetime import datetime
  4. from typing import Optional, Dict, Callable, Any
  5. from flask_sqlalchemy import Pagination
  6. from iso8601 import iso8601, ParseError
  7. from corvus.db import db
  8. from corvus.model import User, UserToken
  9. from corvus.service.transformation_service import (
  10. BaseTransformer,
  11. register_transformer
  12. )
  13. from corvus import errors
  14. @register_transformer
  15. class UserTokenTransformer(BaseTransformer):
  16. """Serialize User model."""
  17. type = UserToken
  18. def _deserializers(
  19. self) -> Dict[str, Callable[[UserToken, Any], None]]:
  20. """Define the fields and the accompanying serializer factory."""
  21. return {
  22. 'token': self.deserialize_token,
  23. 'note': self.deserialize_note,
  24. 'enabled': self.deserialize_enabled,
  25. 'expirationTime': self.deserialize_expiration_time,
  26. 'creationTime': self.deserialize_creation_time,
  27. 'lastUsageTime': self.deserialize_last_usage_time,
  28. 'version': self.deserialize_version
  29. }
  30. def _serializers(self) -> Dict[str, Callable[[], Any]]:
  31. """Define the fields and the accompanying serializer factory."""
  32. return {
  33. 'token': self.serialize_token,
  34. 'note': self.serialize_note,
  35. 'enabled': self.serialize_enabled,
  36. 'expirationTime': self.serialize_expiration_time,
  37. 'creationTime': self.serialize_creation_time,
  38. 'lastUsageTime': self.serialize_last_usage_time,
  39. 'isValid': self.serialize_is_valid,
  40. 'version': self.serialize_version
  41. }
  42. def serialize_token(self) -> str:
  43. """User token."""
  44. return self.model.token
  45. @staticmethod
  46. def deserialize_token(model: UserToken, token: str) -> None:
  47. """User token."""
  48. model.token = token
  49. def serialize_note(self) -> str:
  50. """User token note."""
  51. return self.model.note
  52. @staticmethod
  53. def deserialize_note(model: UserToken, note: str) -> None:
  54. """User token note."""
  55. model.note = note
  56. def serialize_enabled(self) -> bool:
  57. """User token enabled."""
  58. return self.model.enabled
  59. @staticmethod
  60. def deserialize_enabled(model: UserToken, enabled: bool) -> None:
  61. """User token enabled."""
  62. model.enabled = enabled
  63. def serialize_expiration_time(self) -> datetime:
  64. """User token expiration time."""
  65. return self.model.expiration_time
  66. @staticmethod
  67. def deserialize_expiration_time(
  68. model: UserToken, expiration_time: datetime) -> None:
  69. """User token expiration time."""
  70. try:
  71. model.expiration_time = iso8601.parse_date(expiration_time)
  72. except ParseError:
  73. raise errors.ValidationError(
  74. 'Cannot parse datetime from %s' % expiration_time)
  75. def serialize_creation_time(self) -> datetime:
  76. """User token creation time."""
  77. return self.model.creation_time
  78. @staticmethod
  79. def deserialize_creation_time(
  80. model: UserToken, creation_time: datetime) -> None:
  81. """User token creation time."""
  82. try:
  83. model.creation_time = iso8601.parse_date(creation_time)
  84. except ParseError:
  85. raise errors.ValidationError(
  86. 'Cannot parse datetime from %s' % creation_time)
  87. def serialize_last_usage_time(self) -> datetime:
  88. """User token last usage time."""
  89. return self.model.last_usage_time
  90. @staticmethod
  91. def deserialize_last_usage_time(
  92. model: UserToken, last_usage_time: datetime) -> None:
  93. """User token last usage time."""
  94. model.last_usage_time = iso8601.parse_date(last_usage_time)
  95. def serialize_is_valid(self):
  96. """User token is_valid computed value."""
  97. return is_valid_token(self.model)
  98. def serialize_version(self) -> int:
  99. """User token version."""
  100. return self.model.version
  101. @staticmethod
  102. def deserialize_version(model: UserToken, version: int) -> None:
  103. """User token version."""
  104. model.version = version
  105. def generate_token() -> uuid.UUID:
  106. """
  107. Generate a unique token.
  108. :return:
  109. """
  110. return uuid.uuid4()
  111. def create(
  112. user: User,
  113. note: Optional[str] = None,
  114. enabled: bool = True,
  115. expiration_time: Optional[datetime] = None) -> UserToken:
  116. """
  117. Create and save a UserToken.
  118. :param user: The User object to bind the token to
  119. :param note: An optional field to store additional information about a
  120. token
  121. :param enabled: A boolean to indicate whether a token can be considered
  122. eligible for authentication
  123. :param expiration_time: An optional argument to determine when the token
  124. becomes invalid as a means of authentication. Defaults to None, which means
  125. no expiration
  126. :return:
  127. """
  128. token = generate_token()
  129. user_token = UserToken(
  130. user_id=user.id,
  131. token=token.__str__(),
  132. note=note,
  133. enabled=enabled,
  134. creation_time=datetime.now(),
  135. expiration_time=expiration_time,
  136. version=0)
  137. db.session.add(user_token)
  138. db.session.commit()
  139. return user_token
  140. def is_valid_token(user_token: Optional[UserToken]) -> bool:
  141. """
  142. Validate a token.
  143. Token must be enabled and if it has an expiration, it must be greater
  144. than now.
  145. :param user_token:
  146. :return:
  147. """
  148. if user_token is None:
  149. return False
  150. if not user_token.enabled:
  151. return False
  152. if (user_token.expiration_time is not None
  153. and user_token.expiration_time < datetime.utcnow()):
  154. return False
  155. return True
  156. def delete(user_token: UserToken) -> bool:
  157. """
  158. Delete a user_token.
  159. :param user_token:
  160. :return:
  161. """
  162. existing_user_token = db.session.delete(user_token)
  163. if existing_user_token is None:
  164. db.session.commit()
  165. return True
  166. return False
  167. def find_by_user_and_token(user: User, token: str) -> Optional[UserToken]:
  168. """
  169. Lookup a user_token by user and token string.
  170. :param user:
  171. :param token:
  172. :return:
  173. """
  174. return UserToken.query.filter_by(user_id=user.id, token=token).first()
  175. def find_by_user(user: User, page: int, per_page: int = 20,
  176. max_per_page: int = 100) -> Pagination:
  177. """
  178. Find all tokens for a user
  179. :param user: The user to find tokens for
  180. :param page: The page to request
  181. :param per_page: The number to get per page
  182. :param max_per_page:
  183. :return:
  184. """
  185. return UserToken.query.filter_by(
  186. user_id=user.id).paginate(page, per_page, True, max_per_page)