[PATCH 3/3] REST: Allow a project to restrict submitter state changes
Stephen Finucane
stephen at that.guru
Wed Aug 4 01:32:48 AEST 2021
On Tue, 2021-08-03 at 01:27 +1000, Daniel Axtens wrote:
> 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
> +
Oh, I think both this and the existing 'validate_delegate' are in the wrong
class. 'PatchListSerializer' is only used by the 'PatchList' view, which is
read-only. These should probably both be 'PatchDetailSerializer'? Assuming I've
groked things correctly, could you move 'validate_delegate' in a precursor
patch?
> 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]))
Same question as the XML-RPC changes. I think a simple positive-negative test
would be more than sufficient here. Let's leave the full matrix to a simple unit
test of the model method.
Stephen
More information about the Patchwork
mailing list