diff --git a/awx/api/serializers.py b/awx/api/serializers.py index ac481a5277..8f47842265 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -23,7 +23,6 @@ from django.db import models # from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import force_text from django.utils.text import capfirst -from django.forms.models import model_to_dict # Django REST Framework from rest_framework.exceptions import ValidationError @@ -1849,9 +1848,8 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): d['can_copy'] = not validation_errors d['can_edit'] = True else: - jt_data = model_to_dict(obj) - d['can_copy'] = (not validation_errors) and request.user.can_access(JobTemplate, 'add', jt_data) - d['can_edit'] = request.user.can_access(JobTemplate, 'change', obj, jt_data) + d['can_copy'] = (not validation_errors) and request.user.can_access(JobTemplate, 'add', {"reference_obj": obj}) + d['can_edit'] = request.user.can_access(JobTemplate, 'change', obj, {}) d['recent_jobs'] = self._recent_jobs(obj) return d diff --git a/awx/main/access.py b/awx/main/access.py index 639245f1bc..624d8945d2 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -788,6 +788,9 @@ class JobTemplateAccess(BaseAccess): if not data or '_method' in data: # So the browseable API will work? return True + # if reference_obj is provided, determine if it can be coppied + reference_obj = data.pop('reference_obj', None) + if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: self.check_license(feature='system_tracking') @@ -797,51 +800,54 @@ class JobTemplateAccess(BaseAccess): if self.user.is_superuser: return True + def get_value(Class, field): + if reference_obj: + return getattr(reference_obj, field, None) + else: + pk = get_pk_from_dict(data, field) + if pk: + return get_object_or_400(Class, pk=pk) + else: + return None + # If a credential is provided, the user should have use access to it. - credential_pk = get_pk_from_dict(data, 'credential') - if credential_pk: - credential = get_object_or_400(Credential, pk=credential_pk) + credential = get_value(Credential, 'credential') + if credential: if self.user not in credential.use_role: return False # If a cloud credential is provided, the user should have use access. - cloud_credential_pk = get_pk_from_dict(data, 'cloud_credential') - if cloud_credential_pk: - cloud_credential = get_object_or_400(Credential, - pk=cloud_credential_pk) + cloud_credential = get_value(Credential, 'cloud_credential') + if cloud_credential: if self.user not in cloud_credential.use_role: return False # If a network credential is provided, the user should have use access. - network_credential_pk = get_pk_from_dict(data, 'network_credential') - if network_credential_pk: - network_credential = get_object_or_400(Credential, - pk=network_credential_pk) + network_credential = get_value(Credential, 'network_credential') + if network_credential: if self.user not in network_credential.use_role: return False # If an inventory is provided, the user should have use access. - inventory_pk = get_pk_from_dict(data, 'inventory') - if inventory_pk: - inventory = get_object_or_400(Inventory, pk=inventory_pk) + inventory = get_value(Inventory, 'inventory') + if inventory: if self.user not in inventory.use_role: return False - project_pk = get_pk_from_dict(data, 'project') + project = get_value(Project, 'project') if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: - if inventory_pk and inventory.organization: + if inventory: org = inventory.organization accessible = self.user in org.admin_role else: accessible = False - if not project_pk and accessible: + if not project and accessible: return True elif not accessible: return False # If the user has admin access to the project (as an org admin), should # be able to proceed without additional checks. - if project_pk: - project = get_object_or_400(Project, pk=project_pk) + if project: return self.user in project.use_role else: return False diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index 64f0ad2197..14c92c4a54 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -195,7 +195,7 @@ def test_org_admin_foreign_cred_no_copy_edit(jt_copy_edit, org_admin, machine_cr """ Organization admins without access to the 3 related resources: SHOULD NOT be able to copy JT - SHOULD NOT be able to edit that job template + SHOULD be able to edit that job template, for nonsensitive changes """ # Attach credential to JT that org admin can not use @@ -209,11 +209,13 @@ def test_org_admin_foreign_cred_no_copy_edit(jt_copy_edit, org_admin, machine_cr serializer.context['request'] = request response = serializer.to_representation(jt_copy_edit) assert not response['summary_fields']['can_copy'] - assert not response['summary_fields']['can_edit'] + assert response['summary_fields']['can_edit'] @pytest.mark.django_db def test_jt_admin_copy_edit(jt_copy_edit, rando): - "JT admins wihout access to associated resources SHOULD NOT be able to copy" + """ + JT admins wihout access to associated resources SHOULD NOT be able to copy + SHOULD be able to make nonsensitive changes""" # random user given JT admin access only jt_copy_edit.admin_role.members.add(rando) @@ -226,7 +228,7 @@ def test_jt_admin_copy_edit(jt_copy_edit, rando): serializer.context['request'] = request response = serializer.to_representation(jt_copy_edit) assert not response['summary_fields']['can_copy'] - assert not response['summary_fields']['can_edit'] + assert response['summary_fields']['can_edit'] @pytest.mark.django_db def test_proj_jt_admin_copy_edit(jt_copy_edit, rando):