From 8fa98d0d542ca3e7ca415359a9974ebac8cac7b3 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 20 Apr 2018 11:51:49 -0400 Subject: [PATCH 1/6] Add new credential become methods, inject instead of set in database --- awx/main/constants.py | 1 + awx/main/fields.py | 14 +++++++++++++- ...0035_v330_credtype_remove_become_methods.py | 18 ++++++++++++++++++ awx/main/migrations/_credentialtypes.py | 6 ++++++ awx/main/models/credential/__init__.py | 4 ++-- 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 awx/main/migrations/0035_v330_credtype_remove_become_methods.py diff --git a/awx/main/constants.py b/awx/main/constants.py index edd00569ea..7305a94a34 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -14,6 +14,7 @@ __all__ = [ CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'cloudforms', 'tower') SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + ('custom', 'scm',) PRIVILEGE_ESCALATION_METHODS = [ + ('', _('None')), ('enable', _('Enable')), ('doas', _('Doas')), ('sudo', _('Sudo')), ('su', _('Su')), ('pbrun', _('Pbrun')), ('pfexec', _('Pfexec')), ('dzdo', _('DZDO')), ('pmrun', _('Pmrun')), ('runas', _('Runas'))] ANSI_SGR_PATTERN = re.compile(r'\x1b\[[0-9;]*m') diff --git a/awx/main/fields.py b/awx/main/fields.py index 1a41d711a3..ec9ada6cf8 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -45,6 +45,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 PRIVILEGE_ESCALATION_METHODS from awx.main import utils @@ -649,7 +650,7 @@ class CredentialTypeInputField(JSONSchemaField): 'items': { 'type': 'object', 'properties': { - 'type': {'enum': ['string', 'boolean']}, + 'type': {'enum': ['string', 'boolean', 'become_method']}, 'format': {'enum': ['ssh_private_key']}, 'choices': { 'type': 'array', @@ -710,6 +711,17 @@ 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( + _('{0} is a reserved type name'.format(field['type'])), + code='invalid', + params={'value': value}, + ) + else: + field['type'] = 'string' + field['choices'] = PRIVILEGE_ESCALATION_METHODS + for key in ('choices', 'multiline', 'format', 'secret',): if key in field and field['type'] != 'string': raise django_exceptions.ValidationError( diff --git a/awx/main/migrations/0035_v330_credtype_remove_become_methods.py b/awx/main/migrations/0035_v330_credtype_remove_become_methods.py new file mode 100644 index 0000000000..130a949ec7 --- /dev/null +++ b/awx/main/migrations/0035_v330_credtype_remove_become_methods.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +# AWX +from awx.main.migrations import _credentialtypes as credentialtypes + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0034_v330_more_oauth2_help_text'), + ] + + operations = [ + migrations.RunPython(credentialtypes.remove_become_methods), + ] diff --git a/awx/main/migrations/_credentialtypes.py b/awx/main/migrations/_credentialtypes.py index bf4128c92e..247ca42a36 100644 --- a/awx/main/migrations/_credentialtypes.py +++ b/awx/main/migrations/_credentialtypes.py @@ -197,3 +197,9 @@ def add_azure_cloud_environment_field(apps, schema_editor): name='Microsoft Azure Resource Manager') azure_rm_credtype.inputs = CredentialType.defaults.get('azure_rm')().inputs azure_rm_credtype.save() + + +def remove_become_methods(apps, schema_editor): + become_credtype = CredentialType.objects.get(kind='ssh') + become_credtype.inputs = CredentialType.defaults.get('ssh')().inputs + become_credtype.save() diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index b390043765..b372144512 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -165,7 +165,7 @@ class V1Credential(object): max_length=32, blank=True, default='', - choices=[('', _('None'))] + PRIVILEGE_ESCALATION_METHODS, + choices=PRIVILEGE_ESCALATION_METHODS, help_text=_('Privilege escalation method.') ), 'become_username': models.CharField( @@ -516,7 +516,7 @@ class CredentialType(CommonModelNameNotUnique): if field['id'] == field_id: if 'choices' in field: return field['choices'][0] - return {'string': '', 'boolean': False}[field['type']] + return {'string': '', 'boolean': False, 'become_method': ''}[field['type']] @classmethod def default(cls, f): From d6ac9b6e3dd5684a4aa3d98322a6b127f9e1851a Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 20 Apr 2018 13:00:18 -0400 Subject: [PATCH 2/6] Restore previous choices, clean up some minor things --- awx/main/constants.py | 2 +- awx/main/fields.py | 2 +- awx/main/migrations/_credentialtypes.py | 2 +- awx/main/models/credential/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/main/constants.py b/awx/main/constants.py index 7305a94a34..2ca34a47c5 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -14,7 +14,7 @@ __all__ = [ CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'cloudforms', 'tower') SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + ('custom', 'scm',) PRIVILEGE_ESCALATION_METHODS = [ - ('', _('None')), ('enable', _('Enable')), ('doas', _('Doas')), + ('enable', _('Enable')), ('doas', _('Doas')), ('sudo', _('Sudo')), ('su', _('Su')), ('pbrun', _('Pbrun')), ('pfexec', _('Pfexec')), ('dzdo', _('DZDO')), ('pmrun', _('Pmrun')), ('runas', _('Runas'))] ANSI_SGR_PATTERN = re.compile(r'\x1b\[[0-9;]*m') diff --git a/awx/main/fields.py b/awx/main/fields.py index ec9ada6cf8..d956e3e808 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -714,7 +714,7 @@ class CredentialTypeInputField(JSONSchemaField): if field['type'] == 'become_method': if not model_instance.managed_by_tower: raise django_exceptions.ValidationError( - _('{0} is a reserved type name'.format(field['type'])), + _('become_method is a reserved type name'), code='invalid', params={'value': value}, ) diff --git a/awx/main/migrations/_credentialtypes.py b/awx/main/migrations/_credentialtypes.py index 247ca42a36..6d71776d6b 100644 --- a/awx/main/migrations/_credentialtypes.py +++ b/awx/main/migrations/_credentialtypes.py @@ -200,6 +200,6 @@ def add_azure_cloud_environment_field(apps, schema_editor): def remove_become_methods(apps, schema_editor): - become_credtype = CredentialType.objects.get(kind='ssh') + become_credtype = CredentialType.objects.get(kind='ssh').first() become_credtype.inputs = CredentialType.defaults.get('ssh')().inputs become_credtype.save() diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index b372144512..b40b85154d 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -165,7 +165,7 @@ class V1Credential(object): max_length=32, blank=True, default='', - choices=PRIVILEGE_ESCALATION_METHODS, + choices=[('', _('None'))] + PRIVILEGE_ESCALATION_METHODS, help_text=_('Privilege escalation method.') ), 'become_username': models.CharField( From 07474d5b21bd12ba2c665651e21b2b31180e3ab8 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 20 Apr 2018 14:37:50 -0400 Subject: [PATCH 3/6] Extend become_method to model field validation as well --- awx/main/constants.py | 1 + awx/main/fields.py | 8 ++++++-- awx/main/models/credential/__init__.py | 8 +++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/awx/main/constants.py b/awx/main/constants.py index 2ca34a47c5..e966d4b72a 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -17,6 +17,7 @@ PRIVILEGE_ESCALATION_METHODS = [ ('enable', _('Enable')), ('doas', _('Doas')), ('sudo', _('Sudo')), ('su', _('Su')), ('pbrun', _('Pbrun')), ('pfexec', _('Pfexec')), ('dzdo', _('DZDO')), ('pmrun', _('Pmrun')), ('runas', _('Runas'))] +CHOICES_PRIVILEGE_ESCALATION_METHODS = [('', _('None'))] + PRIVILEGE_ESCALATION_METHODS ANSI_SGR_PATTERN = re.compile(r'\x1b\[[0-9;]*m') CAN_CANCEL = ('new', 'pending', 'waiting', 'running') ACTIVE_STATES = CAN_CANCEL diff --git a/awx/main/fields.py b/awx/main/fields.py index d956e3e808..ab039eed58 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -4,6 +4,7 @@ # Python import copy import json +import operator import re import six import urllib @@ -45,7 +46,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 PRIVILEGE_ESCALATION_METHODS +from awx.main.constants import CHOICES_PRIVILEGE_ESCALATION_METHODS from awx.main import utils @@ -507,6 +508,9 @@ class CredentialInputField(JSONSchemaField): properties = {} for field in model_instance.credential_type.inputs.get('fields', []): field = field.copy() + if field['type'] == 'become_method': + field.pop('type') + field['choices'] = map(operator.itemgetter(0), CHOICES_PRIVILEGE_ESCALATION_METHODS) properties[field['id']] = field if field.get('choices', []): field['enum'] = field['choices'][:] @@ -720,7 +724,7 @@ class CredentialTypeInputField(JSONSchemaField): ) else: field['type'] = 'string' - field['choices'] = PRIVILEGE_ESCALATION_METHODS + 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 b40b85154d..0d988706f8 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -4,7 +4,6 @@ from collections import OrderedDict import functools import json import logging -import operator import os import re import stat @@ -22,7 +21,6 @@ from django.utils.encoding import force_text # AWX from awx.api.versioning import reverse -from awx.main.constants import PRIVILEGE_ESCALATION_METHODS from awx.main.fields import (ImplicitRoleField, CredentialInputField, CredentialTypeInputField, CredentialTypeInjectorField) @@ -35,6 +33,7 @@ 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'] @@ -165,7 +164,7 @@ class V1Credential(object): max_length=32, blank=True, default='', - choices=[('', _('None'))] + PRIVILEGE_ESCALATION_METHODS, + choices=CHOICES_PRIVILEGE_ESCALATION_METHODS, help_text=_('Privilege escalation method.') ), 'become_username': models.CharField( @@ -708,8 +707,7 @@ def ssh(cls): }, { 'id': 'become_method', 'label': 'Privilege Escalation Method', - 'choices': map(operator.itemgetter(0), - V1Credential.FIELDS['become_method'].choices), + 'type': 'become_method', 'help_text': ('Specify a method for "become" operations. This is ' 'equivalent to specifying the --become-method ' 'Ansible parameter.') From c1a8d8670fb68c75812bf346b5a86a606e1d4ec5 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 20 Apr 2018 15:27:24 -0400 Subject: [PATCH 4/6] Pop the type to be consistent --- awx/main/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index ab039eed58..44389f3879 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -723,7 +723,7 @@ class CredentialTypeInputField(JSONSchemaField): params={'value': value}, ) else: - field['type'] = 'string' + field.pop('type') field['choices'] = CHOICES_PRIVILEGE_ESCALATION_METHODS for key in ('choices', 'multiline', 'format', 'secret',): From f1b37ff53a5a2174123984f1127e528d39e8acfc Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 23 Apr 2018 10:19:07 -0400 Subject: [PATCH 5/6] Fix order become methods render and migration query --- awx/api/serializers.py | 11 ++++++++++- awx/main/constants.py | 5 +++-- awx/main/migrations/_credentialtypes.py | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 0c887da968..955abb9b74 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -38,7 +38,13 @@ from rest_framework.utils.serializer_helpers import ReturnList from polymorphic.models import PolymorphicModel # AWX -from awx.main.constants import SCHEDULEABLE_PROVIDERS, ANSI_SGR_PATTERN, ACTIVE_STATES, TOKEN_CENSOR +from awx.main.constants import ( + SCHEDULEABLE_PROVIDERS, + ANSI_SGR_PATTERN, + ACTIVE_STATES, + TOKEN_CENSOR, + CHOICES_PRIVILEGE_ESCALATION_METHODS, +) from awx.main.models import * # noqa from awx.main.models.base import NEW_JOB_TYPE_CHOICES from awx.main.access import get_user_capabilities @@ -2494,6 +2500,9 @@ class CredentialTypeSerializer(BaseSerializer): field['label'] = _(field['label']) if 'help_text' in field: field['help_text'] = _(field['help_text']) + if field['type'] == 'become_method': + field.pop('type') + field['choices'] = map(operator.itemgetter(0), CHOICES_PRIVILEGE_ESCALATION_METHODS) return value def filter_field_metadata(self, fields, method): diff --git a/awx/main/constants.py b/awx/main/constants.py index e966d4b72a..e7c8a943fc 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -14,9 +14,10 @@ __all__ = [ CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'cloudforms', 'tower') SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + ('custom', 'scm',) PRIVILEGE_ESCALATION_METHODS = [ - ('enable', _('Enable')), ('doas', _('Doas')), ('sudo', _('Sudo')), ('su', _('Su')), ('pbrun', _('Pbrun')), ('pfexec', _('Pfexec')), - ('dzdo', _('DZDO')), ('pmrun', _('Pmrun')), ('runas', _('Runas'))] + ('dzdo', _('DZDO')), ('pmrun', _('Pmrun')), ('runas', _('Runas')), + ('enable', _('Enable')), ('doas', _('Doas')), +] CHOICES_PRIVILEGE_ESCALATION_METHODS = [('', _('None'))] + PRIVILEGE_ESCALATION_METHODS ANSI_SGR_PATTERN = re.compile(r'\x1b\[[0-9;]*m') CAN_CANCEL = ('new', 'pending', 'waiting', 'running') diff --git a/awx/main/migrations/_credentialtypes.py b/awx/main/migrations/_credentialtypes.py index 6d71776d6b..fbf812e8c2 100644 --- a/awx/main/migrations/_credentialtypes.py +++ b/awx/main/migrations/_credentialtypes.py @@ -200,6 +200,6 @@ def add_azure_cloud_environment_field(apps, schema_editor): def remove_become_methods(apps, schema_editor): - become_credtype = CredentialType.objects.get(kind='ssh').first() + become_credtype = CredentialType.objects.filter(kind='ssh', managed_by_tower=True).first() become_credtype.inputs = CredentialType.defaults.get('ssh')().inputs become_credtype.save() From 765ad07d9e44d9a8bee612240703a15d8fa04032 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Mon, 23 Apr 2018 10:35:44 -0400 Subject: [PATCH 6/6] Fix migration name/ordering --- awx/api/serializers.py | 1 + ..._methods.py => 0036_v330_credtype_remove_become_methods.py} | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) rename awx/main/migrations/{0035_v330_credtype_remove_become_methods.py => 0036_v330_credtype_remove_become_methods.py} (86%) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 955abb9b74..ca9464f93b 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -5,6 +5,7 @@ import copy import json import logging +import operator import re import six import urllib diff --git a/awx/main/migrations/0035_v330_credtype_remove_become_methods.py b/awx/main/migrations/0036_v330_credtype_remove_become_methods.py similarity index 86% rename from awx/main/migrations/0035_v330_credtype_remove_become_methods.py rename to awx/main/migrations/0036_v330_credtype_remove_become_methods.py index 130a949ec7..3a43bd6a8b 100644 --- a/awx/main/migrations/0035_v330_credtype_remove_become_methods.py +++ b/awx/main/migrations/0036_v330_credtype_remove_become_methods.py @@ -10,7 +10,8 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('main', '0034_v330_more_oauth2_help_text'), + ('main', '0035_v330_more_oauth2_help_text'), + ] operations = [