From c05733dba2dc19989c2d372acfac44e9ed752f24 Mon Sep 17 00:00:00 2001 From: Hironsan Date: Thu, 8 Jun 2023 19:54:51 +0900 Subject: [PATCH] Deny member to delete label types when allow_member_to_create_label_type is True --- backend/api/tests/utils.py | 7 ++- backend/label_types/tests/test_views.py | 55 ++++++++----------- backend/label_types/views.py | 2 +- .../project/projectApplicationService.ts | 13 +++-- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/backend/api/tests/utils.py b/backend/api/tests/utils.py index 1073ef0e..a52fd0e2 100644 --- a/backend/api/tests/utils.py +++ b/backend/api/tests/utils.py @@ -29,8 +29,11 @@ class CRUDMixin(APITestCase): self.assertEqual(response.status_code, expected) return response - def assert_delete(self, user=None, expected=status.HTTP_403_FORBIDDEN): + def assert_delete(self, user=None, expected=status.HTTP_403_FORBIDDEN, data=None): if user: self.client.force_login(user) - response = self.client.delete(self.url) + + if data is None: + data = {} + response = self.client.delete(self.url, data=data) self.assertEqual(response.status_code, expected) diff --git a/backend/label_types/tests/test_views.py b/backend/label_types/tests/test_views.py index ab1d36aa..45a2c22c 100644 --- a/backend/label_types/tests/test_views.py +++ b/backend/label_types/tests/test_views.py @@ -15,16 +15,18 @@ DATA_DIR = os.path.join(os.path.dirname(__file__), "data") class TestLabelList(CRUDMixin): - @classmethod - def setUpTestData(cls): - cls.non_member = make_user() - cls.project_a = prepare_project(ProjectType.DOCUMENT_CLASSIFICATION) - cls.label = make_label(cls.project_a.item) - cls.url = reverse(viewname="category_types", args=[cls.project_a.item.id]) + def setUp(self): + self.non_member = make_user() + self.project_a = prepare_project(ProjectType.DOCUMENT_CLASSIFICATION) + self.label = make_label(self.project_a.item) + self.url = reverse(viewname="category_types", args=[self.project_a.item.id]) # Ensure that the API does not return the labels of the other project. - cls.project_b = make_project(task="Any", users=["admin"], roles=[settings.ROLE_PROJECT_ADMIN]) - make_label(cls.project_b.item) + self.project_b = make_project(task="Any", users=["admin"], roles=[settings.ROLE_PROJECT_ADMIN]) + make_label(self.project_b.item) + + # for label creation + self.data = {"text": "example"} def test_returns_labels_to_project_member(self): for member in self.project_a.members: @@ -38,32 +40,11 @@ class TestLabelList(CRUDMixin): def test_does_not_return_labels_to_unauthenticated_user(self): self.assert_fetch(expected=status.HTTP_403_FORBIDDEN) - -class TestLabelSearch(CRUDMixin): - def setUp(self): - self.project = prepare_project(ProjectType.DOCUMENT_CLASSIFICATION) - make_label(self.project.item) - self.url = reverse(viewname="category_types", args=[self.project.item.id]) - - def test_search(self): - for member in self.project.members: - response = self.assert_fetch(member, status.HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - -class TestLabelCreate(CRUDMixin): - @classmethod - def setUpTestData(cls): - cls.non_member = make_user(ProjectType.DOCUMENT_CLASSIFICATION) - cls.project = prepare_project() - cls.url = reverse(viewname="category_types", args=[cls.project.item.id]) - cls.data = {"text": "example"} - def test_allows_admin_to_create_label(self): - self.assert_create(self.project.admin, status.HTTP_201_CREATED) + self.assert_create(self.project_a.admin, status.HTTP_201_CREATED) def test_denies_project_staff_to_create_label(self): - for member in self.project.staffs: + for member in self.project_a.staffs: self.assert_create(member, status.HTTP_403_FORBIDDEN) def test_denies_non_project_member_to_create_label(self): @@ -72,11 +53,19 @@ class TestLabelCreate(CRUDMixin): def test_denies_unauthenticated_user_to_create_label(self): self.assert_create(expected=status.HTTP_403_FORBIDDEN) + def test_allows_admin_to_bulk_delete_label(self): + self.assert_delete(self.project_a.admin, status.HTTP_204_NO_CONTENT, data={"ids": [self.label.id]}) + + def test_denies_project_staff_to_bulk_delete_label(self): + member = self.project_a.staffs[0] + self.assert_delete(member, status.HTTP_403_FORBIDDEN, data={"ids": [self.label.id]}) + class TestAllowMemberToCreateLabelType(CRUDMixin): @classmethod def setUpTestData(cls): cls.project = prepare_project(ProjectType.DOCUMENT_CLASSIFICATION, allow_member_to_create_label_type=True) + cls.label = make_label(cls.project.item) cls.url = reverse(viewname="category_types", args=[cls.project.item.id]) cls.data = {"text": "example"} @@ -85,6 +74,10 @@ class TestAllowMemberToCreateLabelType(CRUDMixin): self.data["text"] = member.username self.assert_create(member, status.HTTP_201_CREATED) + def test_denies_project_staff_to_bulk_delete_label(self): + member = self.project.staffs[0] + self.assert_delete(member, status.HTTP_403_FORBIDDEN, data={"ids": [self.label.id]}) + class TestLabelDetailAPI(CRUDMixin): @classmethod diff --git a/backend/label_types/views.py b/backend/label_types/views.py index 4293a1d4..a783d788 100644 --- a/backend/label_types/views.py +++ b/backend/label_types/views.py @@ -44,7 +44,7 @@ class LabelList(generics.ListCreateAPIView): def get_permissions(self): project = get_object_or_404(Project, pk=self.kwargs["project_id"]) - if project.allow_member_to_create_label_type: + if project.allow_member_to_create_label_type and self.request.method == "POST": self.permission_classes = [IsAuthenticated & IsProjectMember] else: self.permission_classes = [IsAuthenticated & (IsProjectAdmin | IsProjectStaffAndReadOnly)] diff --git a/frontend/services/application/project/projectApplicationService.ts b/frontend/services/application/project/projectApplicationService.ts index c6a2ecfb..154a90b4 100644 --- a/frontend/services/application/project/projectApplicationService.ts +++ b/frontend/services/application/project/projectApplicationService.ts @@ -15,6 +15,7 @@ type ProjectFields = { allowOverlappingSpans: boolean enableGraphemeMode: boolean useRelation: boolean + allowMemberToCreateLabelType: boolean } export interface SearchQueryData { @@ -52,7 +53,8 @@ export class ProjectApplicationService { enableGraphemeMode, useRelation, tags, - guideline = '' + guideline = '', + allowMemberToCreateLabelType = false }: ProjectFields): Promise { const project = Project.create( 0, @@ -66,7 +68,8 @@ export class ProjectApplicationService { allowOverlappingSpans, enableGraphemeMode, useRelation, - tags.map((tag) => TagItem.create(tag)) + tags.map((tag) => TagItem.create(tag)), + allowMemberToCreateLabelType ) try { return await this.repository.create(project) @@ -87,7 +90,8 @@ export class ProjectApplicationService { allowOverlappingSpans, enableGraphemeMode, useRelation, - guideline = '' + guideline = '', + allowMemberToCreateLabelType }: Omit ): Promise { const project = Project.create( @@ -102,7 +106,8 @@ export class ProjectApplicationService { allowOverlappingSpans, enableGraphemeMode, useRelation, - [] + [], + allowMemberToCreateLabelType ) try {