mirror of
https://github.com/ansible/awx.git
synced 2026-03-06 11:11:07 -03:30
Merge pull request #297 from AlanCoding/cache_ids
Make cache compatible with encrypted settings
This commit is contained in:
@@ -74,6 +74,10 @@ class Setting(CreatedModifiedModel):
|
|||||||
def get_cache_key(self, key):
|
def get_cache_key(self, key):
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_cache_id_key(self, key):
|
||||||
|
return '{}_ID'.format(key)
|
||||||
|
|
||||||
|
|
||||||
import awx.conf.signals # noqa
|
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 value is not empty and self.registry.is_setting_encrypted(key):
|
||||||
# If the setting exists in the database, we'll use its primary key
|
# If the setting exists in the database, we'll use its primary key
|
||||||
# as part of the AES key when encrypting/decrypting
|
# 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(
|
return method(
|
||||||
TransientSetting(
|
TransientSetting(
|
||||||
pk=getattr(self._get_setting_from_db(key), 'pk', None),
|
pk=obj_id,
|
||||||
value=value
|
value=value
|
||||||
),
|
),
|
||||||
'value'
|
'value'
|
||||||
@@ -241,11 +244,13 @@ class SettingsWrapper(UserSettingsHolder):
|
|||||||
# to indicate from the cache that the setting is not configured without
|
# to indicate from the cache that the setting is not configured without
|
||||||
# a database lookup.
|
# a database lookup.
|
||||||
settings_to_cache = get_settings_to_cache(self.registry)
|
settings_to_cache = get_settings_to_cache(self.registry)
|
||||||
|
setting_ids = {}
|
||||||
# Load all settings defined in the database.
|
# 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'):
|
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:
|
if settings_to_cache[setting.key] != SETTING_CACHE_NOTSET:
|
||||||
continue
|
continue
|
||||||
if self.registry.is_setting_encrypted(setting.key):
|
if self.registry.is_setting_encrypted(setting.key):
|
||||||
|
setting_ids[setting.key] = setting.id
|
||||||
try:
|
try:
|
||||||
value = decrypt_field(setting, 'value')
|
value = decrypt_field(setting, 'value')
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
@@ -268,6 +273,9 @@ class SettingsWrapper(UserSettingsHolder):
|
|||||||
pass
|
pass
|
||||||
# Generate a cache key for each setting and store them all at once.
|
# 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()])
|
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
|
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)
|
logger.debug('cache set_many(%r, %r)', settings_to_cache, SETTING_CACHE_TIMEOUT)
|
||||||
self.cache.set_many(settings_to_cache, timeout=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)
|
field = self.registry.get_setting_field(name)
|
||||||
if value is empty:
|
if value is empty:
|
||||||
setting = None
|
setting = None
|
||||||
|
setting_id = None
|
||||||
if not field.read_only or name in (
|
if not field.read_only or name in (
|
||||||
# these two values are read-only - however - we *do* want
|
# these two values are read-only - however - we *do* want
|
||||||
# to fetch their value from the database
|
# to fetch their value from the database
|
||||||
@@ -303,6 +312,7 @@ class SettingsWrapper(UserSettingsHolder):
|
|||||||
if setting:
|
if setting:
|
||||||
if getattr(field, 'encrypted', False):
|
if getattr(field, 'encrypted', False):
|
||||||
value = decrypt_field(setting, 'value')
|
value = decrypt_field(setting, 'value')
|
||||||
|
setting_id = setting.id
|
||||||
else:
|
else:
|
||||||
value = setting.value
|
value = setting.value
|
||||||
else:
|
else:
|
||||||
@@ -319,6 +329,9 @@ class SettingsWrapper(UserSettingsHolder):
|
|||||||
logger.debug('cache set(%r, %r, %r)', cache_key,
|
logger.debug('cache set(%r, %r, %r)', cache_key,
|
||||||
get_cache_value(value),
|
get_cache_value(value),
|
||||||
SETTING_CACHE_TIMEOUT)
|
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)
|
self.cache.set(cache_key, get_cache_value(value), timeout=SETTING_CACHE_TIMEOUT)
|
||||||
if value == SETTING_CACHE_NOTSET and not SETTING_CACHE_DEFAULTS:
|
if value == SETTING_CACHE_NOTSET and not SETTING_CACHE_DEFAULTS:
|
||||||
try:
|
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(
|
settings.registry.register(
|
||||||
'AWX_ENCRYPTED',
|
'AWX_ENCRYPTED',
|
||||||
field_class=fields.CharField,
|
field_class=fields.CharField,
|
||||||
@@ -402,6 +415,11 @@ def test_settings_use_an_encrypted_cache(settings):
|
|||||||
assert isinstance(settings.cache, EncryptedCacheProxy)
|
assert isinstance(settings.cache, EncryptedCacheProxy)
|
||||||
assert settings.cache.__dict__['encrypter'] == encrypt_field
|
assert settings.cache.__dict__['encrypter'] == encrypt_field
|
||||||
assert settings.cache.__dict__['decrypter'] == decrypt_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):
|
def test_sensitive_cache_data_is_encrypted(settings, mocker):
|
||||||
|
|||||||
Reference in New Issue
Block a user