1
0
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:
Alan Rominger 2017-08-18 17:20:57 -04:00 committed by GitHub
commit caf15abc4f
3 changed files with 37 additions and 2 deletions

View File

@ -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

View File

@ -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:

View File

@ -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):