diff --git a/awx/main/migrations/_save_password_keys.py b/awx/main/migrations/_save_password_keys.py index 3ff7b17562..a5a231a92f 100644 --- a/awx/main/migrations/_save_password_keys.py +++ b/awx/main/migrations/_save_password_keys.py @@ -1,8 +1,10 @@ def survey_password_variables(survey_spec): vars = [] # Get variables that are type password + if 'spec' not in survey_spec: + return vars for survey_element in survey_spec['spec']: - if survey_element['type'] == 'password': + if 'type' in survey_element and survey_element['type'] == 'password': vars.append(survey_element['variable']) return vars diff --git a/awx/main/tests/conftest.py b/awx/main/tests/conftest.py index 470f43e661..1f21905fb9 100644 --- a/awx/main/tests/conftest.py +++ b/awx/main/tests/conftest.py @@ -26,16 +26,16 @@ def survey_spec_factory(): return create_survey_spec @pytest.fixture -def job_with_secret_key_factory(job_template_factory): +def job_template_with_survey_passwords_factory(job_template_factory): def rf(persisted): "Returns job with linked JT survey with password survey questions" objects = job_template_factory('jt', organization='org1', survey=[ {'variable': 'submitter_email', 'type': 'text', 'default': 'foobar@redhat.com'}, {'variable': 'secret_key', 'default': '6kQngg3h8lgiSTvIEb21', 'type': 'password'}, - {'variable': 'SSN', 'type': 'password'}], jobs=[1], persisted=persisted) - return objects.jobs[1] + {'variable': 'SSN', 'type': 'password'}], persisted=persisted) + return objects.job_template return rf @pytest.fixture -def job_with_secret_key_unit(job_with_secret_key_factory): - return job_with_secret_key_factory(persisted=False) +def job_template_with_survey_passwords_unit(job_template_with_survey_passwords_factory): + return job_template_with_survey_passwords_factory(persisted=False) diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index a5a961f88e..88437a0037 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -3,12 +3,14 @@ import mock # AWX from awx.api.serializers import JobTemplateSerializer, JobLaunchSerializer -from awx.main.models.jobs import JobTemplate +from awx.main.models.jobs import JobTemplate, Job from awx.main.models.projects import ProjectOptions +from awx.main.migrations import _save_password_keys as save_password_keys # Django from django.test.client import RequestFactory from django.core.urlresolvers import reverse +from django.apps import apps @property def project_playbooks(self): @@ -348,3 +350,20 @@ def test_disallow_template_delete_on_running_job(job_template_factory, delete, a objects.job_template.create_unified_job() delete_response = delete(reverse('api:job_template_detail', args=[objects.job_template.pk]), user=admin_user) assert delete_response.status_code == 409 + +@pytest.mark.django_db +def test_save_survey_passwords_to_job(job_template_with_survey_passwords): + """Test that when a new job is created, the survey_passwords field is + given all of the passwords that exist in the JT survey""" + job = job_template_with_survey_passwords.create_unified_job() + assert job.survey_passwords == {'SSN': '$encrypted$', 'secret_key': '$encrypted$'} + +@pytest.mark.django_db +def test_save_survey_passwords_on_migration(job_template_with_survey_passwords): + """Test that when upgrading to 3.0.2, the jobs connected to a JT that has + a survey with passwords in it, the survey passwords get saved to the + job survey_passwords field.""" + Job.objects.create(job_template=job_template_with_survey_passwords) + save_password_keys.migrate_survey_passwords(apps, None) + job = job_template_with_survey_passwords.jobs.all()[0] + assert job.survey_passwords == {'SSN': '$encrypted$', 'secret_key': '$encrypted$'} diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index dc7071fc11..d6cc512847 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -193,7 +193,8 @@ 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_with_secret_key): +def test_redact_survey_passwords_in_activity_stream(job_template_with_survey_passwords): + 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/functional/conftest.py b/awx/main/tests/functional/conftest.py index f970adc2e7..5e67dda1b5 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -206,8 +206,8 @@ def notification(notification_template): subject='email subject') @pytest.fixture -def job_with_secret_key(job_with_secret_key_factory): - return job_with_secret_key_factory(persisted=True) +def job_template_with_survey_passwords(job_template_with_survey_passwords_factory): + return job_template_with_survey_passwords_factory(persisted=True) @pytest.fixture def admin(user): diff --git a/awx/main/tests/unit/models/test_job_template_unit.py b/awx/main/tests/unit/models/test_job_template_unit.py index a25cce6f6c..b9a72edea5 100644 --- a/awx/main/tests/unit/models/test_job_template_unit.py +++ b/awx/main/tests/unit/models/test_job_template_unit.py @@ -35,6 +35,7 @@ def test_inventory_credential_contradictions(job_template_factory): assert 'credential' in validation_errors @pytest.mark.survey -def test_survey_password_list(job_with_secret_key_unit): - """Verify that survey_password_variables method gives a list of survey passwords""" - assert job_with_secret_key_unit.job_template.survey_password_variables() == ['secret_key', 'SSN'] +def test_job_template_survey_password_redaction(job_template_with_survey_passwords_unit): + """Tests the JobTemplate model's funciton to redact passwords from + extra_vars - used when creating a new job""" + assert job_template_with_survey_passwords_unit.survey_password_variables() == ['secret_key', 'SSN'] diff --git a/awx/main/tests/unit/models/test_job_unit.py b/awx/main/tests/unit/models/test_job_unit.py index a1791c59d5..1b66681dcf 100644 --- a/awx/main/tests/unit/models/test_job_unit.py +++ b/awx/main/tests/unit/models/test_job_unit.py @@ -2,6 +2,7 @@ import pytest import json from awx.main.tasks import RunJob +from awx.main.models import Job @pytest.fixture @@ -14,9 +15,19 @@ def job(mocker): 'launch_type': 'manual'}) @pytest.mark.survey -def test_job_redacted_extra_vars(job_with_secret_key_unit): - """Verify that this method redacts vars marked as passwords in a survey""" - assert json.loads(job_with_secret_key_unit.display_extra_vars()) == { +def test_job_survey_password_redaction(): + """Tests the Job model's funciton to redact passwords from + extra_vars - used when displaying job information""" + job = Job( + name="test-job-with-passwords", + extra_vars=json.dumps({ + 'submitter_email': 'foobar@redhat.com', + 'secret_key': '6kQngg3h8lgiSTvIEb21', + 'SSN': '123-45-6789'}), + survey_passwords={ + 'secret_key': '$encrypted$', + 'SSN': '$encrypted$'}) + assert json.loads(job.display_extra_vars()) == { 'submitter_email': 'foobar@redhat.com', 'secret_key': '$encrypted$', 'SSN': '$encrypted$'}