mirror of
https://github.com/ansible/awx.git
synced 2026-02-04 02:58:13 -03:30
raise 400 error on removal of credential on launch
Definition of removal is providing a `credentials` list on launch that lacks a type of credential that the job template has. This assures that every category of credential the job template has will also exist on jobs ran from that job template. This restriction already existed, but this makes the endpoint fail instead of re-adding the credentials. This change makes manual launch congruent with saved launch configurations.
This commit is contained in:
@@ -3721,15 +3721,30 @@ class JobLaunchSerializer(BaseSerializer):
|
||||
distinct_cred_kinds = []
|
||||
for cred in accepted.get('credentials', []):
|
||||
if cred.unique_hash() in distinct_cred_kinds:
|
||||
errors['credentials'] = _('Cannot assign multiple %s credentials.' % cred.credential_type.name)
|
||||
errors.setdefault('credentials', []).append(_(
|
||||
'Cannot assign multiple {} credentials.'
|
||||
).format(cred.unique_hash(display=True)))
|
||||
distinct_cred_kinds.append(cred.unique_hash())
|
||||
|
||||
# Prohibit removing credentials from the JT list (unsupported for now)
|
||||
template_credentials = template.credentials.all()
|
||||
if 'credentials' in attrs:
|
||||
removed_creds = set(template_credentials) - set(attrs['credentials'])
|
||||
provided_mapping = Credential.unique_dict(attrs['credentials'])
|
||||
for cred in removed_creds:
|
||||
if cred.unique_hash() in provided_mapping.keys():
|
||||
continue # User replaced credential with new of same type
|
||||
errors.setdefault('credentials', []).append(_(
|
||||
'Removing {} credential at launch time without replacement is not supported. '
|
||||
'Provided list lacked credential(s): {}.'
|
||||
).format(cred.unique_hash(display=True), ', '.join([str(c) for c in removed_creds])))
|
||||
|
||||
# verify that credentials (either provided or existing) don't
|
||||
# require launch-time passwords that have not been provided
|
||||
if 'credentials' in accepted:
|
||||
launch_credentials = accepted['credentials']
|
||||
else:
|
||||
launch_credentials = template.credentials.all()
|
||||
launch_credentials = template_credentials
|
||||
passwords = attrs.get('credential_passwords', {}) # get from original attrs
|
||||
passwords_lacking = []
|
||||
for cred in launch_credentials:
|
||||
|
||||
@@ -2884,6 +2884,7 @@ class JobTemplateLaunch(RetrieveAPIView):
|
||||
):
|
||||
# make a list of the current credentials
|
||||
existing_credentials = obj.credentials.all()
|
||||
template_credentials = list(existing_credentials) # save copy of existing
|
||||
new_credentials = []
|
||||
for key, conditional in (
|
||||
('credential', lambda cred: cred.credential_type.kind != 'ssh'),
|
||||
@@ -2910,6 +2911,11 @@ class JobTemplateLaunch(RetrieveAPIView):
|
||||
# combine the list of "new" and the filtered list of "old"
|
||||
new_credentials.extend([cred.pk for cred in existing_credentials])
|
||||
if new_credentials:
|
||||
# If provided list doesn't contain the pre-existing credentials
|
||||
# defined on the template, add them back here
|
||||
for cred_obj in template_credentials:
|
||||
if cred_obj.pk not in new_credentials:
|
||||
new_credentials.append(cred_obj.pk)
|
||||
modern_data['credentials'] = new_credentials
|
||||
|
||||
# credential passwords were historically provided as top-level attributes
|
||||
|
||||
@@ -283,7 +283,7 @@ def test_job_launch_JT_with_validation(machine_credential, credential, deploy_jo
|
||||
deploy_jobtemplate.ask_variables_on_launch = True
|
||||
deploy_jobtemplate.save()
|
||||
|
||||
kv = dict(extra_vars={"job_launch_var": 4}, credentials=[machine_credential.pk])
|
||||
kv = dict(extra_vars={"job_launch_var": 4}, credentials=[machine_credential.pk, credential.pk])
|
||||
serializer = JobLaunchSerializer(data=kv, context={'template': deploy_jobtemplate})
|
||||
validated = serializer.is_valid()
|
||||
assert validated, serializer.errors
|
||||
@@ -338,16 +338,17 @@ def test_job_launch_JT_enforces_unique_credentials_kinds(machine_credential, cre
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_launch_with_empty_creds(machine_credential, vault_credential, deploy_jobtemplate):
|
||||
def test_job_launch_with_empty_creds(machine_credential, vault_credential, deploy_jobtemplate, credential):
|
||||
deploy_jobtemplate.ask_credential_on_launch = True
|
||||
deploy_jobtemplate.credentials.add(machine_credential)
|
||||
deploy_jobtemplate.credentials.add(vault_credential)
|
||||
kv = dict(credentials=[])
|
||||
# `credentials` list is strictly those already present on deploy_jobtemplate
|
||||
kv = dict(credentials=[credential.pk, machine_credential.pk, vault_credential.pk])
|
||||
serializer = JobLaunchSerializer(data=kv, context={'template': deploy_jobtemplate})
|
||||
validated = serializer.is_valid()
|
||||
assert validated
|
||||
assert validated, serializer.errors
|
||||
|
||||
prompted_fields, ignored_fields, errors = deploy_jobtemplate._accept_or_ignore_job_kwargs(**kv)
|
||||
prompted_fields, ignored_fields, errors = deploy_jobtemplate._accept_or_ignore_job_kwargs(**serializer.validated_data)
|
||||
job_obj = deploy_jobtemplate.create_unified_job(**prompted_fields)
|
||||
assert job_obj.credential is deploy_jobtemplate.credential
|
||||
assert job_obj.vault_credential is deploy_jobtemplate.vault_credential
|
||||
|
||||
Reference in New Issue
Block a user