mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 15:21:13 +03:00
Merge pull request #297 from AlanCoding/cache_ids
Make cache compatible with encrypted settings
This commit is contained in:
commit
caf15abc4f
@ -74,6 +74,10 @@ class Setting(CreatedModifiedModel):
|
||||
def get_cache_key(self, key):
|
||||
return key
|
||||
|
||||
@classmethod
|
||||
def get_cache_id_key(self, key):
|
||||
return '{}_ID'.format(key)
|
||||
|
||||
|
||||
import awx.conf.signals # noqa
|
||||
|
||||
|
@ -124,9 +124,12 @@ class EncryptedCacheProxy(object):
|
||||
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:
|
||||
obj_id = getattr(self._get_setting_from_db(key), 'pk', None)
|
||||
return method(
|
||||
TransientSetting(
|
||||
pk=getattr(self._get_setting_from_db(key), 'pk', None),
|
||||
pk=obj_id,
|
||||
value=value
|
||||
),
|
||||
'value'
|
||||
@ -241,11 +244,13 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
# 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:
|
||||
@ -268,6 +273,9 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
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', k)
|
||||
self.cache.set(Setting.get_cache_id_key(k), id_val)
|
||||
settings_to_cache['_awx_conf_preload_expires'] = self._awx_conf_preload_expires
|
||||
logger.debug('cache set_many(%r, %r)', settings_to_cache, SETTING_CACHE_TIMEOUT)
|
||||
self.cache.set_many(settings_to_cache, timeout=SETTING_CACHE_TIMEOUT)
|
||||
@ -293,6 +301,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
field = self.registry.get_setting_field(name)
|
||||
if value is empty:
|
||||
setting = None
|
||||
setting_id = None
|
||||
if not field.read_only or name in (
|
||||
# these two values are read-only - however - we *do* want
|
||||
# to fetch their value from the database
|
||||
@ -303,6 +312,7 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
if setting:
|
||||
if getattr(field, 'encrypted', False):
|
||||
value = decrypt_field(setting, 'value')
|
||||
setting_id = setting.id
|
||||
else:
|
||||
value = setting.value
|
||||
else:
|
||||
@ -319,6 +329,9 @@ class SettingsWrapper(UserSettingsHolder):
|
||||
logger.debug('cache set(%r, %r, %r)', cache_key,
|
||||
get_cache_value(value),
|
||||
SETTING_CACHE_TIMEOUT)
|
||||
if setting_id:
|
||||
logger.debug('Saving id in cache for encrypted setting %s', cache_key)
|
||||
self.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:
|
||||
|
@ -391,7 +391,20 @@ def test_charfield_properly_sets_none(settings, mocker):
|
||||
)
|
||||
|
||||
|
||||
def test_settings_use_an_encrypted_cache(settings):
|
||||
def test_settings_use_cache(settings, mocker):
|
||||
settings.registry.register(
|
||||
'AWX_VAR',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
settings.cache.set('AWX_VAR', 'foobar')
|
||||
settings.cache.set('_awx_conf_preload_expires', 100)
|
||||
# Will fail test if database is used
|
||||
getattr(settings, 'AWX_VAR')
|
||||
|
||||
|
||||
def test_settings_use_an_encrypted_cache(settings, mocker):
|
||||
settings.registry.register(
|
||||
'AWX_ENCRYPTED',
|
||||
field_class=fields.CharField,
|
||||
@ -402,6 +415,11 @@ def test_settings_use_an_encrypted_cache(settings):
|
||||
assert isinstance(settings.cache, EncryptedCacheProxy)
|
||||
assert settings.cache.__dict__['encrypter'] == encrypt_field
|
||||
assert settings.cache.__dict__['decrypter'] == decrypt_field
|
||||
settings.cache.set('AWX_ENCRYPTED_ID', 402)
|
||||
settings.cache.set('AWX_ENCRYPTED', 'foobar')
|
||||
settings.cache.set('_awx_conf_preload_expires', 100)
|
||||
# Will fail test if database is used
|
||||
getattr(settings, 'AWX_ENCRYPTED')
|
||||
|
||||
|
||||
def test_sensitive_cache_data_is_encrypted(settings, mocker):
|
||||
|
Loading…
Reference in New Issue
Block a user