mirror of
https://github.com/ansible/awx.git
synced 2026-03-26 13:25:02 -02:30
remove /api/v1 and deprecated credential fields
This commit is contained in:
@@ -16,7 +16,7 @@ from awx.main.models.organization import ( # noqa
|
||||
Organization, Profile, Team, UserSessionMembership
|
||||
)
|
||||
from awx.main.models.credential import ( # noqa
|
||||
Credential, CredentialType, CredentialInputSource, ManagedCredentialType, V1Credential, build_safe_env
|
||||
Credential, CredentialType, CredentialInputSource, ManagedCredentialType, build_safe_env
|
||||
)
|
||||
from awx.main.models.projects import Project, ProjectUpdate # noqa
|
||||
from awx.main.models.inventory import ( # noqa
|
||||
@@ -174,9 +174,6 @@ User.add_to_class('is_in_enterprise_category', user_is_in_enterprise_category)
|
||||
|
||||
|
||||
def o_auth2_application_get_absolute_url(self, request=None):
|
||||
# this page does not exist in v1
|
||||
if request.version == 'v1':
|
||||
return reverse('api:o_auth2_application_detail', kwargs={'pk': self.pk}) # use default version
|
||||
return reverse('api:o_auth2_application_detail', kwargs={'pk': self.pk}, request=request)
|
||||
|
||||
|
||||
@@ -184,9 +181,6 @@ OAuth2Application.add_to_class('get_absolute_url', o_auth2_application_get_absol
|
||||
|
||||
|
||||
def o_auth2_token_get_absolute_url(self, request=None):
|
||||
# this page does not exist in v1
|
||||
if request.version == 'v1':
|
||||
return reverse('api:o_auth2_token_detail', kwargs={'pk': self.pk}) # use default version
|
||||
return reverse('api:o_auth2_token_detail', kwargs={'pk': self.pk}, request=request)
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ from awx.main.models.rbac import (
|
||||
from awx.main.utils import encrypt_field
|
||||
from . import injectors as builtin_injectors
|
||||
|
||||
__all__ = ['Credential', 'CredentialType', 'CredentialInputSource', 'V1Credential', 'build_safe_env']
|
||||
__all__ = ['Credential', 'CredentialType', 'CredentialInputSource', 'build_safe_env']
|
||||
|
||||
logger = logging.getLogger('awx.main.models.credential')
|
||||
credential_plugins = dict(
|
||||
@@ -73,164 +73,6 @@ def build_safe_env(env):
|
||||
return safe_env
|
||||
|
||||
|
||||
class V1Credential(object):
|
||||
|
||||
#
|
||||
# API v1 backwards compat; as long as we continue to support the
|
||||
# /api/v1/credentials/ endpoint, we'll keep these definitions around.
|
||||
# The credential serializers are smart enough to detect the request
|
||||
# version and use *these* fields for constructing the serializer if the URL
|
||||
# starts with /api/v1/
|
||||
#
|
||||
PASSWORD_FIELDS = ('password', 'security_token', 'ssh_key_data',
|
||||
'ssh_key_unlock', 'become_password',
|
||||
'vault_password', 'secret', 'authorize_password')
|
||||
KIND_CHOICES = [
|
||||
('ssh', 'Machine'),
|
||||
('net', 'Network'),
|
||||
('scm', 'Source Control'),
|
||||
('aws', 'Amazon Web Services'),
|
||||
('vmware', 'VMware vCenter'),
|
||||
('satellite6', 'Red Hat Satellite 6'),
|
||||
('cloudforms', 'Red Hat CloudForms'),
|
||||
('gce', 'Google Compute Engine'),
|
||||
('azure_rm', 'Microsoft Azure Resource Manager'),
|
||||
('openstack', 'OpenStack'),
|
||||
('rhv', 'Red Hat Virtualization'),
|
||||
('insights', 'Insights'),
|
||||
('tower', 'Ansible Tower'),
|
||||
]
|
||||
FIELDS = {
|
||||
'kind': models.CharField(
|
||||
max_length=32,
|
||||
choices=[
|
||||
(kind[0], _(kind[1]))
|
||||
for kind in KIND_CHOICES
|
||||
],
|
||||
default='ssh',
|
||||
),
|
||||
'cloud': models.BooleanField(
|
||||
default=False,
|
||||
editable=False,
|
||||
),
|
||||
'host': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=1024,
|
||||
verbose_name=_('Host'),
|
||||
help_text=_('The hostname or IP address to use.'),
|
||||
),
|
||||
'username': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=1024,
|
||||
verbose_name=_('Username'),
|
||||
help_text=_('Username for this credential.'),
|
||||
),
|
||||
'password': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=1024,
|
||||
verbose_name=_('Password'),
|
||||
help_text=_('Password for this credential (or "ASK" to prompt the '
|
||||
'user for machine credentials).'),
|
||||
),
|
||||
'security_token': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=1024,
|
||||
verbose_name=_('Security Token'),
|
||||
help_text=_('Security Token for this credential'),
|
||||
),
|
||||
'project': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=100,
|
||||
verbose_name=_('Project'),
|
||||
help_text=_('The identifier for the project.'),
|
||||
),
|
||||
'domain': models.CharField(
|
||||
blank=True,
|
||||
default='',
|
||||
max_length=100,
|
||||
verbose_name=_('Domain'),
|
||||
help_text=_('The identifier for the domain.'),
|
||||
),
|
||||
'ssh_key_data': models.TextField(
|
||||
blank=True,
|
||||
default='',
|
||||
verbose_name=_('SSH private key'),
|
||||
help_text=_('RSA or DSA private key to be used instead of password.'),
|
||||
),
|
||||
'ssh_key_unlock': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
verbose_name=_('SSH key unlock'),
|
||||
help_text=_('Passphrase to unlock SSH private key if encrypted (or '
|
||||
'"ASK" to prompt the user for machine credentials).'),
|
||||
),
|
||||
'become_method': models.CharField(
|
||||
max_length=32,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Privilege escalation method.')
|
||||
),
|
||||
'become_username': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Privilege escalation username.'),
|
||||
),
|
||||
'become_password': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Password for privilege escalation method.')
|
||||
),
|
||||
'vault_password': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Vault password (or "ASK" to prompt the user).'),
|
||||
),
|
||||
'authorize': models.BooleanField(
|
||||
default=False,
|
||||
help_text=_('Whether to use the authorize mechanism.'),
|
||||
),
|
||||
'authorize_password': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Password used by the authorize mechanism.'),
|
||||
),
|
||||
'client': models.CharField(
|
||||
max_length=128,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Client Id or Application Id for the credential'),
|
||||
),
|
||||
'secret': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Secret Token for this credential'),
|
||||
),
|
||||
'subscription': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Subscription identifier for this credential'),
|
||||
),
|
||||
'tenant': models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=_('Tenant identifier for this credential'),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
'''
|
||||
A credential contains information about how to talk to a remote resource
|
||||
@@ -286,34 +128,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
'admin_role',
|
||||
])
|
||||
|
||||
def __getattr__(self, item):
|
||||
if item != 'inputs':
|
||||
if item in V1Credential.FIELDS:
|
||||
return self.inputs.get(item, V1Credential.FIELDS[item].default)
|
||||
elif item in self.inputs:
|
||||
return self.inputs[item]
|
||||
raise AttributeError(item)
|
||||
|
||||
def __setattr__(self, item, value):
|
||||
if item in V1Credential.FIELDS and item in self.credential_type.defined_fields:
|
||||
if value:
|
||||
self.inputs[item] = value
|
||||
elif item in self.inputs:
|
||||
del self.inputs[item]
|
||||
return
|
||||
super(Credential, self).__setattr__(item, value)
|
||||
|
||||
@property
|
||||
def kind(self):
|
||||
# TODO 3.3: remove the need for this helper property by removing its
|
||||
# usage throughout the codebase
|
||||
type_ = self.credential_type
|
||||
if type_.kind != 'cloud':
|
||||
return type_.kind
|
||||
for field in V1Credential.KIND_CHOICES:
|
||||
kind, name = field
|
||||
if name == type_.name:
|
||||
return kind
|
||||
return self.credential_type.namespace
|
||||
|
||||
@property
|
||||
def cloud(self):
|
||||
@@ -330,7 +147,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
#
|
||||
@property
|
||||
def needs_ssh_password(self):
|
||||
return self.credential_type.kind == 'ssh' and self.password == 'ASK'
|
||||
return self.credential_type.kind == 'ssh' and self.inputs.get('password') == 'ASK'
|
||||
|
||||
@property
|
||||
def has_encrypted_ssh_key_data(self):
|
||||
@@ -350,17 +167,17 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
|
||||
@property
|
||||
def needs_ssh_key_unlock(self):
|
||||
if self.credential_type.kind == 'ssh' and self.ssh_key_unlock in ('ASK', ''):
|
||||
if self.credential_type.kind == 'ssh' and self.inputs.get('ssh_key_unlock') in ('ASK', ''):
|
||||
return self.has_encrypted_ssh_key_data
|
||||
return False
|
||||
|
||||
@property
|
||||
def needs_become_password(self):
|
||||
return self.credential_type.kind == 'ssh' and self.become_password == 'ASK'
|
||||
return self.credential_type.kind == 'ssh' and self.inputs.get('become_password') == 'ASK'
|
||||
|
||||
@property
|
||||
def needs_vault_password(self):
|
||||
return self.credential_type.kind == 'vault' and self.vault_password == 'ASK'
|
||||
return self.credential_type.kind == 'vault' and self.inputs.get('vault_password') == 'ASK'
|
||||
|
||||
@property
|
||||
def passwords_needed(self):
|
||||
@@ -396,6 +213,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
|
||||
super(Credential, self).save(*args, **kwargs)
|
||||
|
||||
def mark_field_for_save(self, update_fields, field):
|
||||
if 'inputs' not in update_fields:
|
||||
update_fields.append('inputs')
|
||||
|
||||
def encrypt_field(self, field, ask):
|
||||
if field not in self.inputs:
|
||||
return None
|
||||
@@ -405,13 +226,6 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
elif field in self.inputs:
|
||||
del self.inputs[field]
|
||||
|
||||
def mark_field_for_save(self, update_fields, field):
|
||||
if field in self.credential_type.secret_fields:
|
||||
# If we've encrypted a v1 field, we actually want to persist
|
||||
# self.inputs
|
||||
field = 'inputs'
|
||||
super(Credential, self).mark_field_for_save(update_fields, field)
|
||||
|
||||
def display_inputs(self):
|
||||
field_val = self.inputs.copy()
|
||||
for k, v in field_val.items():
|
||||
@@ -429,7 +243,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
type_alias = self.credential_type.name
|
||||
else:
|
||||
type_alias = self.credential_type_id
|
||||
if self.kind == 'vault' and self.has_input('vault_id'):
|
||||
if self.credential_type.kind == 'vault' and self.has_input('vault_id'):
|
||||
if display:
|
||||
fmt_str = '{} (id={})'
|
||||
else:
|
||||
@@ -456,7 +270,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
:param field_name(str): The name of the input field.
|
||||
:param default(optional[str]): A default return value to use.
|
||||
"""
|
||||
if self.kind != 'external' and field_name in self.dynamic_input_fields:
|
||||
if self.credential_type.kind != 'external' and field_name in self.dynamic_input_fields:
|
||||
return self._get_dynamic_input(field_name)
|
||||
if field_name in self.credential_type.secret_fields:
|
||||
try:
|
||||
@@ -552,15 +366,8 @@ class CredentialType(CommonModelNameNotUnique):
|
||||
return instance
|
||||
|
||||
def get_absolute_url(self, request=None):
|
||||
# Page does not exist in API v1
|
||||
if request.version == 'v1':
|
||||
return reverse('api:credential_type_detail', kwargs={'pk': self.pk})
|
||||
return reverse('api:credential_type_detail', kwargs={'pk': self.pk}, request=request)
|
||||
|
||||
@property
|
||||
def unique_by_kind(self):
|
||||
return self.kind != 'cloud'
|
||||
|
||||
@property
|
||||
def defined_fields(self):
|
||||
return [field.get('id') for field in self.inputs.get('fields', [])]
|
||||
@@ -629,29 +436,6 @@ class CredentialType(CommonModelNameNotUnique):
|
||||
inputs=plugin.inputs
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_v1_kind(cls, kind, data={}):
|
||||
match = None
|
||||
kind = kind or 'ssh'
|
||||
kind_choices = dict(V1Credential.KIND_CHOICES)
|
||||
requirements = {}
|
||||
if kind == 'ssh':
|
||||
if data.get('vault_password'):
|
||||
requirements['kind'] = 'vault'
|
||||
else:
|
||||
requirements['kind'] = 'ssh'
|
||||
elif kind in ('net', 'scm', 'insights'):
|
||||
requirements['kind'] = kind
|
||||
elif kind in kind_choices:
|
||||
requirements.update(dict(
|
||||
kind='cloud',
|
||||
name=kind_choices[kind]
|
||||
))
|
||||
if requirements:
|
||||
requirements['managed_by_tower'] = True
|
||||
match = cls.objects.filter(**requirements)[:1].get()
|
||||
return match
|
||||
|
||||
def inject_credential(self, credential, env, safe_env, args, private_data_dir):
|
||||
"""
|
||||
Inject credential data into the environment variables and arguments
|
||||
@@ -678,9 +462,11 @@ class CredentialType(CommonModelNameNotUnique):
|
||||
files)
|
||||
"""
|
||||
if not self.injectors:
|
||||
if self.managed_by_tower and credential.kind in dir(builtin_injectors):
|
||||
if self.managed_by_tower and credential.credential_type.namespace in dir(builtin_injectors):
|
||||
injected_env = {}
|
||||
getattr(builtin_injectors, credential.kind)(credential, injected_env, private_data_dir)
|
||||
getattr(builtin_injectors, credential.credential_type.namespace)(
|
||||
credential, injected_env, private_data_dir
|
||||
)
|
||||
env.update(injected_env)
|
||||
safe_env.update(build_safe_env(injected_env))
|
||||
return
|
||||
@@ -1335,12 +1121,12 @@ class CredentialInputSource(PrimordialModel):
|
||||
)
|
||||
|
||||
def clean_target_credential(self):
|
||||
if self.target_credential.kind == 'external':
|
||||
if self.target_credential.credential_type.kind == 'external':
|
||||
raise ValidationError(_('Target must be a non-external credential'))
|
||||
return self.target_credential
|
||||
|
||||
def clean_source_credential(self):
|
||||
if self.source_credential.kind != 'external':
|
||||
if self.source_credential.credential_type.kind != 'external':
|
||||
raise ValidationError(_('Source must be an external credential'))
|
||||
return self.source_credential
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from django.db import models
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError, FieldDoesNotExist
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
|
||||
# REST Framework
|
||||
from rest_framework.exceptions import ParseError
|
||||
@@ -152,21 +152,9 @@ class JobOptions(BaseModel):
|
||||
|
||||
extra_vars_dict = VarsDictProperty('extra_vars', True)
|
||||
|
||||
def clean_credential(self):
|
||||
cred = self.credential
|
||||
if cred and cred.kind != 'ssh':
|
||||
raise ValidationError(
|
||||
_('You must provide an SSH credential.'),
|
||||
)
|
||||
return cred
|
||||
|
||||
def clean_vault_credential(self):
|
||||
cred = self.vault_credential
|
||||
if cred and cred.kind != 'vault':
|
||||
raise ValidationError(
|
||||
_('You must provide a Vault credential.'),
|
||||
)
|
||||
return cred
|
||||
@property
|
||||
def machine_credential(self):
|
||||
return self.credentials.filter(credential_type__kind='ssh').first()
|
||||
|
||||
@property
|
||||
def network_credentials(self):
|
||||
@@ -180,41 +168,6 @@ class JobOptions(BaseModel):
|
||||
def vault_credentials(self):
|
||||
return list(self.credentials.filter(credential_type__kind='vault'))
|
||||
|
||||
@property
|
||||
def credential(self):
|
||||
cred = self.get_deprecated_credential('ssh')
|
||||
if cred is not None:
|
||||
return cred.pk
|
||||
|
||||
@property
|
||||
def vault_credential(self):
|
||||
cred = self.get_deprecated_credential('vault')
|
||||
if cred is not None:
|
||||
return cred.pk
|
||||
|
||||
def get_deprecated_credential(self, kind):
|
||||
for cred in self.credentials.all():
|
||||
if cred.credential_type.kind == kind:
|
||||
return cred
|
||||
else:
|
||||
return None
|
||||
|
||||
# TODO: remove when API v1 is removed
|
||||
@property
|
||||
def cloud_credential(self):
|
||||
try:
|
||||
return self.cloud_credentials[-1].pk
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
# TODO: remove when API v1 is removed
|
||||
@property
|
||||
def network_credential(self):
|
||||
try:
|
||||
return self.network_credentials[-1].pk
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def passwords_needed_to_start(self):
|
||||
'''Return list of password field names needed to start the job.'''
|
||||
@@ -707,7 +660,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
||||
data.update(dict(inventory=self.inventory.name if self.inventory else None,
|
||||
project=self.project.name if self.project else None,
|
||||
playbook=self.playbook,
|
||||
credential=getattr(self.get_deprecated_credential('ssh'), 'name', None),
|
||||
credential=getattr(self.machine_credential, 'name', None),
|
||||
limit=self.limit,
|
||||
extra_vars=self.display_extra_vars(),
|
||||
hosts=all_hosts))
|
||||
|
||||
Reference in New Issue
Block a user