request.py 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. # -*- coding: utf-8 -*-
  2. from pyramid.request import Request
  3. from sqlalchemy.orm.exc import NoResultFound
  4. from tracim.exceptions import NotAuthenticated, WorkspaceNotFoundInTracimRequest
  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 in request path
  34. self._current_workspace = None # type: Workspace
  35. # Candidate workspace found in request body
  36. self._candidate_workspace = None # type: Workspace
  37. # Authenticated user
  38. self._current_user = None # type: User
  39. # User found from request headers, content, distinct from authenticated
  40. # user
  41. self._candidate_user = None # type: User
  42. # INFO - G.M - 18-05-2018 - Close db at the end of the request
  43. self.add_finished_callback(self._cleanup)
  44. @property
  45. def current_workspace(self) -> Workspace:
  46. """
  47. Get current workspace of the request according to authentification and
  48. request headers (to retrieve workspace). Setted by default value the
  49. first time if not configured.
  50. :return: Workspace of the request
  51. """
  52. if self._current_workspace is None:
  53. self.current_workspace = self._get_current_workspace(self.current_user, self)
  54. return self._current_workspace
  55. @current_workspace.setter
  56. def current_workspace(self, workspace: Workspace) -> None:
  57. """
  58. Setting current_workspace
  59. :param workspace:
  60. :return:
  61. """
  62. if self._current_workspace is not None:
  63. raise ImmutableAttribute(
  64. "Can't modify already setted current_workspace"
  65. )
  66. self._current_workspace = workspace
  67. @property
  68. def current_user(self) -> User:
  69. """
  70. Get user from authentication mecanism.
  71. """
  72. if self._current_user is None:
  73. self.current_user = self._get_auth_safe_user(self)
  74. return self._current_user
  75. @current_user.setter
  76. def current_user(self, user: User) -> None:
  77. if self._current_user is not None:
  78. raise ImmutableAttribute(
  79. "Can't modify already setted current_user"
  80. )
  81. self._current_user = user
  82. # TODO - G.M - 24-05-2018 - Find a better naming for this ?
  83. @property
  84. def candidate_user(self) -> User:
  85. """
  86. Get user from headers/body request. This user is not
  87. the one found by authentication mecanism. This user
  88. can help user to know about who one page is about in
  89. a similar way as current_workspace.
  90. """
  91. if self._candidate_user is None:
  92. self.candidate_user = self._get_candidate_user(self)
  93. return self._candidate_user
  94. @property
  95. def candidate_workspace(self) -> Workspace:
  96. """
  97. Get user from headers/body request. This user is not
  98. the one found by authentication mecanism. This user
  99. can help user to know about who one page is about in
  100. a similar way as current_workspace.
  101. """
  102. if self._candidate_workspace is None:
  103. self._candidate_workspace = self._get_candidate_workspace(
  104. self.current_user,
  105. self
  106. )
  107. return self._candidate_workspace
  108. def _cleanup(self, request: 'TracimRequest') -> None:
  109. """
  110. Close dbsession at the end of the request in order to avoid exception
  111. about not properly closed session or "object created in another thread"
  112. issue
  113. see https://github.com/tracim/tracim_backend/issues/62
  114. :param request: same as self, request
  115. :return: nothing.
  116. """
  117. self._current_user = None
  118. self._current_workspace = None
  119. self.dbsession.close()
  120. @candidate_user.setter
  121. def candidate_user(self, user: User) -> None:
  122. if self._candidate_user is not None:
  123. raise ImmutableAttribute(
  124. "Can't modify already setted candidate_user"
  125. )
  126. self._candidate_user = user
  127. ###
  128. # Utils for TracimRequest
  129. ###
  130. def _get_candidate_user(
  131. self,
  132. request: 'TracimRequest',
  133. ) -> User:
  134. """
  135. Get candidate user
  136. :param request: pyramid request
  137. :return: user found from header/body
  138. """
  139. app_config = request.registry.settings['CFG']
  140. uapi = UserApi(None, session=request.dbsession, config=app_config)
  141. try:
  142. login = None
  143. if 'user_id' in request.matchdict:
  144. login = request.matchdict['user_id']
  145. if not login:
  146. raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one') # nopep8
  147. user = uapi.get_one(login)
  148. except UserNotFoundInTracimRequest as exc:
  149. raise UserDoesNotExist('User {} not found'.format(login)) from exc
  150. return user
  151. def _get_auth_safe_user(
  152. self,
  153. request: 'TracimRequest',
  154. ) -> User:
  155. """
  156. Get current pyramid authenticated user from request
  157. :param request: pyramid request
  158. :return: current authenticated user
  159. """
  160. app_config = request.registry.settings['CFG']
  161. uapi = UserApi(None, session=request.dbsession, config=app_config)
  162. try:
  163. login = request.authenticated_userid
  164. if not login:
  165. raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one') # nopep8
  166. user = uapi.get_one_by_email(login)
  167. except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc:
  168. raise NotAuthenticated('User {} not found'.format(login)) from exc
  169. return user
  170. def _get_current_workspace(
  171. self,
  172. user: User,
  173. request: 'TracimRequest'
  174. ) -> Workspace:
  175. """
  176. Get current workspace from request
  177. :param user: User who want to check the workspace
  178. :param request: pyramid request
  179. :return: current workspace
  180. """
  181. workspace_id = ''
  182. try:
  183. if 'workspace_id' in request.matchdict:
  184. workspace_id = request.matchdict['workspace_id']
  185. if not workspace_id:
  186. raise WorkspaceNotFoundInTracimRequest('No workspace_id property found in request')
  187. wapi = WorkspaceApi(
  188. current_user=user,
  189. session=request.dbsession,
  190. config=request.registry.settings['CFG']
  191. )
  192. workspace = wapi.get_one(workspace_id)
  193. except JSONDecodeError:
  194. raise WorkspaceNotFound('Bad json body')
  195. except NoResultFound:
  196. raise WorkspaceNotFound(
  197. 'Workspace {} does not exist '
  198. 'or is not visible for this user'.format(workspace_id)
  199. )
  200. return workspace
  201. def _get_candidate_workspace(
  202. self,
  203. user: User,
  204. request: 'TracimRequest'
  205. ) -> Workspace:
  206. """
  207. Get current workspace from request
  208. :param user: User who want to check the workspace
  209. :param request: pyramid request
  210. :return: current workspace
  211. """
  212. workspace_id = ''
  213. try:
  214. if 'new_workspace_id' in request.json_body:
  215. workspace_id = request.json_body['new_workspace_id']
  216. if not workspace_id:
  217. raise WorkspaceNotFoundInTracimRequest('No new_workspace_id property found in body')
  218. wapi = WorkspaceApi(
  219. current_user=user,
  220. session=request.dbsession,
  221. config=request.registry.settings['CFG']
  222. )
  223. workspace = wapi.get_one(workspace_id)
  224. except JSONDecodeError:
  225. raise WorkspaceNotFound('Bad json body')
  226. except NoResultFound:
  227. raise WorkspaceNotFound(
  228. 'Workspace {} does not exist '
  229. 'or is not visible for this user'.format(workspace_id)
  230. )
  231. return workspace