diff --git a/awx/main/fields.py b/awx/main/fields.py index a492932b43..ca4fbc64c8 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -363,6 +363,8 @@ class JSONSchemaField(JSONBField): super(JSONSchemaField, self).validate(value, model_instance) errors = [] for error in Draft4Validator(self.schema(model_instance)).iter_errors(value): + if error.validator == 'pattern' and 'error' in error.schema: + error.message = error.schema['error'] % error.instance errors.append(error) if errors: @@ -471,7 +473,11 @@ class CredentialTypeInputField(JSONSchemaField): 'items': {'type': 'string'}, 'uniqueItems': True }, - 'id': {'type': 'string'}, + 'id': { + 'type': 'string', + 'pattern': '^[a-zA-Z_]+[a-zA-Z0-9_]*$', + 'error': '%s is an invalid variable name', + }, 'label': {'type': 'string'}, 'help_text': {'type': 'string'}, 'multiline': {'type': 'boolean'}, @@ -490,14 +496,24 @@ class CredentialTypeInputField(JSONSchemaField): value, model_instance ) + ids = {} for field in value.get('fields', []): - if field.get('id') == 'tower': + id_ = field.get('id') + if id_ == 'tower': raise django_exceptions.ValidationError( _('"tower" is a reserved field name'), code='invalid', params={'value': value}, ) + if id_ in ids: + raise django_exceptions.ValidationError( + _('field IDs must be unique (%s)' % id_), + code='invalid', + params={'value': value}, + ) + ids[id_] = True + class CredentialTypeInjectorField(JSONSchemaField): diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index 62e6c49bcc..b6ea847038 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -58,6 +58,8 @@ def test_cloud_kind_uniqueness(): ({'fields': [{'id': 'username', 'label': 'Username', 'type': 'string'}]}, True), ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 1}]}, False), ({'fields': [{'id': 'username', 'label': 'Username', 'help_text': 'Help Text'}]}, True), # noqa + ({'fields': [{'id': 'username', 'label': 'Username'}, {'id': 'username', 'label': 'Username 2'}]}, False), # noqa + ({'fields': [{'id': '$invalid$', 'label': 'Invalid'}]}, False), # noqa ({'fields': [{'id': 'password', 'label': 'Password', 'type': 'number'}]}, True), ({'fields': [{'id': 'ssh_key', 'label': 'SSH Key', 'type': 'ssh_private_key'}]}, True), # noqa ({'fields': [{'id': 'other', 'label': 'Other', 'type': 'boolean'}]}, False),