request.py 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 WorkspaceNotFoundInTracimRequest
  6. from tracim.exceptions import UserNotFoundInTracimRequest
  7. from tracim.exceptions import UserDoesNotExist
  8. from tracim.exceptions import WorkspaceNotFound
  9. from tracim.exceptions import ImmutableAttribute
  10. from tracim.lib.core.user import UserApi
  11. from tracim.lib.core.workspace import WorkspaceApi
  12. from tracim.lib.utils.authorization import JSONDecodeError
  13. from tracim.models import User
  14. from tracim.models.data import Workspace
  15. class TracimRequest(Request):
  16. """
  17. Request with tracim specific params/methods
  18. """
  19. def __init__(
  20. self,
  21. environ,
  22. charset=None,
  23. unicode_errors=None,
  24. decode_param_names=None,
  25. **kw
  26. ):
  27. super().__init__(
  28. environ,
  29. charset,
  30. unicode_errors,
  31. decode_param_names,
  32. **kw
  33. )
  34. # Current workspace, found in request path
  35. self._current_workspace = None # type: Workspace
  36. # Candidate workspace found in request body
  37. self._candidate_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._candidate_user = 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 = self._get_current_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 = self._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._candidate_user is None:
  93. self.candidate_user = self._get_candidate_user(self)
  94. return self._candidate_user
  95. @property
  96. def candidate_workspace(self) -> Workspace:
  97. """
  98. Get workspace from headers/body request. This workspace is not
  99. the one found from path. Its the one from json body.
  100. """
  101. if self._candidate_workspace is None:
  102. self._candidate_workspace = self._get_candidate_workspace(
  103. self.current_user,
  104. self
  105. )
  106. return self._candidate_workspace
  107. def _cleanup(self, request: 'TracimRequest') -> None:
  108. """
  109. Close dbsession at the end of the request in order to avoid exception
  110. about not properly closed session or "object created in another thread"
  111. issue
  112. see https://github.com/tracim/tracim_backend/issues/62
  113. :param request: same as self, request
  114. :return: nothing.
  115. """
  116. self._current_user = None
  117. self._current_workspace = None
  118. self.dbsession.close()
  119. @candidate_user.setter
  120. def candidate_user(self, user: User) -> None:
  121. if self._candidate_user is not None:
  122. raise ImmutableAttribute(
  123. "Can't modify already setted candidate_user"
  124. )
  125. self._candidate_user = user
  126. ###
  127. # Utils for TracimRequest
  128. ###
  129. def _get_candidate_user(
  130. self,
  131. request: 'TracimRequest',
  132. ) -> User:
  133. """
  134. Get candidate user
  135. :param request: pyramid request
  136. :return: user found from header/body
  137. """
  138. app_config = request.registry.settings['CFG']
  139. uapi = UserApi(None, session=request.dbsession, config=app_config)
  140. try:
  141. login = None
  142. if 'user_id' in request.matchdict:
  143. login = request.matchdict['user_id']
  144. if not login:
  145. raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one') # nopep8
  146. user = uapi.get_one(login)
  147. except UserNotFoundInTracimRequest as exc:
  148. raise UserDoesNotExist('User {} not found'.format(login)) from exc
  149. return user
  150. def _get_auth_safe_user(
  151. self,
  152. request: 'TracimRequest',
  153. ) -> User:
  154. """
  155. Get current pyramid authenticated user from request
  156. :param request: pyramid request
  157. :return: current authenticated user
  158. """
  159. app_config = request.registry.settings['CFG']
  160. uapi = UserApi(None, session=request.dbsession, config=app_config)
  161. try:
  162. login = request.authenticated_userid
  163. if not login:
  164. raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one') # nopep8
  165. user = uapi.get_one_by_email(login)
  166. except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc:
  167. raise NotAuthenticated('User {} not found'.format(login)) from exc
  168. return user
  169. def _get_current_workspace(
  170. self,
  171. user: User,
  172. request: 'TracimRequest'
  173. ) -> Workspace:
  174. """
  175. Get current workspace from request
  176. :param user: User who want to check the workspace
  177. :param request: pyramid request
  178. :return: current workspace
  179. """
  180. workspace_id = ''
  181. try:
  182. if 'workspace_id' in request.matchdict:
  183. workspace_id = request.matchdict['workspace_id']
  184. if not workspace_id:
  185. raise WorkspaceNotFoundInTracimRequest('No workspace_id property found in request')
  186. wapi = WorkspaceApi(
  187. current_user=user,
  188. session=request.dbsession,
  189. config=request.registry.settings['CFG']
  190. )
  191. workspace = wapi.get_one(workspace_id)
  192. except JSONDecodeError:
  193. raise WorkspaceNotFound('Bad json body')
  194. except NoResultFound:
  195. raise WorkspaceNotFound(
  196. 'Workspace {} does not exist '
  197. 'or is not visible for this user'.format(workspace_id)
  198. )
  199. return workspace
  200. def _get_candidate_workspace(
  201. self,
  202. user: User,
  203. request: 'TracimRequest'
  204. ) -> Workspace:
  205. """
  206. Get current workspace from request
  207. :param user: User who want to check the workspace
  208. :param request: pyramid request
  209. :return: current workspace
  210. """
  211. workspace_id = ''
  212. try:
  213. if 'new_workspace_id' in request.json_body:
  214. workspace_id = request.json_body['new_workspace_id']
  215. if not workspace_id:
  216. raise WorkspaceNotFoundInTracimRequest('No new_workspace_id property found in body')
  217. wapi = WorkspaceApi(
  218. current_user=user,
  219. session=request.dbsession,
  220. config=request.registry.settings['CFG']
  221. )
  222. workspace = wapi.get_one(workspace_id)
  223. except JSONDecodeError:
  224. raise WorkspaceNotFound('Bad json body')
  225. except NoResultFound:
  226. raise WorkspaceNotFound(
  227. 'Workspace {} does not exist '
  228. 'or is not visible for this user'.format(workspace_id)
  229. )
  230. return workspace