Пример #1
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$')
Пример #2
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
Пример #3
0
    def _handle_encryption(self, method, key, value):
        if value is not empty and self.registry.is_setting_encrypted(key):
            # If the setting exists in the database, we'll use its primary key
            # as part of the AES key when encrypting/decrypting
            obj_id = self.cache.get(Setting.get_cache_id_key(key),
                                    default=empty)
            if obj_id is empty:
                logger.info(
                    'Efficiency notice: Corresponding id not stored in cache %s',
                    Setting.get_cache_id_key(key))
                obj_id = getattr(self._get_setting_from_db(key), 'pk', None)
            elif obj_id == SETTING_CACHE_NONE:
                obj_id = None
            return method(TransientSetting(pk=obj_id, value=value), 'value')

        # If the field in question isn't an "encrypted" field, this function is
        # a no-op; it just returns the provided value
        return value
Пример #4
0
def test_setting_signleton_retrieve_hierachy(api_request, dummy_setting):
    with dummy_setting('FOO_BAR',
                       field_class=fields.IntegerField,
                       default=0,
                       category='FooBar',
                       category_slug='foobar'):
        response = api_request(
            'get',
            reverse('api:setting_singleton_detail',
                    kwargs={'category_slug': 'foobar'}))
        assert response.data['FOO_BAR'] == 0
        s = Setting(key='FOO_BAR', value=1)
        s.save()
        response = api_request(
            'get',
            reverse('api:setting_singleton_detail',
                    kwargs={'category_slug': 'foobar'}))
        assert response.data['FOO_BAR'] == 1
Пример #5
0
def handle_setting_change(key, for_delete=False):
    # When a setting changes or is deleted, remove its value from cache along
    # with any other settings that depend on it.
    setting_keys = [key]
    for dependent_key in settings_registry.get_dependent_settings(key):
        # Note: Doesn't handle multiple levels of dependencies!
        setting_keys.append(dependent_key)
    # NOTE: This block is probably duplicated.
    cache_keys = set([Setting.get_cache_key(k) for k in setting_keys])
    cache.delete_many(cache_keys)

    # Send setting_changed signal with new value for each setting.
    for setting_key in setting_keys:
        setting_changed.send(sender=Setting, setting=setting_key, value=getattr(settings, setting_key, None), enter=not bool(for_delete))
Пример #6
0
def handle_setting_change(key, for_delete=False):
    # When a setting changes or is deleted, remove its value from cache along
    # with any other settings that depend on it.
    setting_keys = [key]
    for dependent_key in settings_registry.get_dependent_settings(key):
        # Note: Doesn't handle multiple levels of dependencies!
        setting_keys.append(dependent_key)
    # NOTE: This block is probably duplicated.
    cache_keys = {Setting.get_cache_key(k) for k in setting_keys}
    cache.delete_many(cache_keys)

    # if we have changed a setting, we want to avoid mucking with the in-memory cache entirely
    settings._awx_conf_memoizedcache.clear()

    # Send setting_changed signal with new value for each setting.
    for setting_key in setting_keys:
        setting_changed.send(sender=Setting,
                             setting=setting_key,
                             value=getattr(settings, setting_key, None),
                             enter=not bool(for_delete))
Пример #7
0
 def _get_local(self, name, validate=True):
     self._preload_cache()
     cache_key = Setting.get_cache_key(name)
     try:
         cache_value = self.cache.get(cache_key, default=empty)
     except ValueError:
         cache_value = empty
     if cache_value == SETTING_CACHE_NOTSET:
         value = empty
     elif cache_value == SETTING_CACHE_NONE:
         value = None
     elif cache_value == SETTING_CACHE_EMPTY_LIST:
         value = []
     elif cache_value == SETTING_CACHE_EMPTY_DICT:
         value = {}
     else:
         value = cache_value
     field = self.registry.get_setting_field(name)
     if value is empty:
         setting = None
         setting_id = None
         # this value is read-only, however we *do* want to fetch its value from the database
         if not field.read_only or name == 'INSTALL_UUID':
             setting = Setting.objects.filter(
                 key=name, user__isnull=True).order_by('pk').first()
         if setting:
             if getattr(field, 'encrypted', False):
                 value = decrypt_field(setting, 'value')
                 setting_id = setting.id
             else:
                 value = setting.value
         else:
             value = SETTING_CACHE_NOTSET
             if SETTING_CACHE_DEFAULTS:
                 try:
                     value = field.get_default()
                     if getattr(field, 'encrypted', False):
                         setting_id = SETTING_CACHE_NONE
                 except SkipField:
                     pass
         # If None implies not set, convert when reading the value.
         if value is None and SETTING_CACHE_NOTSET == SETTING_CACHE_NONE:
             value = SETTING_CACHE_NOTSET
         if cache_value != value:
             if setting_id:
                 logger.debug('Saving id in cache for encrypted setting %s',
                              cache_key)
                 self.cache.cache.set(Setting.get_cache_id_key(cache_key),
                                      setting_id)
             self.cache.set(cache_key,
                            get_cache_value(value),
                            timeout=SETTING_CACHE_TIMEOUT)
     if value == SETTING_CACHE_NOTSET and not SETTING_CACHE_DEFAULTS:
         try:
             value = field.get_default()
         except SkipField:
             pass
     if value not in (empty, SETTING_CACHE_NOTSET):
         try:
             if field.read_only:
                 internal_value = field.to_internal_value(value)
                 field.run_validators(internal_value)
                 return internal_value
             else:
                 if validate:
                     return field.run_validation(value)
                 else:
                     return value
         except Exception:
             logger.warning(
                 'The current value "%r" for setting "%s" is invalid.',
                 value,
                 name,
                 exc_info=True)
     return empty
Пример #8
0
 def _preload_cache(self):
     # Ensure we're only modifying local preload timeout from one thread.
     with self._awx_conf_preload_lock:
         # If local preload timeout has not expired, skip preloading.
         if self._awx_conf_preload_expires and self._awx_conf_preload_expires > time.time(
         ):
             return
         # Otherwise update local preload timeout.
         self.__dict__['_awx_conf_preload_expires'] = time.time(
         ) + SETTING_CACHE_TIMEOUT
         # Check for any settings that have been defined in Python files and
         # make those read-only to avoid overriding in the database.
         if not self._awx_conf_init_readonly:
             defaults_snapshot = self._get_default('DEFAULTS_SNAPSHOT')
             for key in get_writeable_settings(self.registry):
                 init_default = defaults_snapshot.get(key, None)
                 try:
                     file_default = self._get_default(key)
                 except AttributeError:
                     file_default = None
                 if file_default != init_default and file_default is not None:
                     logger.debug('Setting %s has been marked read-only!',
                                  key)
                     self.registry._registry[key]['read_only'] = True
                     self.registry._registry[key]['defined_in_file'] = True
                 self.__dict__['_awx_conf_init_readonly'] = True
     # If local preload timer has expired, check to see if another process
     # has already preloaded the cache and skip preloading if so.
     if self.cache.get('_awx_conf_preload_expires',
                       default=empty) is not empty:
         return
     # Initialize all database-configurable settings with a marker value so
     # to indicate from the cache that the setting is not configured without
     # a database lookup.
     settings_to_cache = get_settings_to_cache(self.registry)
     setting_ids = {}
     # Load all settings defined in the database.
     for setting in Setting.objects.filter(
             key__in=settings_to_cache.keys(),
             user__isnull=True).order_by('pk'):
         if settings_to_cache[setting.key] != SETTING_CACHE_NOTSET:
             continue
         if self.registry.is_setting_encrypted(setting.key):
             setting_ids[setting.key] = setting.id
             value = decrypt_field(setting, 'value')
         else:
             value = setting.value
         settings_to_cache[setting.key] = get_cache_value(value)
     # Load field default value for any settings not found in the database.
     if SETTING_CACHE_DEFAULTS:
         for key, value in settings_to_cache.items():
             if value != SETTING_CACHE_NOTSET:
                 continue
             field = self.registry.get_setting_field(key)
             try:
                 settings_to_cache[key] = get_cache_value(
                     field.get_default())
                 if self.registry.is_setting_encrypted(key):
                     # No database pk, so None will be passed to encryption algorithm
                     setting_ids[key] = SETTING_CACHE_NOTSET
             except SkipField:
                 pass
     # Generate a cache key for each setting and store them all at once.
     settings_to_cache = dict([(Setting.get_cache_key(k), v)
                               for k, v in settings_to_cache.items()])
     for k, id_val in setting_ids.items():
         logger.debug('Saving id in cache for encrypted setting %s, %s',
                      Setting.get_cache_id_key(k), id_val)
         self.cache.cache.set(Setting.get_cache_id_key(k), id_val)
     settings_to_cache[
         '_awx_conf_preload_expires'] = self._awx_conf_preload_expires
     self.cache.set_many(settings_to_cache, timeout=SETTING_CACHE_TIMEOUT)
Пример #9
0
class SettingsWrapper(UserSettingsHolder):

    @classmethod
    def initialize(cls, cache=None, registry=None):
        """
        Used to initialize and wrap the Django settings context.

        :param cache: the Django cache backend to use for caching setting
        values.  ``django.core.cache`` is used by default.
        :param registry: the settings registry instance used.  The global
        ``awx.conf.settings_registry`` is used by default.
        """
        if not getattr(settings, '_awx_conf_settings', False):
            settings_wrapper = cls(
                settings._wrapped,
                cache=cache or django_cache,
                registry=registry or settings_registry
            )
            settings._wrapped = settings_wrapper

    def __init__(self, default_settings, cache, registry):
        """
        This constructor is generally not called directly, but by
        ``SettingsWrapper.initialize`` at app startup time when settings are
        parsed.
        """

        # These values have to be stored via self.__dict__ in this way to get
        # around the magic __setattr__ method on this class (which is used to
        # store API-assigned settings in the database).
        self.__dict__['default_settings'] = default_settings
        self.__dict__['_awx_conf_settings'] = self
        self.__dict__['_awx_conf_preload_expires'] = None
        self.__dict__['_awx_conf_preload_lock'] = threading.RLock()
        self.__dict__['_awx_conf_init_readonly'] = False
        self.__dict__['cache'] = EncryptedCacheProxy(cache, registry)
        self.__dict__['registry'] = registry

    @cached_property
    def all_supported_settings(self):
        return self.registry.get_registered_settings()

    def _preload_cache(self):
        # Ensure we're only modifying local preload timeout from one thread.
        with self._awx_conf_preload_lock:
            # If local preload timeout has not expired, skip preloading.
            if self._awx_conf_preload_expires and self._awx_conf_preload_expires > time.time():
                return
            # Otherwise update local preload timeout.
            self.__dict__['_awx_conf_preload_expires'] = time.time() + SETTING_CACHE_TIMEOUT
            # Check for any settings that have been defined in Python files and
            # make those read-only to avoid overriding in the database.
            if not self._awx_conf_init_readonly and 'migrate_to_database_settings' not in sys.argv:
                defaults_snapshot = self._get_default('DEFAULTS_SNAPSHOT')
                for key in get_writeable_settings(self.registry):
                    init_default = defaults_snapshot.get(key, None)
                    try:
                        file_default = self._get_default(key)
                    except AttributeError:
                        file_default = None
                    if file_default != init_default and file_default is not None:
                        logger.debug('Setting %s has been marked read-only!', key)
                        self.registry._registry[key]['read_only'] = True
                        self.registry._registry[key]['defined_in_file'] = True
                    self.__dict__['_awx_conf_init_readonly'] = True
        # If local preload timer has expired, check to see if another process
        # has already preloaded the cache and skip preloading if so.
        if self.cache.get('_awx_conf_preload_expires', default=empty) is not empty:
            return
        # Initialize all database-configurable settings with a marker value so
        # to indicate from the cache that the setting is not configured without
        # a database lookup.
        settings_to_cache = get_settings_to_cache(self.registry)
        setting_ids = {}
        # Load all settings defined in the database.
        for setting in Setting.objects.filter(key__in=settings_to_cache.keys(), user__isnull=True).order_by('pk'):
            if settings_to_cache[setting.key] != SETTING_CACHE_NOTSET:
                continue
            if self.registry.is_setting_encrypted(setting.key):
                setting_ids[setting.key] = setting.id
                try:
                    value = decrypt_field(setting, 'value')
                except ValueError, e:
                    #TODO: Remove in Tower 3.3
                    logger.debug('encountered error decrypting field: %s - attempting fallback to old', e)
                    value = old_decrypt_field(setting, 'value')

            else:
                value = setting.value
            settings_to_cache[setting.key] = get_cache_value(value)
        # Load field default value for any settings not found in the database.
        if SETTING_CACHE_DEFAULTS:
            for key, value in settings_to_cache.items():
                if value != SETTING_CACHE_NOTSET:
                    continue
                field = self.registry.get_setting_field(key)
                try:
                    settings_to_cache[key] = get_cache_value(field.get_default())
                    if self.registry.is_setting_encrypted(key):
                        # No database pk, so None will be passed to encryption algorithm
                        setting_ids[key] = SETTING_CACHE_NOTSET
                except SkipField:
                    pass
        # Generate a cache key for each setting and store them all at once.
        settings_to_cache = dict([(Setting.get_cache_key(k), v) for k, v in settings_to_cache.items()])
        for k, id_val in setting_ids.items():
            logger.debug('Saving id in cache for encrypted setting %s, %s',
                         Setting.get_cache_id_key(k), id_val)
            self.cache.cache.set(Setting.get_cache_id_key(k), id_val)
        settings_to_cache['_awx_conf_preload_expires'] = self._awx_conf_preload_expires
        self.cache.set_many(settings_to_cache, timeout=SETTING_CACHE_TIMEOUT)
Пример #10
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$')
Пример #11
0
def test_encrypt_field_with_empty_value():
    encrypted = encryption.encrypt_field(Setting(value=None), 'value')
    assert encrypted is None
Пример #12
0
def test_encrypt_field_with_ask():
    encrypted = encryption.encrypt_field(Setting(value='ASK'),
                                         'value',
                                         ask=True)
    assert encrypted == 'ASK'
Пример #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$')
Пример #14
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$')