From 340017f7303cd82ef15f7a3813f6b0c9e72ad81b Mon Sep 17 00:00:00 2001 From: David Stanek Date: Fri, 15 Aug 2014 14:59:48 +0000 Subject: [PATCH] Adds a whitelist for endpoint catalog substitution Change-Id: If02327d70d0143d805969fe927898f08eb84c4c2 Closes-Bug: #1354208 --- keystone/catalog/core.py | 4 ++++ keystone/common/config.py | 10 ++++++++++ keystone/common/utils.py | 12 ++++++++++++ keystone/tests/unit/catalog/test_core.py | 21 +++++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py index fd0ebd1..67a3567 100644 --- a/keystone/catalog/core.py +++ b/keystone/catalog/core.py @@ -23,6 +23,7 @@ from keystone.common import cache from keystone.common import dependency from keystone.common import driver_hints from keystone.common import manager +from keystone.common import utils from keystone import config from keystone import exception from keystone.i18n import _ @@ -45,6 +46,9 @@ def format_url(url, substitutions): :returns: a formatted URL """ + substitutions = utils.WhiteListedFormatter( + CONF.catalog.endpoint_substitution_whitelist, + substitutions) try: result = url.replace('$(', '%(') % substitutions except AttributeError: diff --git a/keystone/common/config.py b/keystone/common/config.py index f694c51..883cb08 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -775,6 +775,16 @@ FILE_OPTIONS = { cfg.IntOpt('list_limit', help='Maximum number of entities that will be returned ' 'in a catalog collection.'), + cfg.ListOpt('endpoint_substitution_whitelist', + default=['tenant_id', 'user_id', 'public_bind_host', + 'admin_bind_host', 'compute_host', 'compute_port', + 'admin_port', 'public_port', 'public_endpoint', + 'admin_endpoint'], + help='List of possible substitutions for use in ' + 'formatting endpoints. Use caution when modifying ' + 'this list. It will give users with permission to ' + 'create endpoints the ability to see those values ' + 'in your configuration file.'), ], 'kvs': [ cfg.ListOpt('backends', default=[], diff --git a/keystone/common/utils.py b/keystone/common/utils.py index 4fe97c8..6834d97 100644 --- a/keystone/common/utils.py +++ b/keystone/common/utils.py @@ -526,3 +526,15 @@ def make_dirs(path, mode=None, user=None, group=None, log=None): raise EnvironmentError("makedirs('%s'): %s" % (path, exc.strerror)) set_permissions(path, mode, user, group, log) + + +class WhiteListedFormatter(object): + + def __init__(self, whitelist, data): + self._whitelist = set(whitelist or []) + self._data = data + + def __getitem__(self, name): + if name not in self._whitelist: + raise KeyError + return self._data[name] diff --git a/keystone/tests/unit/catalog/test_core.py b/keystone/tests/unit/catalog/test_core.py index 7b142ed..684e1dc 100644 --- a/keystone/tests/unit/catalog/test_core.py +++ b/keystone/tests/unit/catalog/test_core.py @@ -10,14 +10,26 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo.config import fixture as config_fixture import testtools from keystone.catalog import core +from keystone import config from keystone import exception +CONF = config.CONF + + class FormatUrlTests(testtools.TestCase): + def setUp(self): + super(FormatUrlTests, self).setUp() + fixture = self.useFixture(config_fixture.Config(CONF)) + fixture.config( + group='catalog', + endpoint_substitution_whitelist=['host', 'port', 'part1', 'part2']) + def test_successful_formatting(self): url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s' values = {'host': 'server', 'port': 9090, 'part1': 'A', 'part2': 'B'} @@ -53,3 +65,12 @@ class FormatUrlTests(testtools.TestCase): _test(None) _test(object()) + + def test_substitution_with_key_not_whitelisted(self): + url_template = 'http://%(host)s:%(port)d/%(part1)s/%(part2)s/%(part3)s' + values = {'host': 'server', 'port': 9090, + 'part1': 'A', 'part2': 'B', 'part3': 'C'} + self.assertRaises(exception.MalformedEndpoint, + core.format_url, + url_template, + values) -- 2.0.4