[PATCH 3/3] REST: Allow a project to restrict submitter state changes

Daniel Axtens dja at axtens.net
Tue Aug 3 01:27:29 AEST 2021


As with xmlrpc and the UI.

Signed-off-by: Daniel Axtens <dja at axtens.net>
---
 patchwork/api/patch.py            | 10 +++++
 patchwork/tests/api/test_patch.py | 70 +++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py
index 9d222754412e..b8d0d5e17749 100644
--- a/patchwork/api/patch.py
+++ b/patchwork/api/patch.py
@@ -122,6 +122,16 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
                                   "'%s'" % (value, self.instance.project))
         return value
 
+    def validate_state(self, value):
+        """Check that the users is authorised to set this state."""
+        user = self.context.get('request').user
+        if not self.instance.can_set_state(user):
+            raise ValidationError(
+                "User '%s' is not permitted to set state '%s' on this patch." %
+                (user, value.name))
+
+        return value
+
     def to_representation(self, instance):
         # NOTE(stephenfin): This is here to ensure our API looks the same even
         # after we changed the series-patch relationship from M:N to 1:N. It
diff --git a/patchwork/tests/api/test_patch.py b/patchwork/tests/api/test_patch.py
index da2dd6e9084b..9de7b0d105f4 100644
--- a/patchwork/tests/api/test_patch.py
+++ b/patchwork/tests/api/test_patch.py
@@ -11,6 +11,9 @@ from django.conf import settings
 from django.urls import reverse
 
 from patchwork.models import Patch
+from patchwork.models import Person
+from patchwork.models import Project
+from patchwork.models import State
 from patchwork.tests.api import utils
 from patchwork.tests.utils import create_maintainer
 from patchwork.tests.utils import create_patch
@@ -409,3 +412,70 @@ class TestPatchAPI(utils.APITestCase):
         self.client.force_authenticate(user=user)
         resp = self.client.delete(self.api_url(patch.id))
         self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code)
+
+
+ at unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API')
+class TestPatchStateChecks(utils.APITestCase):
+    fixtures = ['default_tags']
+
+    @staticmethod
+    def api_url(item=None):
+        kwargs = {'pk': item}
+        return reverse('api-patch-detail', kwargs=kwargs)
+
+    def setUp(self):
+        self.projects = {}
+        self.maintainers = {}
+        self.delegates = {}
+        self.submitters = {}
+        self.patches = {}
+
+        for project_type in (Project.SUBMITTER_NO_STATE_CHANGES,
+                             Project.SUBMITTER_ALL_STATE_CHANGES):
+            project = create_project(
+                submitter_state_change_rules=project_type)
+            self.projects[project_type] = project
+            self.maintainers[project_type] = create_maintainer(project)
+            submitter = create_user(project)
+            self.submitters[project_type] = submitter
+            delegate = create_user(project)
+            self.delegates[project_type] = delegate
+
+            patch = create_patch(project=project,
+                                 submitter=Person.objects.get(
+                                     user=submitter),
+                                 delegate=delegate)
+            self.patches[project_type] = patch
+
+        create_state(name="New")
+        create_state(name="RFC")
+
+    def can_set_state(self, patch, user):
+        new_state = State.objects.get(name="New")
+        rfc_state = State.objects.get(name="RFC")
+        patch.state = new_state
+        patch.save()
+
+        self.client.force_authenticate(user=user)
+        resp = self.client.patch(self.api_url(patch.id),
+                                 {'state': rfc_state.slug})
+
+        if resp.status_code != status.HTTP_200_OK:
+            return False
+
+        self.assertEqual(Patch.objects.get(id=patch.id).state, rfc_state)
+        return True
+
+    def test_allset(self):
+        project = Project.SUBMITTER_ALL_STATE_CHANGES
+        patch = self.patches[project]
+        self.assertTrue(self.can_set_state(patch, self.maintainers[project]))
+        self.assertTrue(self.can_set_state(patch, self.delegates[project]))
+        self.assertTrue(self.can_set_state(patch, self.submitters[project]))
+
+    def test_noset(self):
+        project = Project.SUBMITTER_NO_STATE_CHANGES
+        patch = self.patches[project]
+        self.assertTrue(self.can_set_state(patch, self.maintainers[project]))
+        self.assertTrue(self.can_set_state(patch, self.delegates[project]))
+        self.assertFalse(self.can_set_state(patch, self.submitters[project]))
-- 
2.30.2



More information about the Patchwork mailing list