request.py 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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, 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 by request headers or content
  35. self._current_workspace = None # type: Workspace
  36. # Authenticated user
  37. self._current_user = None # type: User
  38. # User found from request headers, content, distinct from authenticated
  39. # user
  40. self._user_candidate = None # type: User
  41. # INFO - G.M - 18-05-2018 - Close db at the end of the request
  42. self.add_finished_callback(self._cleanup)
  43. @property
  44. def current_workspace(self) -> Workspace:
  45. """
  46. Get current workspace of the request according to authentification and
  47. request headers (to retrieve workspace). Setted by default value the
  48. first time if not configured.
  49. :return: Workspace of the request
  50. """
  51. if self._current_workspace is None:
  52. self.current_workspace = get_workspace(self.current_user, self)
  53. return self._current_workspace
  54. @current_workspace.setter
  55. def current_workspace(self, workspace: Workspace) -> None:
  56. """
  57. Setting current_workspace
  58. :param workspace:
  59. :return:
  60. """
  61. if self._current_workspace is not None:
  62. raise ImmutableAttribute(
  63. "Can't modify already setted current_workspace"
  64. )
  65. self._current_workspace = workspace
  66. @property
  67. def current_user(self) -> User:
  68. """
  69. Get user from authentication mecanism.
  70. """
  71. if self._current_user is None:
  72. self.current_user = get_auth_safe_user(self)
  73. return self._current_user
  74. @current_user.setter
  75. def current_user(self, user: User) -> None:
  76. if self._current_user is not None:
  77. raise ImmutableAttribute(
  78. "Can't modify already setted current_user"
  79. )
  80. self._current_user = user
  81. # TODO - G.M - 24-05-2018 - Find a better naming for this ?
  82. @property
  83. def candidate_user(self) -> User:
  84. """
  85. Get user from headers/body request. This user is not
  86. the one found by authentication mecanism. This user
  87. can help user to know about who one page is about in
  88. a similar way as current_workspace.
  89. """
  90. if self._user_candidate is None:
  91. self.candidate_user = get_candidate_user(self)
  92. return self._user_candidate
  93. def _cleanup(self, request: 'TracimRequest') -> None:
  94. """
  95. Close dbsession at the end of the request in order to avoid exception
  96. about not properly closed session or "object created in another thread"
  97. issue
  98. see https://github.com/tracim/tracim_backend/issues/62
  99. :param request: same as self, request
  100. :return: nothing.
  101. """
  102. self._current_user = None
  103. self._current_workspace = None
  104. self.dbsession.close()
  105. @candidate_user.setter
  106. def candidate_user(self, user: User) -> None:
  107. if self._user_candidate is not None:
  108. raise ImmutableAttribute(
  109. "Can't modify already setted candidate_user"
  110. )
  111. self._user_candidate = user
  112. ###
  113. # Utils for TracimRequest
  114. ###
  115. def get_candidate_user(
  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 UserDoesNotExist('no user_id found, incorrect request ?')
  131. user = uapi.get_one(login)
  132. except NoResultFound:
  133. raise NotAuthentificated('User not found')
  134. return user
  135. def get_auth_safe_user(
  136. request: TracimRequest,
  137. ) -> User:
  138. """
  139. Get current pyramid authenticated user from request
  140. :param request: pyramid request
  141. :return: current authenticated user
  142. """
  143. app_config = request.registry.settings['CFG']
  144. uapi = UserApi(None, session=request.dbsession, config=app_config)
  145. try:
  146. login = request.authenticated_userid
  147. if not login:
  148. raise NotAuthentificated('not authenticated user_id,'
  149. 'Failed Authentification ?')
  150. user = uapi.get_one_by_email(login)
  151. except NoResultFound:
  152. raise NotAuthentificated('User not found')
  153. return user
  154. def get_workspace(
  155. user: User,
  156. request: TracimRequest
  157. ) -> Workspace:
  158. """
  159. Get current workspace from request
  160. :param user: User who want to check the workspace
  161. :param request: pyramid request
  162. :return: current workspace
  163. """
  164. workspace_id = ''
  165. try:
  166. if 'workspace_id' in request.matchdict:
  167. workspace_id = request.matchdict['workspace_id']
  168. if not workspace_id:
  169. raise WorkspaceNotFound('No workspace_id param')
  170. wapi = WorkspaceApi(
  171. current_user=user,
  172. session=request.dbsession,
  173. config=request.registry.settings['CFG']
  174. )
  175. workspace = wapi.get_one(workspace_id)
  176. except JSONDecodeError:
  177. raise WorkspaceNotFound('Bad json body')
  178. except NoResultFound:
  179. raise WorkspaceNotFound(
  180. 'Workspace {} does not exist '
  181. 'or is not visible for this user'.format(workspace_id)
  182. )
  183. return workspace