user_controller.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. from pyramid.config import Configurator
  2. try: # Python 3.5+
  3. from http import HTTPStatus
  4. except ImportError:
  5. from http import client as HTTPStatus
  6. from tracim_backend import hapic
  7. from tracim_backend import TracimRequest
  8. from tracim_backend.models import Group
  9. from tracim_backend.lib.core.group import GroupApi
  10. from tracim_backend.lib.core.user import UserApi
  11. from tracim_backend.lib.core.workspace import WorkspaceApi
  12. from tracim_backend.lib.core.content import ContentApi
  13. from tracim_backend.views.controllers import Controller
  14. from tracim_backend.lib.utils.authorization import require_same_user_or_profile
  15. from tracim_backend.lib.utils.authorization import require_profile
  16. from tracim_backend.exceptions import WrongUserPassword
  17. from tracim_backend.exceptions import PasswordDoNotMatch
  18. from tracim_backend.views.core_api.schemas import UserSchema
  19. from tracim_backend.views.core_api.schemas import SetEmailSchema
  20. from tracim_backend.views.core_api.schemas import SetPasswordSchema
  21. from tracim_backend.views.core_api.schemas import UserInfosSchema
  22. from tracim_backend.views.core_api.schemas import UserCreationSchema
  23. from tracim_backend.views.core_api.schemas import UserProfileSchema
  24. from tracim_backend.views.core_api.schemas import UserIdPathSchema
  25. from tracim_backend.views.core_api.schemas import ReadStatusSchema
  26. from tracim_backend.views.core_api.schemas import ContentIdsQuerySchema
  27. from tracim_backend.views.core_api.schemas import NoContentSchema
  28. from tracim_backend.views.core_api.schemas import UserWorkspaceIdPathSchema
  29. from tracim_backend.views.core_api.schemas import UserWorkspaceAndContentIdPathSchema
  30. from tracim_backend.views.core_api.schemas import ContentDigestSchema
  31. from tracim_backend.views.core_api.schemas import ActiveContentFilterQuerySchema
  32. from tracim_backend.views.core_api.schemas import WorkspaceDigestSchema
  33. SWAGGER_TAG__USER_ENDPOINTS = 'Users'
  34. class UserController(Controller):
  35. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  36. @require_same_user_or_profile(Group.TIM_ADMIN)
  37. @hapic.input_path(UserIdPathSchema())
  38. @hapic.output_body(WorkspaceDigestSchema(many=True),)
  39. def user_workspace(self, context, request: TracimRequest, hapic_data=None):
  40. """
  41. Get list of user workspaces
  42. """
  43. app_config = request.registry.settings['CFG']
  44. wapi = WorkspaceApi(
  45. current_user=request.candidate_user, # User
  46. session=request.dbsession,
  47. config=app_config,
  48. )
  49. workspaces = wapi.get_all_for_user(request.candidate_user)
  50. return [
  51. wapi.get_workspace_with_context(workspace)
  52. for workspace in workspaces
  53. ]
  54. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  55. @require_same_user_or_profile(Group.TIM_ADMIN)
  56. @hapic.input_path(UserIdPathSchema())
  57. @hapic.output_body(UserSchema())
  58. def user(self, context, request: TracimRequest, hapic_data=None):
  59. """
  60. Get user infos.
  61. """
  62. app_config = request.registry.settings['CFG']
  63. uapi = UserApi(
  64. current_user=request.current_user, # User
  65. session=request.dbsession,
  66. config=app_config,
  67. )
  68. return uapi.get_user_with_context(request.candidate_user)
  69. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  70. @hapic.handle_exception(WrongUserPassword, HTTPStatus.FORBIDDEN)
  71. @require_same_user_or_profile(Group.TIM_ADMIN)
  72. @hapic.input_body(SetEmailSchema())
  73. @hapic.input_path(UserIdPathSchema())
  74. @hapic.output_body(UserSchema())
  75. def set_user_email(self, context, request: TracimRequest, hapic_data=None):
  76. """
  77. Set user Email
  78. """
  79. app_config = request.registry.settings['CFG']
  80. uapi = UserApi(
  81. current_user=request.current_user, # User
  82. session=request.dbsession,
  83. config=app_config,
  84. )
  85. user = uapi.set_email(
  86. request.candidate_user,
  87. hapic_data.body.loggedin_user_password,
  88. hapic_data.body.email,
  89. do_save=True
  90. )
  91. return uapi.get_user_with_context(user)
  92. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  93. @hapic.handle_exception(WrongUserPassword, HTTPStatus.FORBIDDEN)
  94. @hapic.handle_exception(PasswordDoNotMatch, HTTPStatus.BAD_REQUEST)
  95. @require_same_user_or_profile(Group.TIM_ADMIN)
  96. @hapic.input_body(SetPasswordSchema())
  97. @hapic.input_path(UserIdPathSchema())
  98. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  99. def set_user_password(self, context, request: TracimRequest, hapic_data=None): # nopep8
  100. """
  101. Set user password
  102. """
  103. app_config = request.registry.settings['CFG']
  104. uapi = UserApi(
  105. current_user=request.current_user, # User
  106. session=request.dbsession,
  107. config=app_config,
  108. )
  109. uapi.set_password(
  110. request.candidate_user,
  111. hapic_data.body.loggedin_user_password,
  112. hapic_data.body.new_password,
  113. hapic_data.body.new_password2,
  114. do_save=True
  115. )
  116. return
  117. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  118. @require_same_user_or_profile(Group.TIM_ADMIN)
  119. @hapic.input_body(UserInfosSchema())
  120. @hapic.input_path(UserIdPathSchema())
  121. @hapic.output_body(UserSchema())
  122. def set_user_infos(self, context, request: TracimRequest, hapic_data=None):
  123. """
  124. Set user info data
  125. """
  126. app_config = request.registry.settings['CFG']
  127. uapi = UserApi(
  128. current_user=request.current_user, # User
  129. session=request.dbsession,
  130. config=app_config,
  131. )
  132. user = uapi.update(
  133. request.candidate_user,
  134. name=hapic_data.body.public_name,
  135. timezone=hapic_data.body.timezone,
  136. do_save=True
  137. )
  138. return uapi.get_user_with_context(user)
  139. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  140. @require_profile(Group.TIM_ADMIN)
  141. @hapic.input_path(UserIdPathSchema())
  142. @hapic.input_body(UserCreationSchema())
  143. @hapic.output_body(UserSchema())
  144. def create_user(self, context, request: TracimRequest, hapic_data=None):
  145. """
  146. Create new user
  147. """
  148. app_config = request.registry.settings['CFG']
  149. uapi = UserApi(
  150. current_user=request.current_user, # User
  151. session=request.dbsession,
  152. config=app_config,
  153. )
  154. gapi = GroupApi(
  155. current_user=request.current_user, # User
  156. session=request.dbsession,
  157. config=app_config,
  158. )
  159. groups = [gapi.get_one_with_name(hapic_data.body.profile)]
  160. user = uapi.create_user(
  161. email=hapic_data.body.email,
  162. password=hapic_data.body.password,
  163. timezone=hapic_data.body.timezone,
  164. name=hapic_data.body.public_name,
  165. do_notify=hapic_data.body.email_notification,
  166. groups=groups,
  167. do_save=True
  168. )
  169. return uapi.get_user_with_context(user)
  170. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  171. @require_profile(Group.TIM_ADMIN)
  172. @hapic.input_path(UserIdPathSchema())
  173. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  174. def enable_user(self, context, request: TracimRequest, hapic_data=None):
  175. """
  176. enable user
  177. """
  178. app_config = request.registry.settings['CFG']
  179. uapi = UserApi(
  180. current_user=request.current_user, # User
  181. session=request.dbsession,
  182. config=app_config,
  183. )
  184. uapi.enable(user=request.candidate_user, do_save=True)
  185. return
  186. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  187. @require_profile(Group.TIM_ADMIN)
  188. @hapic.input_path(UserIdPathSchema())
  189. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  190. def disable_user(self, context, request: TracimRequest, hapic_data=None):
  191. """
  192. disable user
  193. """
  194. app_config = request.registry.settings['CFG']
  195. uapi = UserApi(
  196. current_user=request.current_user, # User
  197. session=request.dbsession,
  198. config=app_config,
  199. )
  200. uapi.disable(user=request.candidate_user, do_save=True)
  201. return
  202. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  203. @require_profile(Group.TIM_ADMIN)
  204. @hapic.input_path(UserIdPathSchema())
  205. @hapic.input_body(UserProfileSchema())
  206. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  207. def set_profile(self, context, request: TracimRequest, hapic_data=None):
  208. """
  209. set user profile
  210. """
  211. app_config = request.registry.settings['CFG']
  212. uapi = UserApi(
  213. current_user=request.current_user, # User
  214. session=request.dbsession,
  215. config=app_config,
  216. )
  217. gapi = GroupApi(
  218. current_user=request.current_user, # User
  219. session=request.dbsession,
  220. config=app_config,
  221. )
  222. groups = [gapi.get_one_with_name(hapic_data.body.profile)]
  223. uapi.update(
  224. user=request.candidate_user,
  225. groups=groups,
  226. do_save=True,
  227. )
  228. return
  229. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  230. @require_same_user_or_profile(Group.TIM_ADMIN)
  231. @hapic.input_path(UserWorkspaceIdPathSchema())
  232. @hapic.input_query(ActiveContentFilterQuerySchema())
  233. @hapic.output_body(ContentDigestSchema(many=True))
  234. def last_active_content(self, context, request: TracimRequest, hapic_data=None): # nopep8
  235. """
  236. Get last_active_content for user
  237. """
  238. app_config = request.registry.settings['CFG']
  239. content_filter = hapic_data.query
  240. api = ContentApi(
  241. current_user=request.candidate_user, # User
  242. session=request.dbsession,
  243. config=app_config,
  244. )
  245. wapi = WorkspaceApi(
  246. current_user=request.candidate_user, # User
  247. session=request.dbsession,
  248. config=app_config,
  249. )
  250. workspace = None
  251. if hapic_data.path.workspace_id:
  252. workspace = wapi.get_one(hapic_data.path.workspace_id)
  253. last_actives = api.get_last_active(
  254. workspace=workspace,
  255. limit=content_filter.limit or None,
  256. before_datetime=content_filter.before_datetime or None,
  257. )
  258. return [
  259. api.get_content_in_context(content)
  260. for content in last_actives
  261. ]
  262. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  263. @require_same_user_or_profile(Group.TIM_ADMIN)
  264. @hapic.input_path(UserWorkspaceIdPathSchema())
  265. @hapic.input_query(ContentIdsQuerySchema(), as_list=['contents_ids'])
  266. @hapic.output_body(ReadStatusSchema(many=True)) # nopep8
  267. def contents_read_status(self, context, request: TracimRequest, hapic_data=None): # nopep8
  268. """
  269. get user_read status of contents
  270. """
  271. app_config = request.registry.settings['CFG']
  272. content_filter = hapic_data.query
  273. api = ContentApi(
  274. current_user=request.candidate_user, # User
  275. session=request.dbsession,
  276. config=app_config,
  277. )
  278. wapi = WorkspaceApi(
  279. current_user=request.candidate_user, # User
  280. session=request.dbsession,
  281. config=app_config,
  282. )
  283. workspace = None
  284. if hapic_data.path.workspace_id:
  285. workspace = wapi.get_one(hapic_data.path.workspace_id)
  286. last_actives = api.get_last_active(
  287. workspace=workspace,
  288. limit=None,
  289. before_datetime=None,
  290. content_ids=hapic_data.query.contents_ids or None
  291. )
  292. return [
  293. api.get_content_in_context(content)
  294. for content in last_actives
  295. ]
  296. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  297. @require_same_user_or_profile(Group.TIM_ADMIN)
  298. @hapic.input_path(UserWorkspaceAndContentIdPathSchema())
  299. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  300. def set_content_as_read(self, context, request: TracimRequest, hapic_data=None): # nopep8
  301. """
  302. set user_read status of content to read
  303. """
  304. app_config = request.registry.settings['CFG']
  305. api = ContentApi(
  306. current_user=request.candidate_user,
  307. session=request.dbsession,
  308. config=app_config,
  309. )
  310. api.mark_read(request.current_content, do_flush=True)
  311. return
  312. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  313. @require_same_user_or_profile(Group.TIM_ADMIN)
  314. @hapic.input_path(UserWorkspaceAndContentIdPathSchema())
  315. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  316. def set_content_as_unread(self, context, request: TracimRequest, hapic_data=None): # nopep8
  317. """
  318. set user_read status of content to unread
  319. """
  320. app_config = request.registry.settings['CFG']
  321. api = ContentApi(
  322. current_user=request.candidate_user,
  323. session=request.dbsession,
  324. config=app_config,
  325. )
  326. api.mark_unread(request.current_content, do_flush=True)
  327. return
  328. @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
  329. @require_same_user_or_profile(Group.TIM_ADMIN)
  330. @hapic.input_path(UserWorkspaceIdPathSchema())
  331. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  332. def set_workspace_as_read(self, context, request: TracimRequest, hapic_data=None): # nopep8
  333. """
  334. set user_read status of all content of workspace to read
  335. """
  336. app_config = request.registry.settings['CFG']
  337. api = ContentApi(
  338. current_user=request.candidate_user,
  339. session=request.dbsession,
  340. config=app_config,
  341. )
  342. api.mark_read__workspace(request.current_workspace)
  343. return
  344. def bind(self, configurator: Configurator) -> None:
  345. """
  346. Create all routes and views using pyramid configurator
  347. for this controller
  348. """
  349. # user workspace
  350. configurator.add_route('user_workspace', '/users/{user_id}/workspaces', request_method='GET') # nopep8
  351. configurator.add_view(self.user_workspace, route_name='user_workspace')
  352. # user info
  353. configurator.add_route('user', '/users/{user_id}', request_method='GET') # nopep8
  354. configurator.add_view(self.user, route_name='user')
  355. # set user email
  356. configurator.add_route('set_user_email', '/users/{user_id}/email', request_method='PUT') # nopep8
  357. configurator.add_view(self.set_user_email, route_name='set_user_email')
  358. # set user password
  359. configurator.add_route('set_user_password', '/users/{user_id}/password', request_method='PUT') # nopep8
  360. configurator.add_view(self.set_user_password, route_name='set_user_password') # nopep8
  361. # set user_info
  362. configurator.add_route('set_user_info', '/users/{user_id}', request_method='PUT') # nopep8
  363. configurator.add_view(self.set_user_infos, route_name='set_user_info')
  364. # create user
  365. configurator.add_route('create_user', '/users', request_method='POST')
  366. configurator.add_view(self.create_user, route_name='create_user')
  367. # enable user
  368. configurator.add_route('enable_user', '/users/{user_id}/enable', request_method='PUT') # nopep8
  369. configurator.add_view(self.enable_user, route_name='enable_user')
  370. # disable user
  371. configurator.add_route('disable_user', '/users/{user_id}/disable', request_method='PUT') # nopep8
  372. configurator.add_view(self.disable_user, route_name='disable_user')
  373. # set user profile
  374. configurator.add_route('set_user_profile', '/users/{user_id}/profile', request_method='PUT') # nopep8
  375. configurator.add_view(self.set_profile, route_name='set_user_profile')
  376. # user content
  377. configurator.add_route('contents_read_status', '/users/{user_id}/workspaces/{workspace_id}/contents/read_status', request_method='GET') # nopep8
  378. configurator.add_view(self.contents_read_status, route_name='contents_read_status') # nopep8
  379. # last active content for user
  380. configurator.add_route('last_active_content', '/users/{user_id}/workspaces/{workspace_id}/contents/recently_active', request_method='GET') # nopep8
  381. configurator.add_view(self.last_active_content, route_name='last_active_content') # nopep8
  382. # set content as read/unread
  383. configurator.add_route('read_content', '/users/{user_id}/workspaces/{workspace_id}/contents/{content_id}/read', request_method='PUT') # nopep8
  384. configurator.add_view(self.set_content_as_read, route_name='read_content') # nopep8
  385. configurator.add_route('unread_content', '/users/{user_id}/workspaces/{workspace_id}/contents/{content_id}/unread', request_method='PUT') # nopep8
  386. configurator.add_view(self.set_content_as_unread, route_name='unread_content') # nopep8
  387. # set workspace as read
  388. configurator.add_route('read_workspace', '/users/{user_id}/workspaces/{workspace_id}/read', request_method='PUT') # nopep8
  389. configurator.add_view(self.set_workspace_as_read, route_name='read_workspace') # nopep8