From afb307c146d2ca914727d1a9cb62018bef7ab0e6 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Mon, 17 Jul 2017 16:24:41 -0400 Subject: [PATCH] properly validate `choices` for credential input validation see: #7119 --- awx/main/fields.py | 13 +++++++++ awx/main/tests/functional/test_credential.py | 29 +++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index e523afa16d..ae0d89848a 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -48,6 +48,17 @@ from awx.main import utils __all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField', 'SmartFilterField'] +# Provide a (better) custom error message for enum jsonschema validation +def __enum_validate__(validator, enums, instance, schema): + if instance not in enums: + yield jsonschema.exceptions.ValidationError( + _("'%s' is not one of ['%s']") % (instance, "', '".join(enums)) + ) + + +Draft4Validator.VALIDATORS['enum'] = __enum_validate__ + + class JSONField(upstream_JSONField): def db_type(self, connection): @@ -451,6 +462,8 @@ class CredentialInputField(JSONSchemaField): for field in model_instance.credential_type.inputs.get('fields', []): field = field.copy() properties[field['id']] = field + if field.get('choices', []): + field['enum'] = field['choices'][:] return { 'type': 'object', 'properties': properties, diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index a288eff1e4..25b4504687 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -1,11 +1,13 @@ # Copyright (c) 2017 Ansible by Red Hat # All Rights Reserved. +import itertools + import pytest from django.core.exceptions import ValidationError from awx.main.utils import decrypt_field -from awx.main.models import Credential, CredentialType +from awx.main.models import Credential, CredentialType, V1Credential from rest_framework import serializers @@ -245,6 +247,31 @@ def test_ssh_key_data_validation(organization, kind, ssh_key_data, ssh_key_unloc assert e.type in (ValidationError, serializers.ValidationError) +@pytest.mark.django_db +@pytest.mark.parametrize('become_method, valid', zip( + dict(V1Credential.FIELDS['become_method'].choices).keys(), + itertools.repeat(True) +) + [('invalid-choice', False)]) +def test_choices_validity(become_method, valid, organization): + inputs = {'become_method': become_method} + cred_type = CredentialType.defaults['ssh']() + cred_type.save() + cred = Credential( + credential_type=cred_type, + name="Best credential ever", + inputs=inputs, + organization=organization + ) + cred.save() + + if valid: + cred.full_clean() + else: + with pytest.raises(serializers.ValidationError) as e: + cred.full_clean() + assert "'%s' is not one of" % become_method in str(e) + + @pytest.mark.django_db def test_credential_encryption(organization_factory, credentialtype_ssh): org = organization_factory('test').organization