# Postgres client configuration.
-# (An empty string for host means use the unix socket.)
+# Both pgwui.pg_host and pgwui.pg_port are optional;
+# they default to the Unix PG socket and the default Postgres port.
+# An empty string for host means use the unix socket.
pgwui.pg_host =
pgwui.pg_port = 5432
-# There are occasions when PGWUI uses a default database.
+# There are occasions when PGWUI uses a default database. (optional)
pgwui.default_db = template1
# What PGWUI modules to use.
pgwui_upload
pyramid_debugtoolbar
-# Whether or not to change the db content.
+# Whether or not to change the db content. (required)
pgwui.dry_run = False
# Routing
# For more information on route syntax see:
# https://docs.pylonsproject.org/projects/pyramid/en/master/narr/urldispatch.html#route-pattern-syntax
-# A prefix for all routes. If the prefix is "/a/b/c" then
+# A prefix for all routes. (optional) If the prefix is "/a/b/c" then
# all URLs will begin with something like: http://example.com/a/b/c
# The default is no prefix.
# pgwui.route_prefix =
# the component. So to access the logout page at
# http://example.com/logmeout the line would be: logout = /logmeout
#
+# Overriding routes is optional.
+#
# The default for some PGWUI components are:
# pgwui.routes =
# logout = /logout
# Settings validation
-# Whether or not to validate the session.secret setting.
+# Whether or not to validate the session.secret setting. (optional)
# session.secret must be valid to detect Cross-Site Request Forgery (CSRF)
# vulnerabilties. Validation is on by default.
pgwui.validate_hmac = False
# Postgres client configuration.
-# (An empty string for host means use the unix socket.)
+# Both pgwui.pg_host and pgwui.pg_port are optional;
+# they default to the Unix PG socket and the default Postgres port.
+# An empty string for host means use the unix socket.
pgwui.pg_host =
pgwui.pg_port = 5432
-# There are occasions when PGWUI uses a default database.
+# There are occasions when PGWUI uses a default database. (optional)
pgwui.default_db = template1
# What PGWUI modules to use.
pgwui_logout
pgwui_upload
-# Whether or not to change the db content.
+# Whether or not to change the db content. (required)
pgwui.dry_run = False
# Routing
# For more information on route syntax see:
# https://docs.pylonsproject.org/projects/pyramid/en/master/narr/urldispatch.html#route-pattern-syntax
-# A prefix for all routes. If the prefix is "/a/b/c" then
+# A prefix for all routes. (optional) If the prefix is "/a/b/c" then
# all URLs will begin with something like: http://example.com/a/b/c
# The default is no prefix.
# pgwui.route_prefix =
# the component. So to access the logout page at
# http://example.com/logmeout the line would be: logout = /logmeout
#
+# Overriding routes is optional.
+#
# The default for some PGWUI components are:
# pgwui.routes =
# logout = /logmeout
# upload = /put-in
-
-
# Settings validation
-# Whether or not to validate the session.secret setting.
+# Whether or not to validate the session.secret setting. (optional)
# session.secret must be valid to detect Cross-Site Request Forgery (CSRF)
# vulnerabilties. Validation is on by default.
# pgwui.validate_hmac = True
super().__init__('Unknown PGWUI setting: {}'.format(key))
+class MissingSettingError(Error):
+ def __init__(self, key):
+ super().__init__('Missing PGWUI setting: {}'.format(key))
+
+
+class NotBooleanSettingError(Error):
+ def __init__(self, key, value):
+ super().__init__(
+ 'The "{}" PGWUI setting must be "True" or "False"'
+ .format(key))
+
+
class BadHMACError(Error):
pass
raise UnknownSettingKeyError(key)
+def require_setting(setting, settings):
+ if setting not in settings:
+ raise MissingSettingError(setting)
+
+
+def boolean_setting(setting, settings):
+ if setting in settings:
+ val = literal_eval(settings[setting])
+ if (val is not True
+ and val is not False):
+ raise NotBooleanSettingError(setting, settings[setting])
+
+
+def validate_setting_values(settings):
+ '''Check each settings value for validity
+ '''
+ # pg_host can be missing, it defaults to the Unix socket (in psycopg2)
+
+ # pg_port can be missing, it defaults to 5432 (in psycopg2)
+
+ # default_db can be missing, then the user sees no default
+
+ # dry_run
+ require_setting('pgwui.dry_run', settings)
+ boolean_setting('pgwui.dry_run', settings)
+
+ # route_prefix can be missing, defaults to no route prefix which is fine.
+
+ # routes can be missing, sensible defaults are built-in.
+
+ # validate_hmac
+ boolean_setting('pgwui.validate_hmac', settings)
+
+
+def supply_default_settings(settings):
+ '''Supply sensible defaults for omitted settings.
+ '''
+ settings['pgwui.default_db'] = ''
+
+
def do_validate_hmac(settings):
'''True unless the user has specificly rejected hmac validation
'''
'''
for key in settings.keys():
abort_on_bad_setting(key)
+ validate_setting_values(settings)
+ supply_default_settings(settings)
validate_hmac(settings)
TEST_SETTINGS = {
'pgwui.validate_hmac': 'False',
+ 'pgwui.dry_run': 'False',
}
pgwui_server_init.abort_on_bad_setting('pgwui.pg_host')
+# require_setting()
+
+def test_require_setting_missing():
+ '''Raise an exception when a required setting is missing'''
+ with pytest.raises(pgwui_server_init.MissingSettingError):
+ pgwui_server_init.require_setting('key', {})
+
+
+def test_require_setting_present():
+ '''Does nothing when a required setting is present'''
+ pgwui_server_init.require_setting('key',
+ {'key': 'value'})
+
+
+# boolean_setting()
+
+def test_boolean_setting_missing():
+ '''Does nothing when the setting is not in the settings'''
+ pgwui_server_init.boolean_setting('key', {})
+
+
+def test_boolean_setting_true():
+ '''Does nothing when the setting is "True"'''
+ pgwui_server_init.boolean_setting('key',
+ {'key': 'True'})
+
+
+def test_boolean_setting_false():
+ '''Does nothing when the setting is "False"'''
+ pgwui_server_init.boolean_setting('key',
+ {'key': 'False'})
+
+
+def test_boolean_setting_notboolean():
+ '''Raise an exception when the setting does not evaluate to a boolean'''
+ with pytest.raises(pgwui_server_init.NotBooleanSettingError):
+ pgwui_server_init.boolean_setting('key',
+ {'key': '0'})
+
+
+# validate_setting_values()
+
+def test_validate_setting_values(monkeypatch):
+ '''Calls require_setting() and boolean_setting()'''
+ require_setting_called = False
+ boolean_setting_called = False
+
+ def mock_require_setting(*args):
+ nonlocal require_setting_called
+ require_setting_called = True
+
+ def mock_boolean_setting(*args):
+ nonlocal boolean_setting_called
+ boolean_setting_called = True
+
+ monkeypatch.setattr(pgwui_server_init, 'require_setting',
+ mock_require_setting)
+ monkeypatch.setattr(pgwui_server_init, 'boolean_setting',
+ mock_boolean_setting)
+
+ pgwui_server_init.validate_setting_values({})
+
+ assert require_setting_called
+ assert boolean_setting_called
+
+
+# supply_default_settings()
+def test_supply_default_settings():
+ '''Something is changed in the supplied settings'''
+ assert {} != pgwui_server_init.supply_default_settings({})
+
+
# do_validate_hmac()
def test_do_validate_hmac_none():
monkeypatch.setattr(pgwui_server_init, 'abort_on_bad_setting',
mock_abort_on_bad_setting)
+ monkeypatch.setattr(pgwui_server_init, 'validate_setting_values',
+ lambda *args: None)
+ monkeypatch.setattr(pgwui_server_init, 'supply_default_settings',
+ lambda arg: arg)
monkeypatch.setattr(pgwui_server_init, 'validate_hmac',
lambda *args: None)
settings = {'key1': 'value1',
# Functional tests
+
def test_unknownsettingkeyerror():
'''Takes an argument'''
assert isinstance(pgwui_server_init.UnknownSettingKeyError('key'),
- Exception)
+ pgwui_server_init.Error)
+
+
+def test_missingsettingerror():
+ '''Takes an argument'''
+ assert isinstance(pgwui_server_init.MissingSettingError('key'),
+ pgwui_server_init.Error)
+
+
+def test_notbooleansettingerror():
+ '''Takes two arguments'''
+ assert isinstance(pgwui_server_init.NotBooleanSettingError('key', 'val'),
+ pgwui_server_init.Error)