From c99d4659dacb9ecdf017088a6184b6bfa1e5eb8d Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 24 Mar 2017 15:43:52 -0400 Subject: [PATCH 1/3] Do not set the default if the field was not passed in to kwargs_extra_vars --- awx/main/models/mixins.py | 4 +++- awx/main/tests/functional/api/test_survey_spec.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index 3ae26eaf71..9ec0a8d736 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -130,12 +130,14 @@ class SurveyJobTemplateMixin(models.Model): for survey_element in self.survey_spec.get("spec", []): default = survey_element.get('default') variable_key = survey_element.get('variable') + if survey_element.get('type') == 'password': if variable_key in kwargs_extra_vars and default: kw_value = kwargs_extra_vars[variable_key] if kw_value.startswith('$encrypted$') and kw_value != default: kwargs_extra_vars[variable_key] = default - if default is not None: + + if default is not None and variable_key in extra_vars: extra_vars[variable_key] = default # Overwrite job template extra vars with explicit job extra vars diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index f954538973..1aff4b8eea 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -211,7 +211,7 @@ def test_launch_with_non_empty_survey_spec_no_license(job_template_factory, post @pytest.mark.django_db @pytest.mark.survey def test_redact_survey_passwords_in_activity_stream(job_template_with_survey_passwords): - job_template_with_survey_passwords.create_unified_job() + job_template_with_survey_passwords.create_unified_job(extra_vars={'secret_key':''}) AS_record = ActivityStream.objects.filter(object1='job').all()[0] changes_dict = json.loads(AS_record.changes) extra_vars = json.loads(changes_dict['extra_vars']) From 18eaacf4bb205d291ce907e1c3a7b3327593138c Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 24 Mar 2017 17:57:52 -0400 Subject: [PATCH 2/3] create _survey_element_validation and use it for updating extra_vars --- awx/main/models/mixins.py | 123 ++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index 9ec0a8d736..e818b5b648 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -137,8 +137,11 @@ class SurveyJobTemplateMixin(models.Model): if kw_value.startswith('$encrypted$') and kw_value != default: kwargs_extra_vars[variable_key] = default - if default is not None and variable_key in extra_vars: - extra_vars[variable_key] = default + if default is not None: + data = {variable_key: default} + errors = self._survey_element_validation(survey_element, data) + if not errors: + extra_vars[variable_key] = default # Overwrite job template extra vars with explicit job extra vars # and add on job extra vars @@ -146,6 +149,65 @@ class SurveyJobTemplateMixin(models.Model): kwargs['extra_vars'] = json.dumps(extra_vars) return kwargs + def _survey_element_validation(self, survey_element, data): + errors = [] + if survey_element['variable'] not in data and survey_element['required']: + errors.append("'%s' value missing" % survey_element['variable']) + elif survey_element['type'] in ["textarea", "text", "password"]: + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) not in (str, unicode): + errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']): + errors.append("'%s' value %s is too small (length is %s must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], len(data[survey_element['variable']]), survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and len(data[survey_element['variable']]) > int(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'integer': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) != int: + errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \ + data[survey_element['variable']] < int(survey_element['min']): + errors.append("'%s' value %s is too small (must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \ + data[survey_element['variable']] > int(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'float': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) not in (float, int): + errors.append("Value %s for '%s' expected to be a numeric type." % (data[survey_element['variable']], + survey_element['variable'])) + return errors + if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']): + errors.append("'%s' value %s is too small (must be at least %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) + if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']): + errors.append("'%s' value %s is too large (must be no more than %s)." % + (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) + elif survey_element['type'] == 'multiselect': + if survey_element['variable'] in data: + if type(data[survey_element['variable']]) != list: + errors.append("'%s' value is expected to be a list." % survey_element['variable']) + else: + for val in data[survey_element['variable']]: + if val not in survey_element['choices']: + errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'], + survey_element['choices'])) + elif survey_element['type'] == 'multiplechoice': + if survey_element['variable'] in data: + if data[survey_element['variable']] not in survey_element['choices']: + errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']], + survey_element['variable'], + survey_element['choices'])) + return errors + def survey_variable_validation(self, data): errors = [] if not self.survey_enabled: @@ -155,62 +217,7 @@ class SurveyJobTemplateMixin(models.Model): if 'description' not in self.survey_spec: errors.append("'description' missing from survey spec.") for survey_element in self.survey_spec.get("spec", []): - if survey_element['variable'] not in data and \ - survey_element['required']: - errors.append("'%s' value missing" % survey_element['variable']) - elif survey_element['type'] in ["textarea", "text", "password"]: - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) not in (str, unicode): - errors.append("Value %s for '%s' expected to be a string." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and len(data[survey_element['variable']]) < int(survey_element['min']): - errors.append("'%s' value %s is too small (length is %s must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], len(data[survey_element['variable']]), survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and len(data[survey_element['variable']]) > int(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'integer': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) != int: - errors.append("Value %s for '%s' expected to be an integer." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and survey_element['variable'] in data and \ - data[survey_element['variable']] < int(survey_element['min']): - errors.append("'%s' value %s is too small (must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and survey_element['variable'] in data and \ - data[survey_element['variable']] > int(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'float': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) not in (float, int): - errors.append("Value %s for '%s' expected to be a numeric type." % (data[survey_element['variable']], - survey_element['variable'])) - continue - if 'min' in survey_element and survey_element['min'] not in ["", None] and data[survey_element['variable']] < float(survey_element['min']): - errors.append("'%s' value %s is too small (must be at least %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['min'])) - if 'max' in survey_element and survey_element['max'] not in ["", None] and data[survey_element['variable']] > float(survey_element['max']): - errors.append("'%s' value %s is too large (must be no more than %s)." % - (survey_element['variable'], data[survey_element['variable']], survey_element['max'])) - elif survey_element['type'] == 'multiselect': - if survey_element['variable'] in data: - if type(data[survey_element['variable']]) != list: - errors.append("'%s' value is expected to be a list." % survey_element['variable']) - else: - for val in data[survey_element['variable']]: - if val not in survey_element['choices']: - errors.append("Value %s for '%s' expected to be one of %s." % (val, survey_element['variable'], - survey_element['choices'])) - elif survey_element['type'] == 'multiplechoice': - if survey_element['variable'] in data: - if data[survey_element['variable']] not in survey_element['choices']: - errors.append("Value %s for '%s' expected to be one of %s." % (data[survey_element['variable']], - survey_element['variable'], - survey_element['choices'])) + errors += self._survey_element_validation(survey_element, data) return errors From 45c3a389d45b457d2f2b20ec2158e169d65cb039 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Sun, 26 Mar 2017 22:43:46 -0400 Subject: [PATCH 3/3] add test, restore old behavior in api test --- awx/main/tests/functional/api/test_survey_spec.py | 2 +- awx/main/tests/unit/models/test_survey_models.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index 1aff4b8eea..f954538973 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -211,7 +211,7 @@ def test_launch_with_non_empty_survey_spec_no_license(job_template_factory, post @pytest.mark.django_db @pytest.mark.survey def test_redact_survey_passwords_in_activity_stream(job_template_with_survey_passwords): - job_template_with_survey_passwords.create_unified_job(extra_vars={'secret_key':''}) + job_template_with_survey_passwords.create_unified_job() AS_record = ActivityStream.objects.filter(object1='job').all()[0] changes_dict = json.loads(AS_record.changes) extra_vars = json.loads(changes_dict['extra_vars']) diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py index 584a4cc7f0..eefc5d97ab 100644 --- a/awx/main/tests/unit/models/test_survey_models.py +++ b/awx/main/tests/unit/models/test_survey_models.py @@ -4,6 +4,7 @@ import json from awx.main.tasks import RunJob from awx.main.models import ( Job, + JobTemplate, WorkflowJobTemplate ) @@ -78,6 +79,18 @@ def test_job_args_unredacted_passwords(job): assert extra_vars['secret_key'] == 'my_password' +def test_update_kwargs_survey_invalid_default(survey_spec_factory): + spec = survey_spec_factory('var2') + spec['spec'][0]['required'] = False + spec['spec'][0]['min'] = 3 + spec['spec'][0]['default'] = 1 + jt = JobTemplate(name="test-jt", survey_spec=spec, survey_enabled=True, extra_vars="var2: 2") + defaulted_extra_vars = jt._update_unified_job_kwargs() + assert 'extra_vars' in defaulted_extra_vars + # Make sure we did not set the invalid default of 1 + assert json.loads(defaulted_extra_vars['extra_vars'])['var2'] == 2 + + class TestWorkflowSurveys: def test_update_kwargs_survey_defaults(self, survey_spec_factory): "Assure that the survey default over-rides a JT variable"