Пример #1
0
def test_encrypted_survey_answer(post, patch, admin_user, project, inventory, survey_spec_factory):
    job_template = JobTemplate.objects.create(
        name='test-jt',
        project=project,
        playbook='helloworld.yml',
        inventory=inventory,
        ask_variables_on_launch=False,
        survey_enabled=True,
        survey_spec=survey_spec_factory([{'variable': 'var1', 'type': 'password'}]),
    )

    # test encrypted-on-create
    url = reverse('api:job_template_schedules_list', kwargs={'pk': job_template.id})
    r = post(url, {'name': 'test sch', 'rrule': RRULE_EXAMPLE, 'extra_data': '{"var1": "foo"}'}, admin_user, expect=201)
    assert r.data['extra_data']['var1'] == "$encrypted$"
    schedule = Schedule.objects.get(pk=r.data['id'])
    assert schedule.extra_data['var1'].startswith('$encrypted$')
    assert decrypt_value(get_encryption_key('value', pk=None), schedule.extra_data['var1']) == 'foo'

    # test a no-op change
    r = patch(schedule.get_absolute_url(), data={'extra_data': {'var1': '$encrypted$'}}, user=admin_user, expect=200)
    assert r.data['extra_data']['var1'] == '$encrypted$'
    schedule.refresh_from_db()
    assert decrypt_value(get_encryption_key('value', pk=None), schedule.extra_data['var1']) == 'foo'

    # change to a different value
    r = patch(schedule.get_absolute_url(), data={'extra_data': {'var1': 'bar'}}, user=admin_user, expect=200)
    assert r.data['extra_data']['var1'] == '$encrypted$'
    schedule.refresh_from_db()
    assert decrypt_value(get_encryption_key('value', pk=None), schedule.extra_data['var1']) == 'bar'
Пример #2
0
    def _accept_or_ignore_variables(self,
                                    data,
                                    errors=None,
                                    _exclude_errors=(),
                                    extra_passwords=None):
        survey_is_enabled = (self.survey_enabled and self.survey_spec)
        extra_vars = data.copy()
        if errors is None:
            errors = {}
        rejected = {}
        accepted = {}

        if survey_is_enabled:
            # Check for data violation of survey rules
            survey_errors = []
            for survey_element in self.survey_spec.get("spec", []):
                key = survey_element.get('variable', None)
                value = data.get(key, None)
                validate_required = 'required' not in _exclude_errors
                if extra_passwords and key in extra_passwords and is_encrypted(
                        value):
                    element_errors = self._survey_element_validation(
                        survey_element, {
                            key:
                            decrypt_value(get_encryption_key('value', pk=None),
                                          value)
                        },
                        validate_required=validate_required)
                else:
                    element_errors = self._survey_element_validation(
                        survey_element,
                        data,
                        validate_required=validate_required)

                if element_errors:
                    survey_errors += element_errors
                    if key is not None and key in extra_vars:
                        rejected[key] = extra_vars.pop(key)
                elif key in extra_vars:
                    accepted[key] = extra_vars.pop(key)
            if survey_errors:
                errors['variables_needed_to_start'] = survey_errors

        if self.ask_variables_on_launch:
            # We can accept all variables
            accepted.update(extra_vars)
            extra_vars = {}

        if extra_vars:
            # Leftover extra_vars, keys provided that are not allowed
            rejected.update(extra_vars)
            # ignored variables does not block manual launch
            if 'prompts' not in _exclude_errors:
                errors['extra_vars'] = [
                    _('Variables {list_of_keys} are not allowed on launch. Check the Prompt on Launch setting '
                      + 'on the Job Template to include Extra Variables.').
                    format(list_of_keys=', '.join(extra_vars.keys()))
                ]

        return (accepted, rejected, errors)
Пример #3
0
    def _update_unified_job_kwargs(self, create_kwargs, kwargs):
        '''
        Combine extra_vars with variable precedence order:
          JT extra_vars -> JT survey defaults -> runtime extra_vars

        :param create_kwargs: key-worded arguments to be updated and later used for creating unified job.
        :type create_kwargs: dict
        :param kwargs: request parameters used to override unified job template fields with runtime values.
        :type kwargs: dict
        :return: modified create_kwargs.
        :rtype: dict
        '''
        # Job Template extra_vars
        extra_vars = self.extra_vars_dict

        survey_defaults = {}

        # transform to dict
        if 'extra_vars' in kwargs:
            runtime_extra_vars = kwargs['extra_vars']
            runtime_extra_vars = parse_yaml_or_json(runtime_extra_vars)
        else:
            runtime_extra_vars = {}

        # Overwrite job template extra vars with survey default vars
        if self.survey_enabled and 'spec' in self.survey_spec:
            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 runtime_extra_vars:
                        kw_value = runtime_extra_vars[variable_key]
                        if kw_value == '$encrypted$':
                            runtime_extra_vars.pop(variable_key)

                if default is not None:
                    decrypted_default = default
                    if (survey_element['type'] == "password"
                            and isinstance(decrypted_default, str)
                            and decrypted_default.startswith('$encrypted$')):
                        decrypted_default = decrypt_value(
                            get_encryption_key('value', pk=None),
                            decrypted_default)
                    errors = self._survey_element_validation(
                        survey_element, {variable_key: decrypted_default})
                    if not errors:
                        survey_defaults[variable_key] = default
        extra_vars.update(survey_defaults)

        # Overwrite job template extra vars with explicit job extra vars
        # and add on job extra vars
        extra_vars.update(runtime_extra_vars)
        create_kwargs['extra_vars'] = json.dumps(extra_vars)
        return create_kwargs
Пример #4
0
 def decrypted_extra_vars(self):
     """
     Decrypts fields marked as passwords in survey.
     """
     if self.survey_passwords:
         extra_vars = json.loads(self.extra_vars)
         for key in self.survey_passwords:
             value = extra_vars.get(key)
             if value and isinstance(value, str) and value.startswith('$encrypted$'):
                 extra_vars[key] = decrypt_value(get_encryption_key('value', pk=None), value)
         return json.dumps(extra_vars)
     else:
         return self.extra_vars
Пример #5
0
    def _survey_passwords(self):
        for _type in (JobTemplate, WorkflowJobTemplate):
            for jt in _type.objects.exclude(survey_spec={}):
                changed = False
                if jt.survey_spec.get('spec', []):
                    for field in jt.survey_spec['spec']:
                        if field.get('type') == 'password' and field.get(
                                'default', ''):
                            raw = decrypt_value(
                                get_encryption_key('value',
                                                   None,
                                                   secret_key=self.old_key),
                                field['default'])
                            field['default'] = encrypt_value(
                                raw, pk=None, secret_key=self.new_key)
                            changed = True
                if changed:
                    jt.save(update_fields=["survey_spec"])

        for _type in (Job, WorkflowJob):
            for job in _type.objects.exclude(survey_passwords={}).iterator():
                changed = False
                for key in job.survey_passwords:
                    if key in job.extra_vars:
                        extra_vars = json.loads(job.extra_vars)
                        if not extra_vars.get(key):
                            continue
                        raw = decrypt_value(
                            get_encryption_key('value',
                                               None,
                                               secret_key=self.old_key),
                            extra_vars[key])
                        extra_vars[key] = encrypt_value(
                            raw, pk=None, secret_key=self.new_key)
                        job.extra_vars = json.dumps(extra_vars)
                        changed = True
                if changed:
                    job.save(update_fields=["extra_vars"])
Пример #6
0
def test_oauth_application_encryption(admin, organization, post):
    response = post(
        reverse('api:o_auth2_application_list'), {
            'name': 'test app',
            'organization': organization.pk,
            'client_type': 'confidential',
            'authorization_grant_type': 'password',
        }, admin, expect=201
    )
    pk = response.data.get('id')
    secret = response.data.get('client_secret')
    with connection.cursor() as cursor:
        encrypted = cursor.execute(
            'SELECT client_secret FROM main_oauth2application WHERE id={}'.format(pk)
        ).fetchone()[0]
        assert encrypted.startswith('$encrypted$')
        assert decrypt_value(get_encryption_key('value', pk=None), encrypted) == secret
Пример #7
0
 def from_db_value(self, value, expression, connection, context):
     if value and value.startswith('$encrypted$'):
         return decrypt_value(get_encryption_key('value', pk=None), value)
     return value
Пример #8
0
 def test_encrypt_empty_string_twice(self):
     # Encryption is idempotent
     val = encryption.encrypt_value('foobar')
     val2 = encryption.encrypt_value(val)
     assert encryption.decrypt_value(self._key, val2) == 'foobar'