request.py 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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 NotAuthentificated
  8. from tracim.exceptions import UserNotExist
  9. from tracim.exceptions import WorkspaceNotFound
  10. from tracim.exceptions import ImmutableAttribute
  11. from tracim.lib.core.user import UserApi
  12. from tracim.lib.core.workspace import WorkspaceApi
  13. from tracim.lib.utils.authorization import JSONDecodeError
  14. from tracim.models import User
  15. from tracim.models.data import Workspace
  16. class TracimRequest(Request):
  17. """
  18. Request with tracim specific params/methods
  19. """
  20. def __init__(
  21. self,
  22. environ,
  23. charset=None,
  24. unicode_errors=None,
  25. decode_param_names=None,
  26. **kw
  27. ):
  28. super().__init__(
  29. environ,
  30. charset,
  31. unicode_errors,
  32. decode_param_names,
  33. **kw
  34. )
  35. # Current workspace, found by request headers or content
  36. self._current_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._user_candidate = 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 = get_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 = 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._user_candidate is None:
  92. self.candidate_user = get_candidate_user(self)
  93. return self._user_candidate
  94. def _cleanup(self, request: 'TracimRequest') -> None:
  95. """
  96. Close dbsession at the end of the request in order to avoid exception
  97. about not properly closed session or "object created in another thread"
  98. issue
  99. see https://github.com/tracim/tracim_backend/issues/62
  100. :param request: same as self, request
  101. :return: nothing.
  102. """
  103. self._current_user = None
  104. self._current_workspace = None
  105. self.dbsession.close()
  106. @candidate_user.setter
  107. def candidate_user(self, user: User) -> None:
  108. if self._user_candidate is not None:
  109. raise ImmutableAttribute(
  110. "Can't modify already setted candidate_user"
  111. )
  112. self._user_candidate = user
  113. ###
  114. # Utils for TracimRequest
  115. ###
  116. def get_candidate_user(
  117. request: TracimRequest
  118. ) -> User:
  119. """
  120. Get candidate user
  121. :param request: pyramid request
  122. :return: user found from header/body
  123. """
  124. app_config = request.registry.settings['CFG']
  125. uapi = UserApi(None, session=request.dbsession, config=app_config)
  126. try:
  127. login = None
  128. if 'user_id' in request.matchdict:
  129. login = request.matchdict['user_id']
  130. if not login:
  131. raise UserNotExist('no user_id found, incorrect request ?')
  132. user = uapi.get_one(login)
  133. except NoResultFound:
  134. raise NotAuthentificated('User not found')
  135. return user
  136. def get_auth_safe_user(
  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 NotAuthentificated('not authenticated user_id,'
  150. 'Failed Authentification ?')
  151. user = uapi.get_one_by_email(login)
  152. except NoResultFound:
  153. raise NotAuthentificated('User not found')
  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 param')
  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