Skip to content
Snippets Groups Projects
Commit 5d3624a1 authored by Valentin Rigal's avatar Valentin Rigal Committed by ml bonhomme
Browse files

Restrict ML classes edition to project admins

parent 32b8a4a8
No related branches found
No related tags found
1 merge request!2308Restrict ML classes edition to project admins
import uuid import uuid
from textwrap import dedent
from django.db import transaction from django.db import transaction
from django.utils.functional import cached_property from django.utils.functional import cached_property
...@@ -300,16 +301,22 @@ class CorpusMLClassPagination(PageNumberPagination): ...@@ -300,16 +301,22 @@ class CorpusMLClassPagination(PageNumberPagination):
@extend_schema_view( @extend_schema_view(
get=extend_schema( get=extend_schema(
operation_id="ListCorpusMLClasses", operation_id="ListCorpusMLClasses",
description=dedent("""
List available classes in a corpus.
Requires a **guest** access to the corpus.
"""),
), ),
post=extend_schema( post=extend_schema(
operation_id="CreateMLClass", operation_id="CreateMLClass",
description="Create an ML class in a corpus", description=dedent("""
Create an ML class in a corpus.
Requires an **admin** access to the corpus.
"""),
) )
) )
class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView): class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView):
"""
List available classes in a corpus
"""
serializer_class = MLClassSerializer serializer_class = MLClassSerializer
pagination_class = CorpusMLClassPagination pagination_class = CorpusMLClassPagination
# For OpenAPI type discovery: a corpus ID is in the path # For OpenAPI type discovery: a corpus ID is in the path
...@@ -322,7 +329,7 @@ class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView): ...@@ -322,7 +329,7 @@ class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView):
def corpus(self): def corpus(self):
role = Role.Guest role = Role.Guest
if self.request.method == "POST": if self.request.method == "POST":
role = Role.Contributor role = Role.Admin
return self.get_corpus(self.kwargs["pk"], role=role) return self.get_corpus(self.kwargs["pk"], role=role)
def check_permissions(self, *args, **kwargs): def check_permissions(self, *args, **kwargs):
...@@ -357,10 +364,26 @@ class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView): ...@@ -357,10 +364,26 @@ class CorpusMLClassList(CorpusACLMixin, ListCreateAPIView):
@extend_schema(tags=["classifications"]) @extend_schema(tags=["classifications"])
@extend_schema_view( @extend_schema_view(
get=extend_schema(description="Retrieve a ML class."), get=extend_schema(description=dedent("""
patch=extend_schema(description="Rename a ML class."), Retrieve an ML class.
put=extend_schema(description="Rename a ML class."),
delete=extend_schema(description="Delete a ML class if it is not used by any classification."), Requires a **guest** access to the corpus.
""")),
patch=extend_schema(description=dedent("""
Rename an ML class.
Requires an **admin** access to the corpus.
""")),
put=extend_schema(description=dedent("""
Rename an ML class.
Requires an **admin** access to the corpus.
""")),
delete=extend_schema(description=dedent("""
Delete an ML class if it is not used by any classification.
Requires an **admin** access to the corpus.
""")),
) )
class MLClassRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView): class MLClassRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
serializer_class = MLClassSerializer serializer_class = MLClassSerializer
...@@ -372,7 +395,7 @@ class MLClassRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView): ...@@ -372,7 +395,7 @@ class MLClassRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
def corpus(self): def corpus(self):
role = Role.Guest role = Role.Guest
if self.request and self.request.method != "GET": if self.request and self.request.method != "GET":
role = Role.Contributor role = Role.Admin
return self.get_corpus(self.kwargs["corpus"], role=role) return self.get_corpus(self.kwargs["corpus"], role=role)
......
...@@ -236,12 +236,12 @@ class TestClasses(FixtureAPITestCase): ...@@ -236,12 +236,12 @@ class TestClasses(FixtureAPITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@patch("arkindex.project.mixins.has_access", return_value=False) @patch("arkindex.project.mixins.has_access", return_value=False)
def test_update_requires_contributor(self, has_access_mock): def test_update_requires_admin(self, has_access_mock):
self.user.rights.update(level=Role.Guest.value) self.user.rights.update(level=Role.Contributor.value)
self.client.force_login(self.user) self.client.force_login(self.user)
response = self.client.put(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id}), {"name": "new name"}) response = self.client.put(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id}), {"name": "new name"})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertDictEqual(response.json(), {"detail": "You do not have contributor access to this corpus."}) self.assertDictEqual(response.json(), {"detail": "You do not have admin access to this corpus."})
def test_partial_update(self): def test_partial_update(self):
self.client.force_login(self.superuser) self.client.force_login(self.superuser)
...@@ -266,12 +266,12 @@ class TestClasses(FixtureAPITestCase): ...@@ -266,12 +266,12 @@ class TestClasses(FixtureAPITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@patch("arkindex.project.mixins.has_access", return_value=False) @patch("arkindex.project.mixins.has_access", return_value=False)
def test_partial_update_requires_contributor(self, has_access_mock): def test_partial_update_requires_admin(self, has_access_mock):
self.user.rights.update(level=Role.Guest.value) self.user.rights.update(level=Role.Contributor.value)
self.client.force_login(self.user) self.client.force_login(self.user)
response = self.client.patch(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id}), {"name": "new name"}) response = self.client.patch(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id}), {"name": "new name"})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertDictEqual(response.json(), {"detail": "You do not have contributor access to this corpus."}) self.assertDictEqual(response.json(), {"detail": "You do not have admin access to this corpus."})
def test_destroy(self): def test_destroy(self):
self.client.force_login(self.superuser) self.client.force_login(self.superuser)
...@@ -300,12 +300,12 @@ class TestClasses(FixtureAPITestCase): ...@@ -300,12 +300,12 @@ class TestClasses(FixtureAPITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@patch("arkindex.project.mixins.has_access", return_value=False) @patch("arkindex.project.mixins.has_access", return_value=False)
def test_destroy_requires_contributor(self, has_access_mock): def test_destroy_requires_admin(self, has_access_mock):
self.user.rights.update(level=Role.Guest.value) self.user.rights.update(level=Role.Contributor.value)
self.client.force_login(self.user) self.client.force_login(self.user)
response = self.client.delete(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id})) response = self.client.delete(reverse("api:ml-class-retrieve", kwargs={"corpus": self.corpus.id, "mlclass": self.text.id}))
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertDictEqual(response.json(), {"detail": "You do not have contributor access to this corpus."}) self.assertDictEqual(response.json(), {"detail": "You do not have admin access to this corpus."})
def test_list_elements_db_queries(self): def test_list_elements_db_queries(self):
with self.assertNumQueries(5): with self.assertNumQueries(5):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment