Browse Source

fix access for deleting comments + other access troube

Guénaël Muller 6 years ago
parent
commit
adfa1785e8

+ 16 - 1
tracim/fixtures/content.py View File

@@ -23,6 +23,10 @@ class Content(Fixture):
23 23
         bob = self._session.query(models.User) \
24 24
             .filter(models.User.email == 'bob@fsf.local') \
25 25
             .one()
26
+        john_the_reader = self._session.query(models.User) \
27
+            .filter(models.User.email == 'john-the-reader@reader.local') \
28
+            .one()
29
+
26 30
         admin_workspace_api = WorkspaceApi(
27 31
             current_user=admin,
28 32
             session=self._session,
@@ -43,6 +47,11 @@ class Content(Fixture):
43 47
             session=self._session,
44 48
             config=self._config
45 49
         )
50
+        reader_content_api = ContentApi(
51
+            current_user=john_the_reader,
52
+            session=self._session,
53
+            config=self._config
54
+        )
46 55
         role_api = RoleApi(
47 56
             current_user=admin,
48 57
             session=self._session,
@@ -73,6 +82,12 @@ class Content(Fixture):
73 82
             role_level=UserRoleInWorkspace.CONTENT_MANAGER,
74 83
             with_notif=False,
75 84
         )
85
+        role_api.create_one(
86
+            user=john_the_reader,
87
+            workspace=recipe_workspace,
88
+            role_level=UserRoleInWorkspace.READER,
89
+            with_notif=False,
90
+        )
76 91
         # Folders
77 92
 
78 93
         tool_workspace = content_api.create(
@@ -273,7 +288,7 @@ class Content(Fixture):
273 288
             content='<p>What about Apple Pie ? There are Awesome !</p>',
274 289
             do_save=True,
275 290
         )
276
-        content_api.create_comment(
291
+        reader_content_api.create_comment(
277 292
             parent=best_cake_thread,
278 293
             content='<p>You are right, but Kouign-amann are clearly better.</p>',
279 294
             do_save=True,

+ 9 - 0
tracim/fixtures/users_and_groups.py View File

@@ -61,3 +61,12 @@ class Test(Fixture):
61 61
         bob.password = 'foobarbaz'
62 62
         self._session.add(bob)
63 63
         g2.users.append(bob)
64
+
65
+        g2 = self._session.query(models.Group).\
66
+            filter(models.Group.group_name == 'users').one()
67
+        lawrence = models.User()
68
+        lawrence.display_name = 'John Reader'
69
+        lawrence.email = 'john-the-reader@reader.local'
70
+        lawrence.password = 'read'
71
+        self._session.add(lawrence)
72
+        g2.users.append(lawrence)

+ 32 - 0
tracim/lib/utils/authorization.py View File

@@ -146,3 +146,35 @@ def require_content_types(content_types: typing.List['NewContentType']):
146 146
             raise ContentTypeNotAllowed()
147 147
         return wrapper
148 148
     return decorator
149
+
150
+
151
+def require_comment_ownership_or_role(
152
+        minimal_required_role_for_owner: int,
153
+        minimal_required_role_for_anyone: int,
154
+) -> None:
155
+    """
156
+    Decorator for view to restrict access of tracim request if role is
157
+    not high enough and user is not owner of the current_content
158
+    :param minimal_required_role_for_owner_access: minimal role for owner
159
+    of current_content to access to this view
160
+    :param minimal_required_role_for_anyone: minimal role for anyone to
161
+    access to this view.
162
+    :return:
163
+    """
164
+    def decorator(func):
165
+        @functools.wraps(func)
166
+        def wrapper(self, context, request: 'TracimRequest'):
167
+            user = request.current_user
168
+            workspace = request.current_workspace
169
+            comment = request.current_comment
170
+            # INFO - G.M - 2018-06-178 - find minimal role required
171
+            if comment.owner.user_id == user.user_id:
172
+                minimal_required_role = minimal_required_role_for_owner
173
+            else:
174
+                minimal_required_role = minimal_required_role_for_anyone
175
+            # INFO - G.M - 2018-06-178 - normal role test
176
+            if workspace.get_user_role(user) >= minimal_required_role:
177
+                return func(self, context, request)
178
+            raise InsufficientUserWorkspaceRole()
179
+        return wrapper
180
+    return decorator

+ 62 - 0
tracim/lib/utils/request.py View File

@@ -38,6 +38,9 @@ class TracimRequest(Request):
38 38
             decode_param_names,
39 39
             **kw
40 40
         )
41
+        # Current comment, found in request path
42
+        self._current_comment = None  # type: Content
43
+
41 44
         # Current content, found in request path
42 45
         self._current_content = None  # type: Content
43 46
 
@@ -120,6 +123,27 @@ class TracimRequest(Request):
120 123
             )
121 124
         self._current_content = content
122 125
 
126
+    @property
127
+    def current_comment(self) -> User:
128
+        """
129
+        Get current comment from path
130
+        """
131
+        if self._current_comment is None:
132
+            self._current_comment = self._get_current_comment(
133
+                self.current_user,
134
+                self.current_workspace,
135
+                self.current_content,
136
+                self
137
+                )
138
+        return self._current_comment
139
+
140
+    @current_comment.setter
141
+    def current_comment(self, content: Content) -> None:
142
+        if self._current_comment is not None:
143
+            raise ImmutableAttribute(
144
+                "Can't modify already setted current_content"
145
+            )
146
+        self._current_comment = content
123 147
     # TODO - G.M - 24-05-2018 - Find a better naming for this ?
124 148
     @property
125 149
     def candidate_user(self) -> User:
@@ -170,6 +194,44 @@ class TracimRequest(Request):
170 194
     ###
171 195
     # Utils for TracimRequest
172 196
     ###
197
+    def _get_current_comment(
198
+            self,
199
+            user: User,
200
+            workspace: Workspace,
201
+            content: Content,
202
+            request: 'TracimRequest'
203
+    ):
204
+        """
205
+        Get current content from request
206
+        :param user: User who want to check the workspace
207
+        :param request: pyramid request
208
+        :return: current content
209
+        """
210
+        comment_id = ''
211
+        try:
212
+            if 'comment_id' in request.matchdict:
213
+                comment_id = int(request.matchdict['comment_id'])
214
+            if not comment_id:
215
+                raise ContentNotFoundInTracimRequest('No comment_id property found in request')  # nopep8
216
+            api = ContentApi(
217
+                current_user=user,
218
+                session=request.dbsession,
219
+                config=request.registry.settings['CFG']
220
+            )
221
+            comment = api.get_one(
222
+                comment_id,
223
+                content_type=ContentType.Comment,
224
+                workspace=workspace,
225
+                parent=content,
226
+            )
227
+        except JSONDecodeError:
228
+            raise ContentNotFound('Bad json body')
229
+        except NoResultFound:
230
+            raise ContentNotFound(
231
+                'Comment {} does not exist '
232
+                'or is not visible for this user'.format(comment_id)
233
+            )
234
+        return comment
173 235
 
174 236
     def _get_current_content(
175 237
             self,

+ 155 - 9
tracim/tests/functional/test_comments.py View File

@@ -50,10 +50,10 @@ class TestCommentsEndpoint(FunctionalTest):
50 50
         assert comment['parent_id'] == 7
51 51
         assert comment['raw_content'] == '<p>You are right, but Kouign-amann are clearly better.</p>'  # nopep8
52 52
         assert comment['author']
53
-        assert comment['author']['user_id'] == 1
53
+        assert comment['author']['user_id'] == 4
54 54
         # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
55 55
         assert comment['author']['avatar_url'] == None
56
-        assert comment['author']['public_name'] == 'Global manager'
56
+        assert comment['author']['public_name'] == 'John Reader'
57 57
 
58 58
     def test_api__post_content_comment__ok_200__nominal_case(self) -> None:
59 59
         """
@@ -88,9 +88,9 @@ class TestCommentsEndpoint(FunctionalTest):
88 88
         assert len(res.json_body) == 4
89 89
         assert comment == res.json_body[3]
90 90
 
91
-    def test_api__delete_content_comment__ok_200__nominal_case(self) -> None:
91
+    def test_api__delete_content_comment__ok_200__workspace_manager_owner(self) -> None:
92 92
         """
93
-        Get alls comments of a content
93
+        delete comment (user is workspace_manager and owner)
94 94
         """
95 95
         self.testapp.authorization = (
96 96
             'Basic',
@@ -101,10 +101,10 @@ class TestCommentsEndpoint(FunctionalTest):
101 101
         )
102 102
         res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
103 103
         assert len(res.json_body) == 3
104
-        comment = res.json_body[2]
105
-        assert comment['content_id'] == 20
104
+        comment = res.json_body[0]
105
+        assert comment['content_id'] == 18
106 106
         assert comment['parent_id'] == 7
107
-        assert comment['raw_content'] == '<p>You are right, but Kouign-amann are clearly better.</p>'   # nopep8
107
+        assert comment['raw_content'] == '<p> What is for you the best cake ever ? </br> I personnally vote for Chocolate cupcake !</p>'   # nopep8
108 108
         assert comment['author']
109 109
         assert comment['author']['user_id'] == 1
110 110
         # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
@@ -112,9 +112,155 @@ class TestCommentsEndpoint(FunctionalTest):
112 112
         assert comment['author']['public_name'] == 'Global manager'
113 113
 
114 114
         res = self.testapp.delete(
115
-            '/api/v2/workspaces/2/contents/7/comments/20',
115
+            '/api/v2/workspaces/2/contents/7/comments/18',
116
+            status=204
117
+        )
118
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
119
+        assert len(res.json_body) == 2
120
+        assert not [content for content in res.json_body if content['content_id'] == 18]  # nopep8
121
+
122
+    def test_api__delete_content_comment__ok_200__workspace_manager(self) -> None:
123
+        """
124
+        delete comment (user is workspace_manager)
125
+        """
126
+        self.testapp.authorization = (
127
+            'Basic',
128
+            (
129
+                'admin@admin.admin',
130
+                'admin@admin.admin'
131
+            )
132
+        )
133
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
134
+        assert len(res.json_body) == 3
135
+        comment = res.json_body[1]
136
+        assert comment['content_id'] == 19
137
+        assert comment['parent_id'] == 7
138
+        assert comment['raw_content'] == '<p>What about Apple Pie ? There are Awesome !</p>'   # nopep8
139
+        assert comment['author']
140
+        assert comment['author']['user_id'] == 3
141
+        # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
142
+        assert comment['author']['avatar_url'] is None
143
+        assert comment['author']['public_name'] == 'Bob i.'
144
+
145
+        res = self.testapp.delete(
146
+            '/api/v2/workspaces/2/contents/7/comments/19',
116 147
             status=204
117 148
         )
118 149
         res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
119 150
         assert len(res.json_body) == 2
120
-        assert not [content for content in res.json_body if content['content_id'] == 20]  # nopep8
151
+        assert not [content for content in res.json_body if content['content_id'] == 19]  # nopep8
152
+
153
+    def test_api__delete_content_comment__ok_200__content_manager_owner(self) -> None:
154
+        """
155
+        delete comment (user is content-manager and owner)
156
+        """
157
+        self.testapp.authorization = (
158
+            'Basic',
159
+            (
160
+                'admin@admin.admin',
161
+                'admin@admin.admin'
162
+            )
163
+        )
164
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
165
+        assert len(res.json_body) == 3
166
+        comment = res.json_body[1]
167
+        assert comment['content_id'] == 19
168
+        assert comment['parent_id'] == 7
169
+        assert comment['raw_content'] == '<p>What about Apple Pie ? There are Awesome !</p>'   # nopep8
170
+        assert comment['author']
171
+        assert comment['author']['user_id'] == 3
172
+        # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
173
+        assert comment['author']['avatar_url'] is None
174
+        assert comment['author']['public_name'] == 'Bob i.'
175
+
176
+        res = self.testapp.delete(
177
+            '/api/v2/workspaces/2/contents/7/comments/19',
178
+            status=204
179
+        )
180
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
181
+        assert len(res.json_body) == 2
182
+        assert not [content for content in res.json_body if content['content_id'] == 19]  # nopep8
183
+
184
+    def test_api__delete_content_comment__err_403__content_manager(self) -> None:
185
+        """
186
+        delete comment (user is content-manager)
187
+        """
188
+        self.testapp.authorization = (
189
+            'Basic',
190
+            (
191
+                'bob@fsf.local',
192
+                'foobarbaz'
193
+            )
194
+        )
195
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
196
+        assert len(res.json_body) == 3
197
+        comment = res.json_body[2]
198
+        assert comment['content_id'] == 20
199
+        assert comment['parent_id'] == 7
200
+        assert comment['raw_content'] == '<p>You are right, but Kouign-amann are clearly better.</p>'   # nopep8
201
+        assert comment['author']
202
+        assert comment['author']['user_id'] == 4
203
+        # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
204
+        assert comment['author']['avatar_url'] is None
205
+        assert comment['author']['public_name'] == 'John Reader'
206
+
207
+        res = self.testapp.delete(
208
+            '/api/v2/workspaces/2/contents/7/comments/20',
209
+            status=403
210
+        )
211
+
212
+    def test_api__delete_content_comment__err_403__reader_owner(self) -> None:
213
+        """
214
+        delete comment (user is reader and owner)
215
+        """
216
+        self.testapp.authorization = (
217
+            'Basic',
218
+            (
219
+                'bob@fsf.local',
220
+                'foobarbaz'
221
+            )
222
+        )
223
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
224
+        assert len(res.json_body) == 3
225
+        comment = res.json_body[2]
226
+        assert comment['content_id'] == 20
227
+        assert comment['parent_id'] == 7
228
+        assert comment['raw_content'] == '<p>You are right, but Kouign-amann are clearly better.</p>'   # nopep8
229
+        assert comment['author']
230
+        assert comment['author']['user_id'] == 4
231
+        # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
232
+        assert comment['author']['avatar_url'] is None
233
+        assert comment['author']['public_name'] == 'John Reader'
234
+
235
+        res = self.testapp.delete(
236
+            '/api/v2/workspaces/2/contents/7/comments/20',
237
+            status=403
238
+        )
239
+
240
+    def test_api__delete_content_comment__err_403__reader(self) -> None:
241
+        """
242
+        delete comment (user is reader)
243
+        """
244
+        self.testapp.authorization = (
245
+            'Basic',
246
+            (
247
+                'bob@fsf.local',
248
+                'foobarbaz'
249
+            )
250
+        )
251
+        res = self.testapp.get('/api/v2/workspaces/2/contents/7/comments', status=200)
252
+        assert len(res.json_body) == 3
253
+        comment = res.json_body[2]
254
+        assert comment['content_id'] == 20
255
+        assert comment['parent_id'] == 7
256
+        assert comment['raw_content'] == '<p>You are right, but Kouign-amann are clearly better.</p>'   # nopep8
257
+        assert comment['author']
258
+        assert comment['author']['user_id'] == 4
259
+        # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
260
+        assert comment['author']['avatar_url'] is None
261
+        assert comment['author']['public_name'] == 'John Reader'
262
+
263
+        res = self.testapp.delete(
264
+            '/api/v2/workspaces/2/contents/7/comments/20',
265
+            status=403
266
+        )

+ 14 - 5
tracim/views/contents_api/comment_controller.py View File

@@ -1,6 +1,7 @@
1 1
 # coding=utf-8
2 2
 import transaction
3 3
 from pyramid.config import Configurator
4
+
4 5
 try:  # Python 3.5+
5 6
     from http import HTTPStatus
6 7
 except ImportError:
@@ -10,6 +11,8 @@ from tracim import TracimRequest
10 11
 from tracim.extensions import hapic
11 12
 from tracim.lib.core.content import ContentApi
12 13
 from tracim.lib.core.workspace import WorkspaceApi
14
+from tracim.lib.utils.authorization import require_workspace_role
15
+from tracim.lib.utils.authorization import require_comment_ownership_or_role
13 16
 from tracim.views.controllers import Controller
14 17
 from tracim.views.core_api.schemas import CommentSchema
15 18
 from tracim.views.core_api.schemas import CommentsPathSchema
@@ -17,23 +20,24 @@ from tracim.views.core_api.schemas import SetCommentSchema
17 20
 from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
18 21
 from tracim.views.core_api.schemas import NoContentSchema
19 22
 from tracim.exceptions import WorkspaceNotFound
20
-from tracim.exceptions import InsufficientUserProfile
23
+from tracim.exceptions import InsufficientUserWorkspaceRole
21 24
 from tracim.exceptions import NotAuthenticated
22 25
 from tracim.exceptions import AuthenticationFailed
23 26
 from tracim.models.contents import ContentTypeLegacy as ContentType
24 27
 from tracim.models.revision_protection import new_revision
28
+from tracim.models.data import UserRoleInWorkspace
25 29
 
26 30
 COMMENT_ENDPOINTS_TAG = 'Comments'
27 31
 
28 32
 
29 33
 class CommentController(Controller):
30
-    pass
31 34
 
32 35
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
33 36
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
34
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
37
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
35 38
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
36 39
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
40
+    @require_workspace_role(UserRoleInWorkspace.READER)
37 41
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
38 42
     @hapic.output_body(CommentSchema(many=True),)
39 43
     def content_comments(self, context, request: TracimRequest, hapic_data=None):
@@ -60,9 +64,10 @@ class CommentController(Controller):
60 64
 
61 65
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
62 66
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
63
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
67
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
64 68
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
65 69
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
70
+    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
66 71
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
67 72
     @hapic.input_body(SetCommentSchema())
68 73
     @hapic.output_body(CommentSchema(),)
@@ -91,9 +96,13 @@ class CommentController(Controller):
91 96
 
92 97
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
93 98
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
94
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
99
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
95 100
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
96 101
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
102
+    @require_comment_ownership_or_role(
103
+        minimal_required_role_for_anyone=UserRoleInWorkspace.WORKSPACE_MANAGER,
104
+        minimal_required_role_for_owner=UserRoleInWorkspace.CONTRIBUTOR,
105
+    )
97 106
     @hapic.input_path(CommentsPathSchema())
98 107
     @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
99 108
     def delete_comment(self, context, request: TracimRequest, hapic_data=None):

+ 5 - 5
tracim/views/contents_api/html_document_controller.py View File

@@ -22,7 +22,7 @@ from tracim.views.core_api.schemas import NoContentSchema
22 22
 from tracim.lib.utils.authorization import require_content_types
23 23
 from tracim.lib.utils.authorization import require_workspace_role
24 24
 from tracim.exceptions import WorkspaceNotFound, ContentTypeNotAllowed
25
-from tracim.exceptions import InsufficientUserProfile
25
+from tracim.exceptions import InsufficientUserWorkspaceRole
26 26
 from tracim.exceptions import NotAuthenticated
27 27
 from tracim.exceptions import AuthenticationFailed
28 28
 from tracim.models.contents import ContentTypeLegacy as ContentType
@@ -36,7 +36,7 @@ class HTMLDocumentController(Controller):
36 36
 
37 37
     @hapic.with_api_doc(tags=[HTML_DOCUMENT_ENDPOINTS_TAG])
38 38
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
39
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
39
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
40 40
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
41 41
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
42 42
     @hapic.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
@@ -62,7 +62,7 @@ class HTMLDocumentController(Controller):
62 62
 
63 63
     @hapic.with_api_doc(tags=[HTML_DOCUMENT_ENDPOINTS_TAG])
64 64
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
65
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
65
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
66 66
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
67 67
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
68 68
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
@@ -100,7 +100,7 @@ class HTMLDocumentController(Controller):
100 100
 
101 101
     @hapic.with_api_doc(tags=[HTML_DOCUMENT_ENDPOINTS_TAG])
102 102
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
103
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
103
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
104 104
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
105 105
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
106 106
     @require_workspace_role(UserRoleInWorkspace.READER)
@@ -129,7 +129,7 @@ class HTMLDocumentController(Controller):
129 129
 
130 130
     @hapic.with_api_doc(tags=[HTML_DOCUMENT_ENDPOINTS_TAG])
131 131
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
132
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
132
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
133 133
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
134 134
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
135 135
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)

+ 5 - 5
tracim/views/contents_api/threads_controller.py View File

@@ -22,7 +22,7 @@ from tracim.views.core_api.schemas import NoContentSchema
22 22
 from tracim.lib.utils.authorization import require_content_types
23 23
 from tracim.lib.utils.authorization import require_workspace_role
24 24
 from tracim.exceptions import WorkspaceNotFound, ContentTypeNotAllowed
25
-from tracim.exceptions import InsufficientUserProfile
25
+from tracim.exceptions import InsufficientUserWorkspaceRole
26 26
 from tracim.exceptions import NotAuthenticated
27 27
 from tracim.exceptions import AuthenticationFailed
28 28
 from tracim.models.contents import ContentTypeLegacy as ContentType
@@ -36,7 +36,7 @@ class ThreadController(Controller):
36 36
 
37 37
     @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
38 38
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
39
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
39
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
40 40
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
41 41
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
42 42
     @hapic.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
@@ -62,7 +62,7 @@ class ThreadController(Controller):
62 62
 
63 63
     @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
64 64
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
65
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
65
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
66 66
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
67 67
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
68 68
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
@@ -100,7 +100,7 @@ class ThreadController(Controller):
100 100
 
101 101
     @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
102 102
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
103
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
103
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
104 104
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
105 105
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
106 106
     @require_workspace_role(UserRoleInWorkspace.READER)
@@ -129,7 +129,7 @@ class ThreadController(Controller):
129 129
 
130 130
     @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
131 131
     @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
132
-    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
132
+    @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
133 133
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
134 134
     @hapic.handle_exception(AuthenticationFailed, HTTPStatus.BAD_REQUEST)
135 135
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)