mirror of
https://github.com/ansible/awx.git
synced 2026-01-22 23:18:03 -03:30
338 lines
11 KiB
Python
338 lines
11 KiB
Python
import logging
|
|
|
|
|
|
from awxkit.utils import (
|
|
cloud_types,
|
|
filter_by_class,
|
|
not_provided,
|
|
random_title,
|
|
update_payload,
|
|
PseudoNamespace)
|
|
from awxkit.api.pages import Organization, User, Team
|
|
from awxkit.api.mixins import HasCreate, HasCopy, DSAdapter
|
|
from awxkit.api.resources import resources
|
|
from awxkit.config import config
|
|
|
|
from . import base
|
|
from . import page
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
credential_input_fields = (
|
|
'authorize_password',
|
|
'become_method',
|
|
'become_password',
|
|
'become_username',
|
|
'client',
|
|
'cloud_environment',
|
|
'domain',
|
|
'host',
|
|
'password',
|
|
'project_id',
|
|
'project_name',
|
|
'secret',
|
|
'ssh_key_data',
|
|
'ssh_key_unlock',
|
|
'subscription',
|
|
'tenant',
|
|
'username',
|
|
'vault_password',
|
|
'vault_id')
|
|
|
|
|
|
def generate_private_key():
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
|
key = rsa.generate_private_key(
|
|
public_exponent=65537,
|
|
key_size=4096,
|
|
backend=default_backend()
|
|
)
|
|
return key.private_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
encryption_algorithm=serialization.NoEncryption()
|
|
).decode('utf-8')
|
|
|
|
|
|
def config_cred_from_kind(kind):
|
|
try:
|
|
if kind == 'net':
|
|
config_cred = config.credentials.network
|
|
elif kind in cloud_types:
|
|
if kind == 'azure_rm':
|
|
config_cred = config.credentials.cloud.azure
|
|
else:
|
|
config_cred = config.credentials.cloud[kind]
|
|
else:
|
|
config_cred = config.credentials[kind]
|
|
return config_cred
|
|
except (KeyError, AttributeError):
|
|
return PseudoNamespace()
|
|
|
|
|
|
credential_type_name_to_config_kind_map = {
|
|
'amazon web services': 'aws',
|
|
'ansible tower': 'tower',
|
|
'google compute engine': 'gce',
|
|
'insights': 'insights',
|
|
'openshift or kubernetes api bearer token': 'kubernetes',
|
|
'microsoft azure classic (deprecated)': 'azure_classic',
|
|
'microsoft azure resource manager': 'azure_rm',
|
|
'network': 'net',
|
|
'openstack': 'OpenStack',
|
|
'red hat virtualization': 'rhv',
|
|
'red hat cloudforms': 'cloudforms',
|
|
'red hat satellite 6': 'satellite6',
|
|
'source control': 'scm',
|
|
'machine': 'ssh',
|
|
'vault': 'vault',
|
|
'vmware vcenter': 'vmware'}
|
|
|
|
config_kind_to_credential_type_name_map = {
|
|
kind: name
|
|
for name, kind in credential_type_name_to_config_kind_map.items()}
|
|
|
|
|
|
def kind_and_config_cred_from_credential_type(credential_type):
|
|
kind = ''
|
|
|
|
if not credential_type.managed_by_tower:
|
|
return kind, PseudoNamespace()
|
|
try:
|
|
if credential_type.kind == 'net':
|
|
config_cred = config.credentials.network
|
|
kind = 'net'
|
|
elif credential_type.kind == 'cloud':
|
|
kind = credential_type_name_to_config_kind_map[credential_type.name.lower(
|
|
)]
|
|
config_kind = kind if kind != 'azure_rm' else 'azure'
|
|
config_cred = config.credentials.cloud[config_kind]
|
|
else:
|
|
kind = credential_type.kind.lower()
|
|
config_cred = config.credentials[kind]
|
|
return kind, config_cred
|
|
except (KeyError, AttributeError):
|
|
return kind, PseudoNamespace()
|
|
|
|
|
|
def get_payload_field_and_value_from_kwargs_or_config_cred(
|
|
field, kind, kwargs, config_cred):
|
|
if field in (
|
|
'project_id',
|
|
'project_name'): # Needed to prevent Project kwarg collision
|
|
config_field = 'project'
|
|
elif field == 'subscription' and 'azure' in kind:
|
|
config_field = 'subscription_id'
|
|
elif field == 'username' and kind == 'azure_ad':
|
|
config_field = 'ad_user'
|
|
elif field == 'client':
|
|
config_field = 'client_id'
|
|
elif field == 'authorize_password':
|
|
config_field = 'authorize'
|
|
else:
|
|
config_field = field
|
|
value = kwargs.get(field, config_cred.get(config_field, not_provided))
|
|
if field in ('project_id', 'project_name'):
|
|
field = 'project'
|
|
return field, value
|
|
|
|
|
|
class CredentialType(HasCreate, base.Base):
|
|
|
|
def silent_delete(self):
|
|
if not self.managed_by_tower:
|
|
return super(CredentialType, self).silent_delete()
|
|
|
|
def payload(self, kind='cloud', **kwargs):
|
|
payload = PseudoNamespace(
|
|
name=kwargs.get('name') or 'CredentialType - {}'.format(
|
|
random_title()),
|
|
description=kwargs.get('description') or random_title(10),
|
|
kind=kind)
|
|
fields = ('inputs', 'injectors')
|
|
update_payload(payload, fields, kwargs)
|
|
return payload
|
|
|
|
def create_payload(self, kind='cloud', **kwargs):
|
|
payload = self.payload(kind=kind, **kwargs)
|
|
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
|
|
return payload
|
|
|
|
def create(self, kind='cloud', **kwargs):
|
|
payload = self.create_payload(kind=kind, **kwargs)
|
|
return self.update_identity(
|
|
CredentialTypes(
|
|
self.connection).post(payload))
|
|
|
|
|
|
page.register_page([resources.credential_type,
|
|
(resources.credential_types, 'post')], CredentialType)
|
|
|
|
|
|
class CredentialTypes(page.PageList, CredentialType):
|
|
|
|
pass
|
|
|
|
|
|
page.register_page(resources.credential_types, CredentialTypes)
|
|
|
|
|
|
class Credential(HasCopy, HasCreate, base.Base):
|
|
|
|
dependencies = [CredentialType]
|
|
optional_dependencies = [Organization, User, Team]
|
|
|
|
def payload(
|
|
self,
|
|
credential_type,
|
|
user=None,
|
|
team=None,
|
|
organization=None,
|
|
inputs=None,
|
|
**kwargs):
|
|
if not any((user, team, organization)):
|
|
raise TypeError(
|
|
'{0.__class__.__name__} requires user, team, and/or organization instances.'.format(self))
|
|
|
|
if inputs is None:
|
|
inputs = {}
|
|
|
|
payload = PseudoNamespace(
|
|
name=kwargs.get('name') or 'Credential - {}'.format(
|
|
random_title()),
|
|
description=kwargs.get('description') or random_title(10),
|
|
credential_type=credential_type.id,
|
|
inputs=inputs)
|
|
if user:
|
|
payload.user = user.id
|
|
if team:
|
|
payload.team = team.id
|
|
if organization:
|
|
payload.organization = organization.id
|
|
|
|
kind, config_cred = kind_and_config_cred_from_credential_type(
|
|
credential_type)
|
|
|
|
for field in credential_input_fields:
|
|
field, value = get_payload_field_and_value_from_kwargs_or_config_cred(
|
|
field, kind, inputs or kwargs, config_cred)
|
|
if value != not_provided:
|
|
payload.inputs[field] = value
|
|
|
|
if kind == 'net':
|
|
payload.inputs.authorize = inputs.get(
|
|
'authorize', bool(inputs.get('authorize_password')))
|
|
|
|
if kind in ('ssh', 'net') and 'ssh_key_data' not in payload.inputs:
|
|
payload.inputs.ssh_key_data = inputs.get(
|
|
'ssh_key_data', generate_private_key())
|
|
|
|
return payload
|
|
|
|
def create_payload(
|
|
self,
|
|
credential_type=CredentialType,
|
|
user=None,
|
|
team=None,
|
|
organization=Organization,
|
|
inputs=None,
|
|
**kwargs):
|
|
if isinstance(credential_type, int):
|
|
# if an int was passed, it is assumed to be the pk id of a
|
|
# credential type
|
|
credential_type = CredentialTypes(
|
|
self.connection).get(id=credential_type).results.pop()
|
|
|
|
if credential_type == CredentialType:
|
|
kind = kwargs.pop('kind', 'ssh')
|
|
if kind in ('openstack', 'openstack_v3'):
|
|
credential_type_name = 'OpenStack'
|
|
if inputs is None:
|
|
if kind == 'openstack_v3':
|
|
inputs = config.credentials.cloud['openstack_v3']
|
|
else:
|
|
inputs = config.credentials.cloud['openstack']
|
|
else:
|
|
credential_type_name = config_kind_to_credential_type_name_map[kind]
|
|
credential_type = CredentialTypes(
|
|
self.connection).get(
|
|
managed_by_tower=True,
|
|
name__icontains=credential_type_name).results.pop()
|
|
|
|
credential_type, organization, user, team = filter_by_class(
|
|
(credential_type, CredentialType), (organization, Organization), (user, User), (team, Team))
|
|
if not any((user, team, organization)):
|
|
organization = Organization
|
|
self.create_and_update_dependencies(
|
|
credential_type, organization, user, team)
|
|
user = self.ds.user if user else None
|
|
team = self.ds.team if team else None
|
|
organization = self.ds.organization if organization else None
|
|
|
|
payload = self.payload(
|
|
self.ds.credential_type,
|
|
user=user,
|
|
team=team,
|
|
organization=organization,
|
|
inputs=inputs,
|
|
**kwargs)
|
|
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
|
|
return payload
|
|
|
|
def create(
|
|
self,
|
|
credential_type=CredentialType,
|
|
user=None,
|
|
team=None,
|
|
organization=Organization,
|
|
inputs=None,
|
|
**kwargs):
|
|
payload = self.create_payload(
|
|
credential_type=credential_type,
|
|
user=user,
|
|
team=team,
|
|
organization=organization,
|
|
inputs=inputs,
|
|
**kwargs)
|
|
return self.update_identity(
|
|
Credentials(
|
|
self.connection)).post(payload)
|
|
|
|
@property
|
|
def expected_passwords_needed_to_start(self):
|
|
"""Return a list of expected passwords needed to start a job using this credential."""
|
|
passwords = []
|
|
for field in (
|
|
'password',
|
|
'become_password',
|
|
'ssh_key_unlock',
|
|
'vault_password'):
|
|
if getattr(self.inputs, field, None) == 'ASK':
|
|
if field == 'password':
|
|
passwords.append('ssh_password')
|
|
else:
|
|
passwords.append(field)
|
|
return passwords
|
|
|
|
|
|
page.register_page([resources.credential,
|
|
(resources.credentials, 'post'),
|
|
(resources.credential_copy, 'post')], Credential)
|
|
|
|
|
|
class Credentials(page.PageList, Credential):
|
|
|
|
pass
|
|
|
|
|
|
page.register_page([resources.credentials,
|
|
resources.related_credentials,
|
|
resources.job_extra_credentials,
|
|
resources.job_template_extra_credentials],
|
|
Credentials)
|