diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py index bf5ed02fe0..523935d1fe 100644 --- a/keystone/catalog/backends/sql.py +++ b/keystone/catalog/backends/sql.py @@ -282,9 +282,12 @@ class Catalog(catalog.CatalogDriverV8): substitutions.update({'user_id': user_id}) silent_keyerror_failures = [] if tenant_id: - substitutions.update({'tenant_id': tenant_id}) + substitutions.update({ + 'tenant_id': tenant_id, + 'project_id': tenant_id + }) else: - silent_keyerror_failures = ['tenant_id'] + silent_keyerror_failures = ['tenant_id', 'project_id', ] session = sql.get_session() endpoints = (session.query(Endpoint). @@ -339,9 +342,12 @@ class Catalog(catalog.CatalogDriverV8): d.update({'user_id': user_id}) silent_keyerror_failures = [] if tenant_id: - d.update({'tenant_id': tenant_id}) + d.update({ + 'tenant_id': tenant_id, + 'project_id': tenant_id, + }) else: - silent_keyerror_failures = ['tenant_id'] + silent_keyerror_failures = ['tenant_id', 'project_id', ] session = sql.get_session() services = (session.query(Service).filter(Service.enabled == true()). diff --git a/keystone/catalog/backends/templated.py b/keystone/catalog/backends/templated.py index 780bc42898..965b71c74a 100644 --- a/keystone/catalog/backends/templated.py +++ b/keystone/catalog/backends/templated.py @@ -214,9 +214,12 @@ class Catalog(core.Driver): substitutions.update({'user_id': user_id}) silent_keyerror_failures = [] if tenant_id: - substitutions.update({'tenant_id': tenant_id}) + substitutions.update({ + 'tenant_id': tenant_id, + 'project_id': tenant_id, + }) else: - silent_keyerror_failures = ['tenant_id'] + silent_keyerror_failures = ['tenant_id', 'project_id', ] catalog = {} # TODO(davechen): If there is service with no endpoints, we should diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py index 8947c8f01c..384a9b2bda 100644 --- a/keystone/catalog/core.py +++ b/keystone/catalog/core.py @@ -37,7 +37,8 @@ from keystone import notifications CONF = cfg.CONF LOG = log.getLogger(__name__) WHITELISTED_PROPERTIES = [ - 'tenant_id', 'user_id', 'public_bind_host', 'admin_bind_host', + 'tenant_id', 'project_id', 'user_id', + 'public_bind_host', 'admin_bind_host', 'compute_host', 'admin_port', 'public_port', 'public_endpoint', 'admin_endpoint', ] diff --git a/keystone/contrib/endpoint_filter/backends/catalog_sql.py b/keystone/contrib/endpoint_filter/backends/catalog_sql.py index db418087e0..ad39d04587 100644 --- a/keystone/contrib/endpoint_filter/backends/catalog_sql.py +++ b/keystone/contrib/endpoint_filter/backends/catalog_sql.py @@ -26,7 +26,11 @@ CONF = cfg.CONF class EndpointFilterCatalog(sql.Catalog): def get_v3_catalog(self, user_id, project_id): substitutions = dict(CONF.items()) - substitutions.update({'tenant_id': project_id, 'user_id': user_id}) + substitutions.update({ + 'tenant_id': project_id, + 'project_id': project_id, + 'user_id': user_id, + }) services = {} diff --git a/keystone/tests/unit/catalog/test_core.py b/keystone/tests/unit/catalog/test_core.py index 05f60a01c1..93a6a85e3c 100644 --- a/keystone/tests/unit/catalog/test_core.py +++ b/keystone/tests/unit/catalog/test_core.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import uuid + from oslo_config import cfg from keystone.catalog import core @@ -24,12 +26,13 @@ class FormatUrlTests(unit.BaseTestCase): def test_successful_formatting(self): url_template = ('http://$(public_bind_host)s:$(admin_port)d/' - '$(tenant_id)s/$(user_id)s') + '$(tenant_id)s/$(user_id)s/$(project_id)s') + project_id = uuid.uuid4().hex values = {'public_bind_host': 'server', 'admin_port': 9090, - 'tenant_id': 'A', 'user_id': 'B'} + 'tenant_id': 'A', 'user_id': 'B', 'project_id': project_id} actual_url = core.format_url(url_template, values) - expected_url = 'http://server:9090/A/B' + expected_url = 'http://server:9090/A/B/%s' % (project_id,) self.assertEqual(expected_url, actual_url) def test_raises_malformed_on_missing_key(self): @@ -73,7 +76,7 @@ class FormatUrlTests(unit.BaseTestCase): url_template, values) - def test_substitution_with_allowed_keyerror(self): + def test_substitution_with_allowed_tenant_keyerror(self): # No value of 'tenant_id' is passed into url_template. # mod: format_url will return None instead of raising # "MalformedEndpoint" exception. @@ -86,3 +89,17 @@ class FormatUrlTests(unit.BaseTestCase): 'user_id': 'B'} self.assertIsNone(core.format_url(url_template, values, silent_keyerror_failures=['tenant_id'])) + + def test_substitution_with_allowed_project_keyerror(self): + # No value of 'project_id' is passed into url_template. + # mod: format_url will return None instead of raising + # "MalformedEndpoint" exception. + # This is intentional behavior since we don't want to skip + # all the later endpoints once there is an URL of endpoint + # trying to replace 'project_id' with None. + url_template = ('http://$(public_bind_host)s:$(admin_port)d/' + '$(project_id)s/$(user_id)s') + values = {'public_bind_host': 'server', 'admin_port': 9090, + 'user_id': 'B'} + self.assertIsNone(core.format_url(url_template, values, + silent_keyerror_failures=['project_id'])) diff --git a/keystone/tests/unit/test_v3_catalog.py b/keystone/tests/unit/test_v3_catalog.py index 951a30cc0a..e4e0396529 100644 --- a/keystone/tests/unit/test_v3_catalog.py +++ b/keystone/tests/unit/test_v3_catalog.py @@ -743,6 +743,16 @@ class CatalogTestCase(test_v3.RestfulTestCase): url=valid_url) self.post('/endpoints', body={'endpoint': ref}) + def test_endpoint_create_with_valid_url_project_id(self): + """Create endpoint with valid url should be tested,too.""" + valid_url = 'http://127.0.0.1:8774/v1.1/$(project_id)s' + + ref = unit.new_endpoint_ref(self.service_id, + interface='public', + region_id=self.region_id, + url=valid_url) + self.post('/endpoints', body={'endpoint': ref}) + def test_endpoint_create_with_invalid_url(self): """Test the invalid cases: substitutions is not exactly right.""" invalid_urls = [ diff --git a/releasenotes/notes/catalog_project_id-519f5a70f9f7c4c6.yaml b/releasenotes/notes/catalog_project_id-519f5a70f9f7c4c6.yaml new file mode 100644 index 0000000000..e0c381d9b9 --- /dev/null +++ b/releasenotes/notes/catalog_project_id-519f5a70f9f7c4c6.yaml @@ -0,0 +1,9 @@ +--- +deprecations: + - Use of ``$(tenant_id)s`` in the catalog endpoints is deprecated in favor + of ``$(project_id)s``. +features: + - Keystone supports ``$(project_id)s`` in the catalog. It works the same as + ``$(tenant_id)s``. Use of ``$(tenant_id)s`` is deprecated and catalog + endpoints should be updated to use ``$(project_id)s``. +