request.py 6.9KB

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