diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 7bb09c4acf..053dc4cd33 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2456,12 +2456,18 @@ class CredentialTypeSerializer(BaseSerializer): raise PermissionDenied( detail=_("Modifications not allowed for managed credential types") ) + + old_inputs = {} + if self.instance: + old_inputs = copy.deepcopy(self.instance.inputs) + + ret = super(CredentialTypeSerializer, self).validate(attrs) + if self.instance and self.instance.credentials.exists(): - if 'inputs' in attrs and attrs['inputs'] != self.instance.inputs: + if 'inputs' in attrs and old_inputs != self.instance.inputs: raise PermissionDenied( detail= _("Modifications to inputs are not allowed for credential types that are in use") ) - ret = super(CredentialTypeSerializer, self).validate(attrs) if 'kind' in attrs and attrs['kind'] not in ('cloud', 'net'): raise serializers.ValidationError({ diff --git a/awx/main/tests/functional/api/test_credential_type.py b/awx/main/tests/functional/api/test_credential_type.py index 730b3b03d3..ecb11e7e0d 100644 --- a/awx/main/tests/functional/api/test_credential_type.py +++ b/awx/main/tests/functional/api/test_credential_type.py @@ -85,14 +85,35 @@ def test_update_credential_type_in_use_xfail(patch, delete, admin): Credential(credential_type=_type, name='My Custom Cred').save() url = reverse('api:credential_type_detail', kwargs={'pk': _type.pk}) - response = patch(url, {'name': 'Some Other Name'}, admin) - assert response.status_code == 200 + patch(url, {'name': 'Some Other Name'}, admin, expect=200) url = reverse('api:credential_type_detail', kwargs={'pk': _type.pk}) - response = patch(url, {'inputs': {}}, admin) - assert response.status_code == 403 + response = patch(url, {'inputs': {}}, admin, expect=403) + assert response.data['detail'] == 'Modifications to inputs are not allowed for credential types that are in use' - assert delete(url, admin).status_code == 403 + response = delete(url, admin, expect=403) + assert response.data['detail'] == 'Credential types that are in use cannot be deleted' + + +@pytest.mark.django_db +def test_update_credential_type_unvalidated_inputs(post, patch, admin): + simple_inputs = {'fields': [ + {'id': 'api_token', 'label': 'fooo'} + ]} + response = post( + url=reverse('api:credential_type_list'), + data={'name': 'foo', 'kind': 'cloud', 'inputs': simple_inputs}, + user=admin, + expect=201 + ) + # validation adds the type field to the input + _type = CredentialType.objects.get(pk=response.data['id']) + Credential(credential_type=_type, name='My Custom Cred').save() + + # should not raise an error because we should only compare + # post-validation values to other post-validation values + url = reverse('api:credential_type_detail', kwargs={'pk': _type.id}) + patch(url, {'inputs': simple_inputs}, admin, expect=200) @pytest.mark.django_db