diff --git a/api/permissions/migrations/0010_add_manage_tags_permission.py b/api/permissions/migrations/0010_add_manage_tags_permission.py new file mode 100644 index 000000000000..c7ddfd24e538 --- /dev/null +++ b/api/permissions/migrations/0010_add_manage_tags_permission.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.15 on 2024-09-13 16:18 +from django.apps.registry import Apps +from django.db import migrations +from django.db.backends.base.schema import BaseDatabaseSchemaEditor + +from permissions.models import PROJECT_PERMISSION_TYPE +from projects.permissions import MANAGE_TAGS + + +def add_manage_tags_permission(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: + permission_model_class = apps.get_model("permissions", "permissionmodel") + permission_model_class.objects.get_or_create( + key=MANAGE_TAGS, + description="Allows the user to manage tags in the given project.", + type=PROJECT_PERMISSION_TYPE, + ) + + +def reverse(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: # pragma: no cover + permission_model_class = apps.get_model("permissions", "permissionmodel") + permission_model_class.objects.filter(key=MANAGE_TAGS).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ("permissions", "0009_move_view_audit_log_permission"), + ] + + operations = [ + migrations.RunPython(add_manage_tags_permission, reverse_code=reverse), + ] diff --git a/api/projects/permissions.py b/api/projects/permissions.py index a6fb98f042ea..1903c94a6b18 100644 --- a/api/projects/permissions.py +++ b/api/projects/permissions.py @@ -17,6 +17,7 @@ CREATE_FEATURE = "CREATE_FEATURE" EDIT_FEATURE = "EDIT_FEATURE" MANAGE_SEGMENTS = "MANAGE_SEGMENTS" +MANAGE_TAGS = "MANAGE_TAGS" TAG_SUPPORTED_PERMISSIONS = [DELETE_FEATURE] @@ -28,6 +29,7 @@ (EDIT_FEATURE, "Ability to edit features in the given project."), (MANAGE_SEGMENTS, "Ability to manage segments in the given project."), (VIEW_AUDIT_LOG, "Allows the user to view the audit logs for this organisation."), + (MANAGE_TAGS, "Allows the user to manage tags in the given project."), ] diff --git a/api/projects/tags/permissions.py b/api/projects/tags/permissions.py index f004ea08a3b0..7fcb5043ef6a 100644 --- a/api/projects/tags/permissions.py +++ b/api/projects/tags/permissions.py @@ -1,7 +1,7 @@ from rest_framework.permissions import BasePermission from projects.models import Project -from projects.permissions import VIEW_PROJECT +from projects.permissions import MANAGE_TAGS, VIEW_PROJECT class TagPermissions(BasePermission): @@ -22,12 +22,9 @@ def has_permission(self, request, view): def has_object_permission(self, request, view, obj): project = obj.project - if request.user.is_project_admin(obj.project): + if request.user.has_project_permission(MANAGE_TAGS, obj.project): return True - if view.action == "detail" and request.user.has_project_permission( + return view.action == "detail" and request.user.has_project_permission( VIEW_PROJECT, project - ): - return True - - return False + ) diff --git a/api/tests/unit/projects/tags/test_unit_projects_tags_permissions.py b/api/tests/unit/projects/tags/test_unit_projects_tags_permissions.py index 616dfc7867a9..8e671a9d5720 100644 --- a/api/tests/unit/projects/tags/test_unit_projects_tags_permissions.py +++ b/api/tests/unit/projects/tags/test_unit_projects_tags_permissions.py @@ -1,7 +1,7 @@ from unittest import mock from projects.models import Project -from projects.permissions import VIEW_PROJECT +from projects.permissions import MANAGE_TAGS, VIEW_PROJECT from projects.tags.models import Tag from projects.tags.permissions import TagPermissions from tests.types import WithProjectPermissionsCallable @@ -142,3 +142,25 @@ def test_project_user_has_detail_permission( # Then assert result is True + + +def test_project_user_with_manage_tags_has_detail_permission( + staff_user: FFAdminUser, + project: Project, + with_project_permissions: WithProjectPermissionsCallable, +) -> None: + # Given + with_project_permissions([VIEW_PROJECT, MANAGE_TAGS]) + mock_request = mock.MagicMock(user=staff_user) + mock_view = mock.MagicMock( + action="detail", + kwargs={"project_pk": project.id}, + ) + permissions = TagPermissions() + tag = Tag.objects.create(label="test", project=project) + + # When + result = permissions.has_object_permission(mock_request, mock_view, tag) + + # Then + assert result is True diff --git a/api/tests/unit/projects/test_unit_projects_views.py b/api/tests/unit/projects/test_unit_projects_views.py index 8a313dffba14..b668f7ceaa9f 100644 --- a/api/tests/unit/projects/test_unit_projects_views.py +++ b/api/tests/unit/projects/test_unit_projects_views.py @@ -152,7 +152,7 @@ def test_can_list_project_permission(client, project): # Then assert response.status_code == status.HTTP_200_OK assert ( - len(response.json()) == 6 + len(response.json()) == 7 ) # hard code how many permissions we expect there to be