request.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. # -*- coding: utf-8 -*-
  2. """
  3. TracimRequest and related functions
  4. """
  5. from pyramid.request import Request
  6. from sqlalchemy.orm.exc import NoResultFound
  7. from tracim.exceptions import NotAuthenticated
  8. from tracim.exceptions import UserNotFoundInTracimRequest
  9. from tracim.exceptions import UserDoesNotExist
  10. from tracim.exceptions import WorkspaceNotFound
  11. from tracim.exceptions import ImmutableAttribute
  12. from tracim.lib.core.user import UserApi
  13. from tracim.lib.core.workspace import WorkspaceApi
  14. from tracim.lib.utils.authorization import JSONDecodeError
  15. from tracim.models import User
  16. from tracim.models.data import Workspace
  17. class TracimRequest(Request):
  18. """
  19. Request with tracim specific params/methods
  20. """
  21. def __init__(
  22. self,
  23. environ,
  24. charset=None,
  25. unicode_errors=None,
  26. decode_param_names=None,
  27. **kw
  28. ):
  29. super().__init__(
  30. environ,
  31. charset,
  32. unicode_errors,
  33. decode_param_names,
  34. **kw
  35. )
  36. # Current workspace, found by request headers or content
  37. self._current_workspace = None # type: Workspace
  38. # Authenticated user
  39. self._current_user = None # type: User
  40. # User found from request headers, content, distinct from authenticated
  41. # user
  42. self._user_candidate = None # type: User
  43. # INFO - G.M - 18-05-2018 - Close db at the end of the request
  44. self.add_finished_callback(self._cleanup)
  45. @property
  46. def current_workspace(self) -> Workspace:
  47. """
  48. Get current workspace of the request according to authentification and
  49. request headers (to retrieve workspace). Setted by default value the
  50. first time if not configured.
  51. :return: Workspace of the request
  52. """
  53. if self._current_workspace is None:
  54. self.current_workspace = get_workspace(self.current_user, self)
  55. return self._current_workspace
  56. @current_workspace.setter
  57. def current_workspace(self, workspace: Workspace) -> None:
  58. """
  59. Setting current_workspace
  60. :param workspace:
  61. :return:
  62. """
  63. if self._current_workspace is not None:
  64. raise ImmutableAttribute(
  65. "Can't modify already setted current_workspace"
  66. )
  67. self._current_workspace = workspace
  68. @property
  69. def current_user(self) -> User:
  70. """
  71. Get user from authentication mecanism.
  72. """
  73. if self._current_user is None:
  74. self.current_user = get_auth_safe_user(self)
  75. return self._current_user
  76. @current_user.setter
  77. def current_user(self, user: User) -> None:
  78. if self._current_user is not None:
  79. raise ImmutableAttribute(
  80. "Can't modify already setted current_user"
  81. )
  82. self._current_user = user
  83. # TODO - G.M - 24-05-2018 - Find a better naming for this ?
  84. @property
  85. def candidate_user(self) -> User:
  86. """
  87. Get user from headers/body request. This user is not
  88. the one found by authentication mecanism. This user
  89. can help user to know about who one page is about in
  90. a similar way as current_workspace.
  91. """
  92. if self._user_candidate is None:
  93. self.candidate_user = get_candidate_user(self)
  94. return self._user_candidate
  95. def _cleanup(self, request: 'TracimRequest') -> None:
  96. """
  97. Close dbsession at the end of the request in order to avoid exception
  98. about not properly closed session or "object created in another thread"
  99. issue
  100. see https://github.com/tracim/tracim_backend/issues/62
  101. :param request: same as self, request
  102. :return: nothing.
  103. """
  104. self._current_user = None
  105. self._current_workspace = None
  106. self.dbsession.close()
  107. @candidate_user.setter
  108. def candidate_user(self, user: User) -> None:
  109. if self._user_candidate is not None:
  110. raise ImmutableAttribute(
  111. "Can't modify already setted candidate_user"
  112. )
  113. self._user_candidate = user
  114. ###
  115. # Utils for TracimRequest
  116. ###
  117. def get_candidate_user(
  118. request: TracimRequest
  119. ) -> User:
  120. """
  121. Get candidate user
  122. :param request: pyramid request
  123. :return: user found from header/body
  124. """
  125. app_config = request.registry.settings['CFG']
  126. uapi = UserApi(None, session=request.dbsession, config=app_config)
  127. try:
  128. login = None
  129. if 'user_id' in request.matchdict:
  130. login = request.matchdict['user_id']
  131. if not login:
  132. raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one') # nopep8
  133. user = uapi.get_one(login)
  134. except UserNotFoundInTracimRequest as exc:
  135. raise UserDoesNotExist('User {} not found'.format(login)) from exc
  136. return user
  137. def get_auth_safe_user(
  138. request: TracimRequest,
  139. ) -> User:
  140. """
  141. Get current pyramid authenticated user from request
  142. :param request: pyramid request
  143. :return: current authenticated user
  144. """
  145. app_config = request.registry.settings['CFG']
  146. uapi = UserApi(None, session=request.dbsession, config=app_config)
  147. try:
  148. login = request.authenticated_userid
  149. if not login:
  150. raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one') # nopep8
  151. user = uapi.get_one_by_email(login)
  152. except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc:
  153. raise NotAuthenticated('User {} not found'.format(login)) from exc
  154. return user
  155. def get_workspace(
  156. user: User,
  157. request: TracimRequest
  158. ) -> Workspace:
  159. """
  160. Get current workspace from request
  161. :param user: User who want to check the workspace
  162. :param request: pyramid request
  163. :return: current workspace
  164. """
  165. workspace_id = ''
  166. try:
  167. if 'workspace_id' in request.matchdict:
  168. workspace_id = request.matchdict['workspace_id']
  169. if not workspace_id:
  170. raise WorkspaceNotFound('No workspace_id property found in request')
  171. wapi = WorkspaceApi(
  172. current_user=user,
  173. session=request.dbsession,
  174. config=request.registry.settings['CFG']
  175. )
  176. workspace = wapi.get_one(workspace_id)
  177. except JSONDecodeError:
  178. raise WorkspaceNotFound('Bad json body')
  179. except NoResultFound:
  180. raise WorkspaceNotFound(
  181. 'Workspace {} does not exist '
  182. 'or is not visible for this user'.format(workspace_id)
  183. )
  184. return workspace