diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py index c47d337af1..bd0f687c16 100644 --- a/keystone/catalog/backends/sql.py +++ b/keystone/catalog/backends/sql.py @@ -158,6 +158,7 @@ class Catalog(sql.Base, catalog.Driver): internal_url = ep['internalurl'].replace('$(', '%(') public_url = ep['publicurl'].replace('$(', '%(') admin_url = ep['adminurl'].replace('$(', '%(') + catalog[region][srv_type]['id'] = ep['id'] catalog[region][srv_type]['name'] = srv_name catalog[region][srv_type]['publicURL'] = public_url % d catalog[region][srv_type]['adminURL'] = admin_url % d diff --git a/keystone/service.py b/keystone/service.py index ddc9e4a718..59e9d43435 100644 --- a/keystone/service.py +++ b/keystone/service.py @@ -543,7 +543,19 @@ class TokenController(wsgi.Application): def endpoints(self, context, token_id): """Return a list of endpoints available to the token.""" - raise exception.NotImplemented() + self.assert_admin(context) + + token_ref = self._get_token_ref(context, token_id) + + catalog_ref = None + if token_ref.get('tenant'): + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=token_ref['user']['id'], + tenant_id=token_ref['tenant']['id'], + metadata=token_ref['metadata']) + + return self._format_endpoint_list(catalog_ref) def _format_authenticate(self, token_ref, roles_ref, catalog_ref): o = self._format_token(token_ref, roles_ref) @@ -629,6 +641,43 @@ class TokenController(wsgi.Application): return services.values() + def _format_endpoint_list(self, catalog_ref): + """Formats a list of endpoints according to Identity API v2. + + The v2.0 API wants an endpoint list to look like:: + + { + 'endpoints': [ + { + 'id': $endpoint_id, + 'name': $SERVICE[name], + 'type': $SERVICE, + 'tenantId': $tenant_id, + 'region': $REGION, + } + ], + 'endpoints_links': [], + } + + """ + if not catalog_ref: + return {} + + endpoints = [] + for region_name, region_ref in catalog_ref.iteritems(): + for service_type, service_ref in region_ref.iteritems(): + endpoints.append({ + 'id': service_ref.get('id'), + 'name': service_ref.get('name'), + 'type': service_type, + 'region': region_name, + 'publicURL': service_ref.get('publicURL'), + 'internalURL': service_ref.get('internalURL'), + 'adminURL': service_ref.get('adminURL'), + }) + + return {'endpoints': endpoints, 'endpoints_links': []} + class ExtensionsController(wsgi.Application): """Base extensions controller to be extended by public and admin API's.""" diff --git a/tests/default_catalog.templates b/tests/default_catalog.templates index c12b5c4ca7..f26c949ac4 100644 --- a/tests/default_catalog.templates +++ b/tests/default_catalog.templates @@ -4,9 +4,11 @@ catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0 catalog.RegionOne.identity.adminURL = http://localhost:$(admin_port)s/v2.0 catalog.RegionOne.identity.internalURL = http://localhost:$(admin_port)s/v2.0 catalog.RegionOne.identity.name = 'Identity Service' +catalog.RegionOne.identity.id = 1 # fake compute service for now to help novaclient tests work catalog.RegionOne.compute.publicURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s catalog.RegionOne.compute.adminURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s catalog.RegionOne.compute.internalURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s catalog.RegionOne.compute.name = 'Compute Service' +catalog.RegionOne.compute.id = 2 diff --git a/tests/test_backend_templated.py b/tests/test_backend_templated.py index 0b9c16984a..263d5c630f 100644 --- a/tests/test_backend_templated.py +++ b/tests/test_backend_templated.py @@ -38,13 +38,15 @@ class TestTemplatedCatalog(test.TestCase, test_backend.CatalogTests): 'adminURL': 'http://localhost:8774/v1.1/bar', 'publicURL': 'http://localhost:8774/v1.1/bar', 'internalURL': 'http://localhost:8774/v1.1/bar', - 'name': "'Compute Service'" + 'name': "'Compute Service'", + 'id': '2' }, 'identity': { 'adminURL': 'http://localhost:35357/v2.0', 'publicURL': 'http://localhost:5000/v2.0', 'internalURL': 'http://localhost:35357/v2.0', - 'name': "'Identity Service'" + 'name': "'Identity Service'", + 'id': '1' } } } diff --git a/tests/test_content_types.py b/tests/test_content_types.py index 0371c556ee..ea501c094a 100644 --- a/tests/test_content_types.py +++ b/tests/test_content_types.py @@ -413,15 +413,13 @@ class CoreApiTests(object): expected_status=204) def test_endpoints(self): - raise nose.exc.SkipTest('Blocked by bug 933555') - token = self.get_scoped_token() r = self.admin_request( path='/v2.0/tokens/%(token_id)s/endpoints' % { 'token_id': token, }, token=token) - self.assertValidTokenCatalogResponse(r) + self.assertValidEndpointListResponse(r) def test_get_tenant(self): token = self.get_scoped_token() @@ -582,6 +580,17 @@ class JsonTestCase(RestfulTestCase, CoreApiTests): def assertValidVersionResponse(self, r): self.assertValidVersion(r.body.get('version')) + def assertValidEndpointListResponse(self, r): + self.assertIsNotNone(r.body.get('endpoints')) + self.assertTrue(len(r.body['endpoints'])) + for endpoint in r.body['endpoints']: + self.assertIsNotNone(endpoint.get('id')) + self.assertIsNotNone(endpoint.get('name')) + self.assertIsNotNone(endpoint.get('type')) + self.assertIsNotNone(endpoint.get('publicURL')) + self.assertIsNotNone(endpoint.get('internalURL')) + self.assertIsNotNone(endpoint.get('adminURL')) + def test_service_crud_requires_auth(self): """Service CRUD should 401 without an X-Auth-Token (bug 1006822).""" # values here don't matter because we should 401 before they're checked @@ -715,13 +724,18 @@ class XmlTestCase(RestfulTestCase, CoreApiTests): self.assertValidVersion(xml) - def assertValidTokenCatalogResponse(self, r): + def assertValidEndpointListResponse(self, r): xml = r.body self.assertEqual(xml.tag, self._tag('endpoints')) self.assertTrue(len(xml.findall(self._tag('endpoint')))) for endpoint in xml.findall(self._tag('endpoint')): - self.assertIsNotNone(endpoint.get('publicUrl')) + self.assertIsNotNone(endpoint.get('id')) + self.assertIsNotNone(endpoint.get('name')) + self.assertIsNotNone(endpoint.get('type')) + self.assertIsNotNone(endpoint.get('publicURL')) + self.assertIsNotNone(endpoint.get('internalURL')) + self.assertIsNotNone(endpoint.get('adminURL')) def assertValidTenantResponse(self, r): xml = r.body