Beispiel #1
0
def test_setting_singleton_update_dont_change_encripted_mark(api_request, dummy_setting):
    with dummy_setting(
        'FOO_BAR',
        field_class=fields.CharField,
        encrypted=True,
        category='FooBar',
        category_slug='foobar'
    ), mock.patch('awx.conf.views.handle_setting_changes'):
        api_request(
            'patch',
            reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}),
            data={'FOO_BAR': 'password'}
        )
        assert Setting.objects.get(key='FOO_BAR').value.startswith('$encrypted$')
        response = api_request(
            'get',
            reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'})
        )
        assert response.data['FOO_BAR'] == '$encrypted$'
        api_request(
            'patch',
            reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}),
            data={'FOO_BAR': '$encrypted$'}
        )
        assert decrypt_field(Setting.objects.get(key='FOO_BAR'), 'value') == 'password'
        api_request(
            'patch',
            reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}),
            data={'FOO_BAR': 'new_pw'}
        )
        assert decrypt_field(Setting.objects.get(key='FOO_BAR'), 'value') == 'new_pw'
Beispiel #2
0
def test_encrypt_subfield():
    field = Setting(value={'name': 'ANSIBLE'})
    encrypted = field.value = encryption.encrypt_field(field,
                                                       'value',
                                                       subfield='name')
    assert encryption.decrypt_field(field, 'value',
                                    subfield='name') == 'ANSIBLE'
    assert encrypted.startswith('$encrypted$UTF8$AESCBC$')
Beispiel #3
0
def test_encrypt_field_force_disable_unicode():
    value = u"NothingSpecial"
    field = Setting(value=value)
    encrypted = field.value = encryption.encrypt_field(field,
                                                       'value',
                                                       skip_utf8=True)
    assert "UTF8" not in encrypted
    assert encryption.decrypt_field(field, 'value') == value
Beispiel #4
0
 def _credentials(self):
     for credential in Credential.objects.iterator():
         for field_name in credential.credential_type.secret_fields:
             if field_name in credential.inputs:
                 credential.inputs[field_name] = decrypt_field(
                     credential, field_name, secret_key=self.old_key)
                 credential.inputs[field_name] = encrypt_field(
                     credential, field_name, secret_key=self.new_key)
             credential.save()
Beispiel #5
0
 def _unified_jobs(self):
     for uj in UnifiedJob.objects.iterator():
         if uj.start_args:
             uj.start_args = decrypt_field(uj,
                                           'start_args',
                                           secret_key=self.old_key)
             uj.start_args = encrypt_field(uj,
                                           'start_args',
                                           secret_key=self.new_key)
             uj.save()
Beispiel #6
0
 def _settings(self):
     # don't update memcached, the *actual* value isn't changing
     post_save.disconnect(on_post_save_setting, sender=Setting)
     for setting in Setting.objects.filter().order_by('pk'):
         if settings_registry.is_setting_encrypted(setting.key):
             setting.value = decrypt_field(setting,
                                           'value',
                                           secret_key=self.old_key)
             setting.value = encrypt_field(setting,
                                           'value',
                                           secret_key=self.new_key)
             setting.save()
    def test_job_start_args(self, job_factory):
        # test basic decryption
        job = job_factory()
        job.start_args = json.dumps({'foo': 'bar'})
        job.start_args = encrypt_field(job, field_name='start_args')
        job.save()
        assert job.start_args.startswith(PREFIX)

        # re-key the start_args
        new_key = regenerate_secret_key.Command().handle()
        new_job = models.Job.objects.get(pk=job.pk)
        assert new_job.start_args != job.start_args

        # verify that the old SECRET_KEY doesn't work
        with pytest.raises(InvalidToken):
            decrypt_field(new_job, field_name='start_args')

        # verify that the new SECRET_KEY *does* work
        with override_settings(SECRET_KEY=new_key):
            assert json.loads(
                decrypt_field(new_job, field_name='start_args')
            ) == {'foo': 'bar'}
Beispiel #8
0
def _migrate_setting(apps, old_key, new_key, encrypted=False):
    Setting = apps.get_model('conf', 'Setting')
    if not Setting.objects.filter(key=old_key).exists():
        return
    new_setting = Setting.objects.create(key=new_key,
                                         created=now(),
                                         modified=now())
    if encrypted:
        new_setting.value = decrypt_field(
            Setting.objects.filter(key=old_key).first(), 'value')
        new_setting.value = encrypt_field(new_setting, 'value')
    else:
        new_setting.value = getattr(
            Setting.objects.filter(key=old_key).first(), 'value')
    new_setting.save()
Beispiel #9
0
 def _notification_templates(self):
     for nt in NotificationTemplate.objects.iterator():
         CLASS_FOR_NOTIFICATION_TYPE = dict([
             (x[0], x[2]) for x in NotificationTemplate.NOTIFICATION_TYPES
         ])
         notification_class = CLASS_FOR_NOTIFICATION_TYPE[
             nt.notification_type]
         for field in filter(
                 lambda x: notification_class.init_parameters[x]['type'] ==
                 "password", notification_class.init_parameters):
             nt.notification_configuration[field] = decrypt_field(
                 nt,
                 'notification_configuration',
                 subfield=field,
                 secret_key=self.old_key)
             nt.notification_configuration[field] = encrypt_field(
                 nt,
                 'notification_configuration',
                 subfield=field,
                 secret_key=self.new_key)
         nt.save()
Beispiel #10
0
def migrate_galaxy_settings(apps, schema_editor):
    Organization = apps.get_model('main', 'Organization')
    if Organization.objects.count() == 0:
        # nothing to migrate
        return
    set_current_apps(apps)
    ModernCredentialType.setup_tower_managed_defaults(apps)
    CredentialType = apps.get_model('main', 'CredentialType')
    Credential = apps.get_model('main', 'Credential')
    Setting = apps.get_model('conf', 'Setting')

    galaxy_type = CredentialType.objects.get(kind='galaxy')
    private_galaxy_url = Setting.objects.filter(
        key='PRIMARY_GALAXY_URL').first()

    # by default, prior versions of AWX automatically pulled content
    # from galaxy.ansible.com
    public_galaxy_enabled = True
    public_galaxy_setting = Setting.objects.filter(
        key='PUBLIC_GALAXY_ENABLED').first()
    if public_galaxy_setting and public_galaxy_setting.value is False:
        # ...UNLESS this behavior was explicitly disabled via this setting
        public_galaxy_enabled = False
    try:
        # Needed for old migrations
        public_galaxy_credential = Credential(
            created=now(),
            modified=now(),
            name='Ansible Galaxy',
            managed_by_tower=True,
            credential_type=galaxy_type,
            inputs={'url': 'https://galaxy.ansible.com/'},
        )
    except:
        # Needed for new migrations, tests
        public_galaxy_credential = Credential(
            created=now(),
            modified=now(),
            name='Ansible Galaxy',
            managed=True,
            credential_type=galaxy_type,
            inputs={'url': 'https://galaxy.ansible.com/'})
    public_galaxy_credential.save()

    for org in Organization.objects.all():
        if private_galaxy_url and private_galaxy_url.value:
            # If a setting exists for a private Galaxy URL, make a credential for it
            username = Setting.objects.filter(
                key='PRIMARY_GALAXY_USERNAME').first()
            password = Setting.objects.filter(
                key='PRIMARY_GALAXY_PASSWORD').first()
            if (username and username.value) or (password and password.value):
                logger.error(
                    f'Specifying HTTP basic auth for the Ansible Galaxy API '
                    f'({private_galaxy_url.value}) is no longer supported. '
                    'Please provide an API token instead after your upgrade '
                    'has completed', )
            inputs = {'url': private_galaxy_url.value}
            token = Setting.objects.filter(key='PRIMARY_GALAXY_TOKEN').first()
            if token and token.value:
                inputs['token'] = decrypt_field(token, 'value')
            auth_url = Setting.objects.filter(
                key='PRIMARY_GALAXY_AUTH_URL').first()
            if auth_url and auth_url.value:
                inputs['auth_url'] = auth_url.value
            name = f'Private Galaxy ({private_galaxy_url.value})'
            if 'cloud.redhat.com' in inputs['url']:
                name = f'Ansible Automation Hub ({private_galaxy_url.value})'
            cred = Credential(created=now(),
                              modified=now(),
                              name=name,
                              organization=org,
                              credential_type=galaxy_type,
                              inputs=inputs)
            cred.save()
            if token and token.value:
                # encrypt based on the primary key from the prior save
                cred.inputs['token'] = encrypt_field(cred, 'token')
                cred.save()
            org.galaxy_credentials.add(cred)

        fallback_servers = getattr(settings, 'FALLBACK_GALAXY_SERVERS', [])
        for fallback in fallback_servers:
            url = fallback.get('url', None)
            auth_url = fallback.get('auth_url', None)
            username = fallback.get('username', None)
            password = fallback.get('password', None)
            token = fallback.get('token', None)
            if username or password:
                logger.error(
                    f'Specifying HTTP basic auth for the Ansible Galaxy API '
                    f'({url}) is no longer supported. '
                    'Please provide an API token instead after your upgrade '
                    'has completed', )
            inputs = {'url': url}
            if token:
                inputs['token'] = token
            if auth_url:
                inputs['auth_url'] = auth_url
            cred = Credential(created=now(),
                              modified=now(),
                              name=f'Ansible Galaxy ({url})',
                              organization=org,
                              credential_type=galaxy_type,
                              inputs=inputs)
            cred.save()
            if token:
                # encrypt based on the primary key from the prior save
                cred.inputs['token'] = encrypt_field(cred, 'token')
                cred.save()
            org.galaxy_credentials.add(cred)

        if public_galaxy_enabled:
            # If public Galaxy was enabled, associate it to the org
            org.galaxy_credentials.add(public_galaxy_credential)
Beispiel #11
0
def migrate_to_v2_credentials(apps, schema_editor):
    CredentialType.setup_tower_managed_defaults()
    deprecated_cred = _generate_deprecated_cred_types()

    # this monkey-patch is necessary to make the implicit role generation save
    # signal use the correct Role model (the version active at this point in
    # migration, not the one at HEAD)
    orig_current_apps = utils.get_current_apps
    try:
        utils.get_current_apps = lambda: apps
        for cred in apps.get_model('main', 'Credential').objects.all():
            job_templates = cred.jobtemplates.all()
            jobs = cred.jobs.all()
            data = {}
            if getattr(cred, 'vault_password', None):
                data['vault_password'] = cred.vault_password
            if _is_insights_scm(apps, cred):
                _disassociate_non_insights_projects(apps, cred)
                credential_type = _get_insights_credential_type()
            else:
                credential_type = _populate_deprecated_cred_types(
                    deprecated_cred, cred.kind) or CredentialType.from_v1_kind(
                        cred.kind, data)

            defined_fields = credential_type.defined_fields
            cred.credential_type = apps.get_model(
                'main', 'CredentialType').objects.get(pk=credential_type.pk)

            for field in defined_fields:
                if getattr(cred, field, None):
                    cred.inputs[field] = getattr(cred, field)
            if cred.vault_password:
                for jt in job_templates:
                    jt.credential = None
                    jt.vault_credential = cred
                    jt.save()
                for job in jobs:
                    job.credential = None
                    job.vault_credential = cred
                    job.save()
            if data.get('is_insights', False):
                cred.kind = 'insights'
            cred.save()

            #
            # If the credential contains a vault password, create a new
            # *additional* credential for the ssh details
            #
            if cred.vault_password:
                # We need to make an ssh credential, too
                ssh_type = CredentialType.from_v1_kind('ssh')
                new_cred = apps.get_model('main',
                                          'Credential').objects.get(pk=cred.pk)
                new_cred.pk = None
                new_cred.vault_password = ''
                new_cred.credential_type = apps.get_model(
                    'main', 'CredentialType').objects.get(pk=ssh_type.pk)
                if 'vault_password' in new_cred.inputs:
                    del new_cred.inputs['vault_password']

                # unset these attributes so that new roles are properly created
                # at save time
                new_cred.read_role = None
                new_cred.admin_role = None
                new_cred.use_role = None

                if any([
                        getattr(cred, field)
                        for field in ssh_type.defined_fields
                ]):
                    new_cred.save(force_insert=True)

                    # copy rbac roles
                    for role_type in ('read_role', 'admin_role', 'use_role'):
                        for member in getattr(cred, role_type).members.all():
                            getattr(new_cred, role_type).members.add(member)
                        for role in getattr(cred, role_type).parents.all():
                            getattr(new_cred, role_type).parents.add(role)

                    for jt in job_templates:
                        jt.credential = new_cred
                        jt.save()
                    for job in jobs:
                        job.credential = new_cred
                        job.save()

                    # passwords must be decrypted and re-encrypted, because
                    # their encryption is based on the Credential's primary key
                    # (which has changed)
                    for field in ssh_type.defined_fields:
                        if field in ssh_type.secret_fields:
                            value = decrypt_field(cred, field)
                            if value:
                                setattr(new_cred, field, value)
                                new_cred.inputs[field] = encrypt_field(
                                    new_cred, field)
                                setattr(new_cred, field, '')
                        elif getattr(cred, field):
                            new_cred.inputs[field] = getattr(cred, field)
                    new_cred.save()
    finally:
        utils.get_current_apps = orig_current_apps
Beispiel #12
0
def test_encrypt_field():
    field = Setting(pk=123, value='ANSIBLE')
    encrypted = field.value = encryption.encrypt_field(field, 'value')
    assert encryption.decrypt_field(field, 'value') == 'ANSIBLE'
    assert encrypted.startswith('$encrypted$UTF8$AESCBC$')
Beispiel #13
0
def test_encrypt_field_with_unicode_string():
    value = u'Iñtërnâtiônàlizætiøn'
    field = Setting(value=value)
    encrypted = field.value = encryption.encrypt_field(field, 'value')
    assert encryption.decrypt_field(field, 'value') == value
    assert encrypted.startswith('$encrypted$UTF8$AESCBC$')
def test_decrypt_field_with_undefined_attr_raises_expected_exception():
    with pytest.raises(AttributeError):
        encryption.decrypt_field({}, 'undefined_attr')
Beispiel #15
0
def test_encrypt_field_without_pk():
    field = Setting(value='ANSIBLE')
    encrypted = field.value = encryption.encrypt_field(field, 'value')
    assert encryption.decrypt_field(field, 'value') == 'ANSIBLE'
    assert encrypted.startswith('$encrypted$AESCBC$')