mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 23:46:05 -03:30
define native CredentialType inputs/injectors in code, not in the DB
This has a few benefits:
1. It makes adding new fields to built-in CredentialTypes _much_
simpler. In the past, we've had to write a migration every time we
want to modify an existing type (changing a label/help text,
changing options like the recent become_method changes) or
when adding a new field entirely
2. It paves the way for third party credential plugins support, where
importable libraries will define their own source code-based schema
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.16 on 2019-02-19 04:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from awx.main.models import CredentialType
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_to_static_inputs(apps, schema_editor):
|
||||||
|
CredentialType.setup_tower_managed_defaults()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0060_v350_update_schedule_uniqueness_constraint'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='credentialtype',
|
||||||
|
name='namespace',
|
||||||
|
field=models.CharField(default=None, editable=False, max_length=1024, null=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(migrate_to_static_inputs)
|
||||||
|
]
|
||||||
@@ -62,155 +62,40 @@ def _disassociate_non_insights_projects(apps, cred):
|
|||||||
|
|
||||||
|
|
||||||
def migrate_to_v2_credentials(apps, schema_editor):
|
def migrate_to_v2_credentials(apps, schema_editor):
|
||||||
CredentialType.setup_tower_managed_defaults()
|
# TODO: remove once legacy/EOL'd Towers no longer support this upgrade path
|
||||||
deprecated_cred = _generate_deprecated_cred_types()
|
pass
|
||||||
|
|
||||||
# this monkey-patch is necessary to make the implicit role generation save
|
|
||||||
# signal use the correct Role model (the version active at this point in
|
|
||||||
# migration, not the one at HEAD)
|
|
||||||
orig_current_apps = utils.get_current_apps
|
|
||||||
try:
|
|
||||||
utils.get_current_apps = lambda: apps
|
|
||||||
for cred in apps.get_model('main', 'Credential').objects.all():
|
|
||||||
job_templates = cred.jobtemplates.all()
|
|
||||||
jobs = cred.jobs.all()
|
|
||||||
data = {}
|
|
||||||
if getattr(cred, 'vault_password', None):
|
|
||||||
data['vault_password'] = cred.vault_password
|
|
||||||
if _is_insights_scm(apps, cred):
|
|
||||||
_disassociate_non_insights_projects(apps, cred)
|
|
||||||
credential_type = _get_insights_credential_type()
|
|
||||||
else:
|
|
||||||
credential_type = _populate_deprecated_cred_types(deprecated_cred, cred.kind) or CredentialType.from_v1_kind(cred.kind, data)
|
|
||||||
|
|
||||||
defined_fields = credential_type.defined_fields
|
|
||||||
cred.credential_type = apps.get_model('main', 'CredentialType').objects.get(pk=credential_type.pk)
|
|
||||||
|
|
||||||
for field in defined_fields:
|
|
||||||
if getattr(cred, field, None):
|
|
||||||
cred.inputs[field] = getattr(cred, field)
|
|
||||||
if cred.vault_password:
|
|
||||||
for jt in job_templates:
|
|
||||||
jt.credential = None
|
|
||||||
jt.vault_credential = cred
|
|
||||||
jt.save()
|
|
||||||
for job in jobs:
|
|
||||||
job.credential = None
|
|
||||||
job.vault_credential = cred
|
|
||||||
job.save()
|
|
||||||
if data.get('is_insights', False):
|
|
||||||
cred.kind = 'insights'
|
|
||||||
cred.save()
|
|
||||||
|
|
||||||
#
|
|
||||||
# If the credential contains a vault password, create a new
|
|
||||||
# *additional* credential for the ssh details
|
|
||||||
#
|
|
||||||
if cred.vault_password:
|
|
||||||
# We need to make an ssh credential, too
|
|
||||||
ssh_type = CredentialType.from_v1_kind('ssh')
|
|
||||||
new_cred = apps.get_model('main', 'Credential').objects.get(pk=cred.pk)
|
|
||||||
new_cred.pk = None
|
|
||||||
new_cred.vault_password = ''
|
|
||||||
new_cred.credential_type = apps.get_model('main', 'CredentialType').objects.get(pk=ssh_type.pk)
|
|
||||||
if 'vault_password' in new_cred.inputs:
|
|
||||||
del new_cred.inputs['vault_password']
|
|
||||||
|
|
||||||
# unset these attributes so that new roles are properly created
|
|
||||||
# at save time
|
|
||||||
new_cred.read_role = None
|
|
||||||
new_cred.admin_role = None
|
|
||||||
new_cred.use_role = None
|
|
||||||
|
|
||||||
if any([getattr(cred, field) for field in ssh_type.defined_fields]):
|
|
||||||
new_cred.save(force_insert=True)
|
|
||||||
|
|
||||||
# copy rbac roles
|
|
||||||
for role_type in ('read_role', 'admin_role', 'use_role'):
|
|
||||||
for member in getattr(cred, role_type).members.all():
|
|
||||||
getattr(new_cred, role_type).members.add(member)
|
|
||||||
for role in getattr(cred, role_type).parents.all():
|
|
||||||
getattr(new_cred, role_type).parents.add(role)
|
|
||||||
|
|
||||||
for jt in job_templates:
|
|
||||||
jt.credential = new_cred
|
|
||||||
jt.save()
|
|
||||||
for job in jobs:
|
|
||||||
job.credential = new_cred
|
|
||||||
job.save()
|
|
||||||
|
|
||||||
# passwords must be decrypted and re-encrypted, because
|
|
||||||
# their encryption is based on the Credential's primary key
|
|
||||||
# (which has changed)
|
|
||||||
for field in ssh_type.defined_fields:
|
|
||||||
if field in ssh_type.secret_fields:
|
|
||||||
value = decrypt_field(cred, field)
|
|
||||||
if value:
|
|
||||||
setattr(new_cred, field, value)
|
|
||||||
new_cred.inputs[field] = encrypt_field(new_cred, field)
|
|
||||||
setattr(new_cred, field, '')
|
|
||||||
elif getattr(cred, field):
|
|
||||||
new_cred.inputs[field] = getattr(cred, field)
|
|
||||||
new_cred.save()
|
|
||||||
finally:
|
|
||||||
utils.get_current_apps = orig_current_apps
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_job_credentials(apps, schema_editor):
|
def migrate_job_credentials(apps, schema_editor):
|
||||||
# this monkey-patch is necessary to make the implicit role generation save
|
# TODO: remove once legacy/EOL'd Towers no longer support this upgrade path
|
||||||
# signal use the correct Role model (the version active at this point in
|
pass
|
||||||
# migration, not the one at HEAD)
|
|
||||||
orig_current_apps = utils.get_current_apps
|
|
||||||
try:
|
|
||||||
utils.get_current_apps = lambda: apps
|
|
||||||
for type_ in ('Job', 'JobTemplate'):
|
|
||||||
for obj in apps.get_model('main', type_).objects.all():
|
|
||||||
if obj.cloud_credential:
|
|
||||||
obj.extra_credentials.add(obj.cloud_credential)
|
|
||||||
if obj.network_credential:
|
|
||||||
obj.extra_credentials.add(obj.network_credential)
|
|
||||||
obj.save()
|
|
||||||
finally:
|
|
||||||
utils.get_current_apps = orig_current_apps
|
|
||||||
|
|
||||||
|
|
||||||
def add_vault_id_field(apps, schema_editor):
|
def add_vault_id_field(apps, schema_editor):
|
||||||
vault_credtype = CredentialType.objects.get(kind='vault')
|
# this is no longer necessary; schemas are defined in code
|
||||||
vault_credtype.inputs = CredentialType.defaults.get('vault')().inputs
|
pass
|
||||||
vault_credtype.save()
|
|
||||||
|
|
||||||
|
|
||||||
def remove_vault_id_field(apps, schema_editor):
|
def remove_vault_id_field(apps, schema_editor):
|
||||||
vault_credtype = CredentialType.objects.get(kind='vault')
|
# this is no longer necessary; schemas are defined in code
|
||||||
idx = 0
|
pass
|
||||||
for i, input in enumerate(vault_credtype.inputs['fields']):
|
|
||||||
if input['id'] == 'vault_id':
|
|
||||||
idx = i
|
|
||||||
break
|
|
||||||
vault_credtype.inputs['fields'].pop(idx)
|
|
||||||
vault_credtype.save()
|
|
||||||
|
|
||||||
|
|
||||||
def create_rhv_tower_credtype(apps, schema_editor):
|
def create_rhv_tower_credtype(apps, schema_editor):
|
||||||
CredentialType.setup_tower_managed_defaults()
|
# this is no longer necessary; schemas are defined in code
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def add_tower_verify_field(apps, schema_editor):
|
def add_tower_verify_field(apps, schema_editor):
|
||||||
tower_credtype = CredentialType.objects.get(
|
# this is no longer necessary; schemas are defined in code
|
||||||
kind='cloud', name='Ansible Tower', managed_by_tower=True
|
pass
|
||||||
)
|
|
||||||
tower_credtype.inputs = CredentialType.defaults.get('tower')().inputs
|
|
||||||
tower_credtype.save()
|
|
||||||
|
|
||||||
|
|
||||||
def add_azure_cloud_environment_field(apps, schema_editor):
|
def add_azure_cloud_environment_field(apps, schema_editor):
|
||||||
azure_rm_credtype = CredentialType.objects.get(kind='cloud',
|
# this is no longer necessary; schemas are defined in code
|
||||||
name='Microsoft Azure Resource Manager')
|
pass
|
||||||
azure_rm_credtype.inputs = CredentialType.defaults.get('azure_rm')().inputs
|
|
||||||
azure_rm_credtype.save()
|
|
||||||
|
|
||||||
|
|
||||||
def remove_become_methods(apps, schema_editor):
|
def remove_become_methods(apps, schema_editor):
|
||||||
become_credtype = CredentialType.objects.filter(kind='ssh', managed_by_tower=True).first()
|
# this is no longer necessary; schemas are defined in code
|
||||||
become_credtype.inputs = CredentialType.defaults.get('ssh')().inputs
|
pass
|
||||||
become_credtype.save()
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from awx.main.models.organization import ( # noqa
|
|||||||
Organization, Profile, Team, UserSessionMembership
|
Organization, Profile, Team, UserSessionMembership
|
||||||
)
|
)
|
||||||
from awx.main.models.credential import ( # noqa
|
from awx.main.models.credential import ( # noqa
|
||||||
Credential, CredentialType, V1Credential, build_safe_env
|
Credential, CredentialType, ManagedCredentialType, V1Credential, build_safe_env
|
||||||
)
|
)
|
||||||
from awx.main.models.projects import Project, ProjectUpdate # noqa
|
from awx.main.models.projects import Project, ProjectUpdate # noqa
|
||||||
from awx.main.models.inventory import ( # noqa
|
from awx.main.models.inventory import ( # noqa
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ __all__ = ['get_object_or_400', 'camelcase_to_underscore', 'memoize', 'memoize_d
|
|||||||
'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict',
|
'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict',
|
||||||
'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest',
|
'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest',
|
||||||
'has_model_field_prefetched', 'set_environ', 'IllegalArgumentError', 'get_custom_venv_choices', 'get_external_account',
|
'has_model_field_prefetched', 'set_environ', 'IllegalArgumentError', 'get_custom_venv_choices', 'get_external_account',
|
||||||
'task_manager_bulk_reschedule', 'schedule_task_manager']
|
'task_manager_bulk_reschedule', 'schedule_task_manager', 'classproperty']
|
||||||
|
|
||||||
|
|
||||||
def get_object_or_400(klass, *args, **kwargs):
|
def get_object_or_400(klass, *args, **kwargs):
|
||||||
@@ -1113,3 +1113,17 @@ def get_external_account(user):
|
|||||||
getattr(settings, 'TACACSPLUS_HOST', None)) and user.enterprise_auth.all():
|
getattr(settings, 'TACACSPLUS_HOST', None)) and user.enterprise_auth.all():
|
||||||
account_type = "enterprise"
|
account_type = "enterprise"
|
||||||
return account_type
|
return account_type
|
||||||
|
|
||||||
|
|
||||||
|
class classproperty:
|
||||||
|
|
||||||
|
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
||||||
|
self.fget = fget
|
||||||
|
self.fset = fset
|
||||||
|
self.fdel = fdel
|
||||||
|
if doc is None and fget is not None:
|
||||||
|
doc = fget.__doc__
|
||||||
|
self.__doc__ = doc
|
||||||
|
|
||||||
|
def __get__(self, instance, ownerclass):
|
||||||
|
return self.fget(ownerclass)
|
||||||
|
|||||||
Reference in New Issue
Block a user