authorization.py 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. # -*- coding: utf-8 -*-
  2. import typing
  3. from typing import TYPE_CHECKING
  4. import functools
  5. from pyramid.interfaces import IAuthorizationPolicy
  6. from zope.interface import implementer
  7. from tracim.models.contents import NewContentType
  8. try:
  9. from json.decoder import JSONDecodeError
  10. except ImportError: # python3.4
  11. JSONDecodeError = ValueError
  12. from tracim.exceptions import InsufficientUserWorkspaceRole
  13. from tracim.exceptions import ContentTypeNotAllowed
  14. from tracim.exceptions import InsufficientUserProfile
  15. if TYPE_CHECKING:
  16. from tracim import TracimRequest
  17. ###
  18. # Pyramid default permission/authorization mecanism
  19. # INFO - G.M - 12-04-2018 - Setiing a Default permission on view is
  20. # needed to activate AuthentificationPolicy and
  21. # AuthorizationPolicy on pyramid request
  22. TRACIM_DEFAULT_PERM = 'tracim'
  23. @implementer(IAuthorizationPolicy)
  24. class AcceptAllAuthorizationPolicy(object):
  25. """
  26. Empty AuthorizationPolicy : Allow all request. As Pyramid need
  27. a Authorization policy when we use AuthentificationPolicy, this
  28. class permit use to disable pyramid authorization mecanism with
  29. working a AuthentificationPolicy.
  30. """
  31. def permits(self, context, principals, permision):
  32. return True
  33. def principals_allowed_by_permission(self, context, permission):
  34. raise NotImplementedError()
  35. ###
  36. # Authorization decorators for views
  37. # INFO - G.M - 12-04-2018
  38. # Instead of relying on pyramid authorization mecanism
  39. # We prefer to use decorators
  40. def require_same_user_or_profile(group: int):
  41. """
  42. Decorator for view to restrict access of tracim request if candidate user
  43. is distinct from authenticated user and not with high enough profile.
  44. :param group: value from Group Object
  45. like Group.TIM_USER or Group.TIM_MANAGER
  46. :return:
  47. """
  48. def decorator(func):
  49. @functools.wraps(func)
  50. def wrapper(self, context, request: 'TracimRequest'):
  51. auth_user = request.current_user
  52. candidate_user = request.candidate_user
  53. if auth_user.user_id == candidate_user.user_id or \
  54. auth_user.profile.id >= group:
  55. return func(self, context, request)
  56. raise InsufficientUserProfile()
  57. return wrapper
  58. return decorator
  59. def require_profile(group: int):
  60. """
  61. Decorator for view to restrict access of tracim request if profile is
  62. not high enough
  63. :param group: value from Group Object
  64. like Group.TIM_USER or Group.TIM_MANAGER
  65. :return:
  66. """
  67. def decorator(func):
  68. @functools.wraps(func)
  69. def wrapper(self, context, request: 'TracimRequest'):
  70. user = request.current_user
  71. if user.profile.id >= group:
  72. return func(self, context, request)
  73. raise InsufficientUserProfile()
  74. return wrapper
  75. return decorator
  76. def require_workspace_role(minimal_required_role: int):
  77. """
  78. Restricts access to endpoint to minimal role or raise an exception.
  79. Check role for current_workspace.
  80. :param minimal_required_role: value from UserInWorkspace Object like
  81. UserRoleInWorkspace.CONTRIBUTOR or UserRoleInWorkspace.READER
  82. :return: decorator
  83. """
  84. def decorator(func):
  85. @functools.wraps(func)
  86. def wrapper(self, context, request: 'TracimRequest'):
  87. user = request.current_user
  88. workspace = request.current_workspace
  89. if workspace.get_user_role(user) >= minimal_required_role:
  90. return func(self, context, request)
  91. raise InsufficientUserWorkspaceRole()
  92. return wrapper
  93. return decorator
  94. def require_candidate_workspace_role(minimal_required_role: int):
  95. """
  96. Restricts access to endpoint to minimal role or raise an exception.
  97. Check role for candidate_workspace.
  98. :param minimal_required_role: value from UserInWorkspace Object like
  99. UserRoleInWorkspace.CONTRIBUTOR or UserRoleInWorkspace.READER
  100. :return: decorator
  101. """
  102. def decorator(func):
  103. def wrapper(self, context, request: 'TracimRequest'):
  104. user = request.current_user
  105. workspace = request.candidate_workspace
  106. if workspace.get_user_role(user) >= minimal_required_role:
  107. return func(self, context, request)
  108. raise InsufficientUserWorkspaceRole()
  109. return wrapper
  110. return decorator
  111. def require_content_types(content_types: typing.List['NewContentType']):
  112. """
  113. Restricts access to specific file type or raise an exception.
  114. Check role for candidate_workspace.
  115. :param content_types: list of NewContentType object
  116. :return: decorator
  117. """
  118. def decorator(func):
  119. @functools.wraps(func)
  120. def wrapper(self, context, request: 'TracimRequest'):
  121. content = request.current_content
  122. if content.type in [content.slug for content in content_types]:
  123. return func(self, context, request)
  124. raise ContentTypeNotAllowed()
  125. return wrapper
  126. return decorator
  127. def require_comment_ownership_or_role(
  128. minimal_required_role_for_owner: int,
  129. minimal_required_role_for_anyone: int,
  130. ) -> None:
  131. """
  132. Decorator for view to restrict access of tracim request if role is
  133. not high enough and user is not owner of the current_content
  134. :param minimal_required_role_for_owner_access: minimal role for owner
  135. of current_content to access to this view
  136. :param minimal_required_role_for_anyone: minimal role for anyone to
  137. access to this view.
  138. :return:
  139. """
  140. def decorator(func):
  141. @functools.wraps(func)
  142. def wrapper(self, context, request: 'TracimRequest'):
  143. user = request.current_user
  144. workspace = request.current_workspace
  145. comment = request.current_comment
  146. # INFO - G.M - 2018-06-178 - find minimal role required
  147. if comment.owner.user_id == user.user_id:
  148. minimal_required_role = minimal_required_role_for_owner
  149. else:
  150. minimal_required_role = minimal_required_role_for_anyone
  151. # INFO - G.M - 2018-06-178 - normal role test
  152. if workspace.get_user_role(user) >= minimal_required_role:
  153. return func(self, context, request)
  154. raise InsufficientUserWorkspaceRole()
  155. return wrapper
  156. return decorator