From 0ecd6542bfc792b0139c7d5a97939306009268f0 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Mon, 28 Jan 2019 14:49:59 -0500 Subject: [PATCH 1/3] Changed the become_method field into one that takes arbitrary input related #2630 Signed-off-by: Jeff Bradberry --- awx/api/serializers.py | 5 +---- awx/main/fields.py | 9 ++------- awx/main/models/credential/__init__.py | 2 -- awx/main/tests/functional/test_credential.py | 13 ++++++------- awx/ui/client/src/credentials/credentials.form.js | 3 +-- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 7f249e3e14..668860f2df 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -5,7 +5,6 @@ import copy import json import logging -import operator import re import urllib.parse from collections import OrderedDict @@ -45,7 +44,6 @@ from awx.main.constants import ( ANSI_SGR_PATTERN, ACTIVE_STATES, CENSOR_VALUE, - CHOICES_PRIVILEGE_ESCALATION_METHODS, ) from awx.main.models import * # noqa from awx.main.models.base import NEW_JOB_TYPE_CHOICES @@ -2499,8 +2497,7 @@ class CredentialTypeSerializer(BaseSerializer): if 'help_text' in field: field['help_text'] = _(field['help_text']) if field['type'] == 'become_method': - field.pop('type') - field['choices'] = list(map(operator.itemgetter(0), CHOICES_PRIVILEGE_ESCALATION_METHODS)) + field['type'] = 'string' return value def filter_field_metadata(self, fields, method): diff --git a/awx/main/fields.py b/awx/main/fields.py index 2a21e063aa..86f58dffdf 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -4,7 +4,6 @@ # Python import copy import json -import operator import re import urllib.parse @@ -45,7 +44,7 @@ from awx.main.utils.filters import SmartFilter from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key from awx.main.validators import validate_ssh_private_key from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role -from awx.main.constants import CHOICES_PRIVILEGE_ESCALATION_METHODS, ENV_BLACKLIST +from awx.main.constants import ENV_BLACKLIST from awx.main import utils @@ -512,8 +511,7 @@ class CredentialInputField(JSONSchemaField): for field in model_instance.credential_type.inputs.get('fields', []): field = field.copy() if field['type'] == 'become_method': - field.pop('type') - field['choices'] = list(map(operator.itemgetter(0), CHOICES_PRIVILEGE_ESCALATION_METHODS)) + field['type'] = 'string' properties[field['id']] = field if field.get('choices', []): field['enum'] = list(field['choices'])[:] @@ -725,9 +723,6 @@ class CredentialTypeInputField(JSONSchemaField): code='invalid', params={'value': value}, ) - else: - field.pop('type') - field['choices'] = CHOICES_PRIVILEGE_ESCALATION_METHODS for key in ('choices', 'multiline', 'format', 'secret',): if key in field and field['type'] != 'string': diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index b5ad4e1636..82b4a4f446 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -32,7 +32,6 @@ from awx.main.models.rbac import ( ROLE_SINGLETON_SYSTEM_AUDITOR, ) from awx.main.utils import encrypt_field -from awx.main.constants import CHOICES_PRIVILEGE_ESCALATION_METHODS from . import injectors as builtin_injectors __all__ = ['Credential', 'CredentialType', 'V1Credential', 'build_safe_env'] @@ -163,7 +162,6 @@ class V1Credential(object): max_length=32, blank=True, default='', - choices=CHOICES_PRIVILEGE_ESCALATION_METHODS, help_text=_('Privilege escalation method.') ), 'become_username': models.CharField( diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index d9b19535e9..4b3ff2d75e 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -1,13 +1,11 @@ # 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, V1Credential +from awx.main.models import Credential, CredentialType from rest_framework import serializers @@ -206,10 +204,11 @@ def test_vault_validation(organization, inputs, valid): @pytest.mark.django_db -@pytest.mark.parametrize('become_method, valid', list(zip( - dict(V1Credential.FIELDS['become_method'].choices).keys(), - itertools.repeat(True) -)) + [('invalid-choice', False)]) +@pytest.mark.parametrize('become_method, valid', [ + ('', True), + ('sudo', True), + ('custom-plugin', True), +]) def test_choices_validity(become_method, valid, organization): inputs = {'become_method': become_method} cred_type = CredentialType.defaults['ssh']() diff --git a/awx/ui/client/src/credentials/credentials.form.js b/awx/ui/client/src/credentials/credentials.form.js index 063e9a3a6b..b42b60738f 100644 --- a/awx/ui/client/src/credentials/credentials.form.js +++ b/awx/ui/client/src/credentials/credentials.form.js @@ -278,10 +278,9 @@ export default ['i18n', function(i18n) { "become_method": { label: i18n._("Privilege Escalation"), // hintText: "If your playbooks use privilege escalation (\"sudo: true\", \"su: true\", etc), you can specify the username to become, and the password to use here.", - type: 'select', + type: 'text', ngShow: "kind.value == 'ssh'", dataTitle: i18n._('Privilege Escalation'), - ngOptions: 'become.label for become in become_options track by become.value', awPopOver: "

" + i18n.sprintf(i18n._("Specify a method for %s operations. " + "This is equivalent to specifying the %s parameter, where %s could be "+ "%s"), "'become'", "--become-method=BECOME_METHOD", "BECOME_METHOD", "sudo | su | pbrun | pfexec | runas") + "
" + i18n.sprintf(i18n._("(defaults to %s)"), "sudo") + "

", From 6e1deed79e73c8d6570d14f75347bbde7c7d8185 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Tue, 29 Jan 2019 14:06:26 -0500 Subject: [PATCH 2/3] Removed the special-case logic for maintaining the schema of the become_method field related #2630 Signed-off-by: Jeff Bradberry --- awx/api/serializers.py | 2 -- awx/main/fields.py | 12 +----------- awx/main/models/credential/__init__.py | 4 ++-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 668860f2df..bb74b4c764 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2496,8 +2496,6 @@ class CredentialTypeSerializer(BaseSerializer): field['label'] = _(field['label']) if 'help_text' in field: field['help_text'] = _(field['help_text']) - if field['type'] == 'become_method': - field['type'] = 'string' return value def filter_field_metadata(self, fields, method): diff --git a/awx/main/fields.py b/awx/main/fields.py index 86f58dffdf..832fa02f1a 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -510,8 +510,6 @@ class CredentialInputField(JSONSchemaField): properties = {} for field in model_instance.credential_type.inputs.get('fields', []): field = field.copy() - if field['type'] == 'become_method': - field['type'] = 'string' properties[field['id']] = field if field.get('choices', []): field['enum'] = list(field['choices'])[:] @@ -655,7 +653,7 @@ class CredentialTypeInputField(JSONSchemaField): 'items': { 'type': 'object', 'properties': { - 'type': {'enum': ['string', 'boolean', 'become_method']}, + 'type': {'enum': ['string', 'boolean']}, 'format': {'enum': ['ssh_private_key']}, 'choices': { 'type': 'array', @@ -716,14 +714,6 @@ class CredentialTypeInputField(JSONSchemaField): # If no type is specified, default to string field['type'] = 'string' - if field['type'] == 'become_method': - if not model_instance.managed_by_tower: - raise django_exceptions.ValidationError( - _('become_method is a reserved type name'), - code='invalid', - params={'value': value}, - ) - for key in ('choices', 'multiline', 'format', 'secret',): if key in field and field['type'] != 'string': raise django_exceptions.ValidationError( diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 82b4a4f446..ffcc087530 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -537,7 +537,7 @@ class CredentialType(CommonModelNameNotUnique): if field['id'] == field_id: if 'choices' in field: return field['choices'][0] - return {'string': '', 'boolean': False, 'become_method': ''}[field['type']] + return {'string': '', 'boolean': False}[field['type']] @classmethod def default(cls, f): @@ -734,7 +734,7 @@ def ssh(cls): }, { 'id': 'become_method', 'label': ugettext_noop('Privilege Escalation Method'), - 'type': 'become_method', + 'type': 'string', 'help_text': ugettext_noop('Specify a method for "become" operations. This is ' 'equivalent to specifying the --become-method ' 'Ansible parameter.') From 6560ab0fab2366e7815c7fff5a7b916aaa83c8fd Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Tue, 29 Jan 2019 15:04:35 -0500 Subject: [PATCH 3/3] Migrated the inputs schema on existing CredentialTypes to convert the custom become_method into a plain string. related #2630 Signed-off-by: Jeff Bradberry --- .../0057_v350_remove_become_method_type.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 awx/main/migrations/0057_v350_remove_become_method_type.py diff --git a/awx/main/migrations/0057_v350_remove_become_method_type.py b/awx/main/migrations/0057_v350_remove_become_method_type.py new file mode 100644 index 0000000000..cd5f76cdec --- /dev/null +++ b/awx/main/migrations/0057_v350_remove_become_method_type.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.16 on 2019-01-29 19:56 +from __future__ import unicode_literals + +from django.db import migrations + +# AWX +from awx.main.migrations import _credentialtypes as credentialtypes + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0056_v350_custom_venv_history'), + ] + + operations = [ + migrations.RunPython(credentialtypes.remove_become_methods), + ]