From 6b89fb04d58bddc48f6f11d6527f50564f080d5c Mon Sep 17 00:00:00 2001 From: amercader Date: Fri, 6 Oct 2023 13:57:46 +0200 Subject: [PATCH 1/2] Keep key format for CKAN___ env vars if they are declared If a config key is declared in the CKAN config declaration, the key is not further processed. This allows to keep eg upper case setttings like SECRET_KEY, WTF_CSRF_ENABLED etc: CKAN___SECRET_KEY=xxx -> config["SECRET_KEY"] = 'xxx' CKAN___WTF_CSRF_ENABLED=True -> config["WTF_CSRF_ENABLED"] = True --- ckanext/envvars/plugin.py | 41 +++++++++++++++++----- ckanext/envvars/tests/test_base_envvars.py | 37 ++++++++++++++++++- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/ckanext/envvars/plugin.py b/ckanext/envvars/plugin.py index 87b0f02..fc36ffb 100644 --- a/ckanext/envvars/plugin.py +++ b/ckanext/envvars/plugin.py @@ -1,8 +1,14 @@ import logging import os + import ckan.plugins as plugins import ckantoolkit as toolkit +if toolkit.check_ckan_version(min_version='2.10'): + from ckan.common import config_declaration +else: + config_declaration = None + log = logging.getLogger(__name__) @@ -10,16 +16,31 @@ class EnvvarsPlugin(plugins.SingletonPlugin): plugins.implements(plugins.IConfigurer) - @staticmethod - def _envvar_to_ini(key): + # This is only used on CKAN>=2.10 + declared_keys = None + + def _envvar_to_ini(self, key): '''Transforms an env var formatted key to ini formatting.''' + + def _format_key(key): + # Set it to lowercase + key = key.lower() + # Replace dunders with dots + key = key.replace('__', '.') + + return key + # If the key starts with 'CKAN___' (3 underscores). if key.startswith('CKAN___'): key = key[7:] - # Set it to lowercase - key = key.lower() - # Replace dunders with dots - key = key.replace('__', '.') + + if self.declared_keys: + # If the key is defined as present in the env var, leave as is + # (eg uppercase) + if key not in self.declared_keys: + key = _format_key(key) + else: + key = _format_key(key) return key @@ -31,6 +52,11 @@ def update_config(self, config): ckan_vars = ((k, v) for k, v in os.environ.items() if k.startswith('CKAN')) + if config_declaration: + self.declared_keys = [str(k) for k in config_declaration.iter_options()] + else: + self.declared_keys = None + # transform vars into ini settings format ckan_vars = ((self._envvar_to_ini(k), v) for k, v in ckan_vars) @@ -38,6 +64,5 @@ def update_config(self, config): config.update(dict(ckan_vars)) # CKAN >=2.10 normalizes config values - if toolkit.check_ckan_version(min_version='2.10'): - from ckan.common import config_declaration + if config_declaration: config_declaration.normalize(config) diff --git a/ckanext/envvars/tests/test_base_envvars.py b/ckanext/envvars/tests/test_base_envvars.py index e15db8d..07ef60d 100644 --- a/ckanext/envvars/tests/test_base_envvars.py +++ b/ckanext/envvars/tests/test_base_envvars.py @@ -34,8 +34,24 @@ def test_envvartoini_expected_output(self): ] for envkey, inikey in envvar_to_ini_examples: - assert EnvvarsPlugin._envvar_to_ini(envkey) == inikey + assert EnvvarsPlugin()._envvar_to_ini(envkey) == inikey + @pytest.mark.skipif(tk.check_ckan_version(max_version='2.10'), reason="This does not apply to CKAN<2.10") + def test_envvartoini_expected_output_declared_option(self): + ''' + EnvvarsPlugin._envvar_to_ini returns expected transformation of env + var formated keys + ''' + + envvar_to_ini_examples = [ + ('CKAN___SECRET_KEY', 'SECRET_KEY'), + ('CKAN___REMEMBER_COOKIE_DURATION', 'REMEMBER_COOKIE_DURATION'), + ('CKAN___WTF_CSRF_ENABLED', 'WTF_CSRF_ENABLED'), + ('CKAN___NOT_DECLARED', 'not_declared'), + ] + + for envkey, inikey in envvar_to_ini_examples: + assert EnvvarsPlugin()._envvar_to_ini(envkey) == inikey class TestEnvVarsConfig(object): @@ -71,6 +87,25 @@ def test_envvars_values_in_config(self): self._teardown_env_vars(envvar_to_ini_examples) + @pytest.mark.skipif(tk.check_ckan_version(max_version='2.10'), reason="This does not apply to CKAN<2.10") + def test_envvars_values_in_config(self): + + envvar_to_ini_examples = [ + ('CKAN___SECRET_KEY', 'super_secret'), + ('CKAN___REMEMBER_COOKIE_DURATION', '1000'), + ('CKAN___WTF_CSRF_ENABLED', 'True'), + ('CKAN___NOT_DECLARED', 'test'), + ] + + self._setup_env_vars(envvar_to_ini_examples) + + assert tk.config['SECRET_KEY'] == 'super_secret' + assert tk.config['REMEMBER_COOKIE_DURATION'] == 1000 + assert tk.config['WTF_CSRF_ENABLED'] == True + assert tk.config['not_declared'] == 'test' + + self._teardown_env_vars(envvar_to_ini_examples) + class TestCkanCoreEnvVarsConfig(object): From 3deb8874c1b97199daad808d1cd34b1b1e46f847 Mon Sep 17 00:00:00 2001 From: amercader Date: Tue, 10 Oct 2023 15:41:49 +0200 Subject: [PATCH 2/2] Add a special case for SECRET_KEY in CKAN 2.10 --- ckanext/envvars/plugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ckanext/envvars/plugin.py b/ckanext/envvars/plugin.py index fc36ffb..c022fd7 100644 --- a/ckanext/envvars/plugin.py +++ b/ckanext/envvars/plugin.py @@ -54,6 +54,10 @@ def update_config(self, config): if config_declaration: self.declared_keys = [str(k) for k in config_declaration.iter_options()] + # SECRET_KEY is marked as internal in CKAN 2.10 but we want to + # encourage its use so we support it here + if "SECRET_KEY" not in self.declared_keys: + self.declared_keys.append("SECRET_KEY") else: self.declared_keys = None