From e995e5fc5f925078aab296acda5acb1df39b49d0 Mon Sep 17 00:00:00 2001 From: Liam Allen Date: Mon, 28 Sep 2026 13:39:21 +0100 Subject: [PATCH] Enforce read permission check on copy API endpoint (#16676) --- awx/api/generics.py | 2 ++ awx/main/tests/functional/test_copy.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/awx/api/generics.py b/awx/api/generics.py index ae2fceb26a..c6a016fa17 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -999,6 +999,8 @@ class CopyAPIView(GenericAPIView): def post(self, request, *args, **kwargs): obj = self.get_object() + if not request.user.can_access(obj.__class__, 'read', obj): + raise PermissionDenied() create_kwargs = self._build_create_dict(obj) create_kwargs_check = {} for key in create_kwargs: diff --git a/awx/main/tests/functional/test_copy.py b/awx/main/tests/functional/test_copy.py index adb4aead4c..c357dc1d0c 100644 --- a/awx/main/tests/functional/test_copy.py +++ b/awx/main/tests/functional/test_copy.py @@ -243,6 +243,27 @@ def test_workflow_approval_node_copy(workflow_job_template, post, get, admin, or assert set([x.name for x in original_templates]) == set([x.name.replace(' copy', '') for x in copied_templates]) +@pytest.mark.django_db +def test_copy_post_requires_read_access(post, project, inventory, admin, alice, organization): + jt = JobTemplate.objects.create( + name='private-jt', + project=project, + inventory=inventory, + organization=organization, + ) + copy_url = reverse('api:job_template_copy', kwargs={'pk': jt.pk}) + + project.use_role.members.add(alice) + inventory.use_role.members.add(alice) + + post(copy_url, {'name': 'stolen copy'}, alice, expect=403) + + jt_copy_pk = post(copy_url, {'name': 'legit copy'}, admin, expect=201).data['id'] + jt_copy = JobTemplate.objects.get(pk=jt_copy_pk) + assert jt_copy.name == 'legit copy' + assert jt_copy.created_by == admin + + @pytest.mark.django_db def test_credential_copy(post, get, machine_credential, credentialtype_ssh, admin): assert get(reverse('api:credential_copy', kwargs={'pk': machine_credential.pk}), admin, expect=200).data['can_copy'] is True