mirror of
https://github.com/ansible/awx.git
synced 2026-03-09 05:29:26 -02:30
Omit placeholder vars with survey password defaults
WFJT nodes & schedules (launch configs) will accept POST/PATCH/PUT with variables in extra_data that have $encrypted$ for their value if a valid survey default exists. In this case, the variable is simply removed from the extra_data. This is done so that it does not affect pre-existing value substitution for $encrypted$ values from the config itself
This commit is contained in:
@@ -3150,19 +3150,27 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
|
|||||||
elif self.instance:
|
elif self.instance:
|
||||||
ujt = self.instance.unified_job_template
|
ujt = self.instance.unified_job_template
|
||||||
|
|
||||||
# Insert survey_passwords to track redacted variables
|
# Replace $encrypted$ submissions with db value if exists
|
||||||
|
# build additional field survey_passwords to track redacted variables
|
||||||
if 'extra_data' in attrs:
|
if 'extra_data' in attrs:
|
||||||
extra_data = parse_yaml_or_json(attrs.get('extra_data', {}))
|
extra_data = parse_yaml_or_json(attrs.get('extra_data', {}))
|
||||||
if hasattr(ujt, 'survey_password_variables'):
|
if hasattr(ujt, 'survey_password_variables'):
|
||||||
|
# Prepare additional field survey_passwords for save
|
||||||
password_dict = {}
|
password_dict = {}
|
||||||
for key in ujt.survey_password_variables():
|
for key in ujt.survey_password_variables():
|
||||||
if key in extra_data:
|
if key in extra_data:
|
||||||
password_dict[key] = REPLACE_STR
|
password_dict[key] = REPLACE_STR
|
||||||
if not self.instance or password_dict != self.instance.survey_passwords:
|
if not self.instance or password_dict != self.instance.survey_passwords:
|
||||||
attrs['survey_passwords'] = password_dict
|
attrs['survey_passwords'] = password_dict.copy()
|
||||||
|
# Force dict type (cannot preserve YAML formatting if passwords are involved)
|
||||||
if not isinstance(attrs['extra_data'], dict):
|
if not isinstance(attrs['extra_data'], dict):
|
||||||
attrs['extra_data'] = parse_yaml_or_json(attrs['extra_data'])
|
attrs['extra_data'] = parse_yaml_or_json(attrs['extra_data'])
|
||||||
|
# Encrypt the extra_data for save, only current password vars in JT survey
|
||||||
encrypt_dict(attrs['extra_data'], password_dict.keys())
|
encrypt_dict(attrs['extra_data'], password_dict.keys())
|
||||||
|
# For any raw $encrypted$ string, either
|
||||||
|
# - replace with existing DB value
|
||||||
|
# - raise a validation error
|
||||||
|
# - remove key from extra_data if survey default is present
|
||||||
if self.instance:
|
if self.instance:
|
||||||
db_extra_data = parse_yaml_or_json(self.instance.extra_data)
|
db_extra_data = parse_yaml_or_json(self.instance.extra_data)
|
||||||
else:
|
else:
|
||||||
@@ -3170,8 +3178,13 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
|
|||||||
for key in password_dict.keys():
|
for key in password_dict.keys():
|
||||||
if attrs['extra_data'].get(key, None) == REPLACE_STR:
|
if attrs['extra_data'].get(key, None) == REPLACE_STR:
|
||||||
if key not in db_extra_data:
|
if key not in db_extra_data:
|
||||||
raise serializers.ValidationError(
|
element = ujt.pivot_spec(ujt.survey_spec)[key]
|
||||||
_('Provided variable {} has no database value to replace with.').format(key))
|
if 'default' in element and element['default']:
|
||||||
|
attrs['survey_passwords'].pop(key, None)
|
||||||
|
attrs['extra_data'].pop(key, None)
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
{"extra_data": _('Provided variable {} has no database value to replace with.').format(key)})
|
||||||
else:
|
else:
|
||||||
attrs['extra_data'][key] = db_extra_data[key]
|
attrs['extra_data'][key] = db_extra_data[key]
|
||||||
|
|
||||||
|
|||||||
@@ -215,6 +215,24 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords():
|
|||||||
assert 'var1' in attrs['survey_passwords']
|
assert 'var1' in attrs['survey_passwords']
|
||||||
assert attrs['extra_data']['var1'] == '$encrypted$foooooo'
|
assert attrs['extra_data']['var1'] == '$encrypted$foooooo'
|
||||||
|
|
||||||
|
def test_accept_password_default(self, jt, mocker):
|
||||||
|
'''
|
||||||
|
If user provides "$encrypted$" without a corresponding DB value for the
|
||||||
|
node, but survey question has a default, then variables are accepted
|
||||||
|
with that particular var omitted so on launch time the default takes effect
|
||||||
|
'''
|
||||||
|
serializer = WorkflowJobTemplateNodeSerializer()
|
||||||
|
wfjt = WorkflowJobTemplate(name='fake-wfjt')
|
||||||
|
jt.survey_spec['spec'][0]['default'] = '$encrypted$bar'
|
||||||
|
attrs = serializer.validate({
|
||||||
|
'unified_job_template': jt,
|
||||||
|
'workflow_job_template': wfjt,
|
||||||
|
'extra_data': {'var1': '$encrypted$'}
|
||||||
|
})
|
||||||
|
assert 'survey_passwords' in attrs
|
||||||
|
assert attrs['survey_passwords'] == {}
|
||||||
|
assert attrs['extra_data'] == {}
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('awx.api.serializers.WorkflowJobTemplateNodeSerializer.get_related', lambda x,y: {})
|
@mock.patch('awx.api.serializers.WorkflowJobTemplateNodeSerializer.get_related', lambda x,y: {})
|
||||||
class TestWorkflowJobNodeSerializerGetRelated():
|
class TestWorkflowJobNodeSerializerGetRelated():
|
||||||
|
|||||||
@@ -240,6 +240,9 @@ of what happened.
|
|||||||
- survey password durability
|
- survey password durability
|
||||||
- schedule has survey password answers from WFJT survey
|
- schedule has survey password answers from WFJT survey
|
||||||
- WFJT node has answers to different password questions from JT survey
|
- WFJT node has answers to different password questions from JT survey
|
||||||
|
- Saving with "$encrypted$" value will either
|
||||||
|
- become a no-op, removing the key if a valid question default exists
|
||||||
|
- replace with the database value if question was previously answered
|
||||||
- final job it spawns has both answers encrypted
|
- final job it spawns has both answers encrypted
|
||||||
- POST to associate credential to WFJT node
|
- POST to associate credential to WFJT node
|
||||||
- requires admin to WFJT and execute to JT
|
- requires admin to WFJT and execute to JT
|
||||||
|
|||||||
Reference in New Issue
Block a user