implement GET /v3/catalog
Change-Id: I0ed53f4758cc6bd8771ef2b4b291480bbbfc1631 blueprint: get-catalog
This commit is contained in:
parent
6ee93ca415
commit
d39f9f2bfa
|
@ -25,6 +25,8 @@
|
|||
"identity:update_endpoint": "rule:admin_required",
|
||||
"identity:delete_endpoint": "rule:admin_required",
|
||||
|
||||
"identity:get_catalog": "",
|
||||
|
||||
"identity:get_domain": "rule:admin_required",
|
||||
"identity:list_domains": "rule:admin_required",
|
||||
"identity:create_domain": "rule:admin_required",
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
"identity:update_endpoint": "rule:cloud_admin",
|
||||
"identity:delete_endpoint": "rule:cloud_admin",
|
||||
|
||||
"identity:get_catalog": "",
|
||||
|
||||
"identity:get_domain": "rule:cloud_admin",
|
||||
"identity:list_domains": "rule:cloud_admin",
|
||||
"identity:create_domain": "rule:cloud_admin",
|
||||
|
|
|
@ -17,6 +17,7 @@ import uuid
|
|||
|
||||
import six
|
||||
|
||||
from keystone.common import authorization
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import wsgi
|
||||
|
@ -138,6 +139,36 @@ class Endpoint(controller.V2Controller):
|
|||
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class CatalogV3(controller.V3Controller):
|
||||
collection_name = 'catalog'
|
||||
|
||||
@controller.protected()
|
||||
def get_catalog(self, context):
|
||||
# TODO(dolphm): this method of accessing the auth context is terrible,
|
||||
# but context needs to be refactored to always have reasonable values.
|
||||
env_context = context.get('environment', {})
|
||||
auth_context = env_context.get(authorization.AUTH_CONTEXT_ENV, {})
|
||||
user_id = auth_context.get('user_id')
|
||||
project_id = auth_context.get('project_id')
|
||||
|
||||
if not user_id or not project_id:
|
||||
raise exception.Forbidden(
|
||||
_('A project-scoped token is required to produce a service '
|
||||
'catalog.'))
|
||||
|
||||
# The V3Controller base methods mostly assume that you're returning
|
||||
# either a collection or a single element from a collection, neither of
|
||||
# which apply to the catalog. Because this is a special case, this
|
||||
# re-implements a tiny bit of work done by the base controller (such as
|
||||
# self-referential link building) to avoid overriding or refactoring
|
||||
# several private methods.
|
||||
return {
|
||||
'catalog': self.catalog_api.get_v3_catalog(user_id, project_id),
|
||||
'links': {
|
||||
'self': CatalogV3.base_url(context)}}
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class RegionV3(controller.V3Controller):
|
||||
collection_name = 'regions'
|
||||
|
|
|
@ -32,3 +32,9 @@ def append_v3_routers(mapper, routers):
|
|||
'services', 'service'))
|
||||
routers.append(router.Router(controllers.EndpointV3(),
|
||||
'endpoints', 'endpoint'))
|
||||
|
||||
mapper.connect(
|
||||
'/catalog',
|
||||
controller=controllers.CatalogV3(),
|
||||
action='get_catalog',
|
||||
conditions=dict(method=['GET']))
|
||||
|
|
|
@ -725,6 +725,35 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase,
|
|||
|
||||
return self.assertDictEqual(normalize(a), normalize(b))
|
||||
|
||||
# catalog validation
|
||||
|
||||
def assertValidCatalogResponse(self, resp, *args, **kwargs):
|
||||
self.assertEqual(['catalog', 'links'], resp.json.keys())
|
||||
self.assertValidCatalog(resp.json['catalog'])
|
||||
self.assertIn('links', resp.json)
|
||||
self.assertIsInstance(resp.json['links'], dict)
|
||||
self.assertEqual(['self'], resp.json['links'].keys())
|
||||
self.assertEqual(
|
||||
'http://localhost/v3/catalog',
|
||||
resp.json['links']['self'])
|
||||
|
||||
def assertValidCatalog(self, entity):
|
||||
self.assertIsInstance(entity, list)
|
||||
self.assertTrue(len(entity) > 0)
|
||||
for service in entity:
|
||||
self.assertIsNotNone(service.get('id'))
|
||||
self.assertIsNotNone(service.get('name'))
|
||||
self.assertIsNotNone(service.get('type'))
|
||||
self.assertNotIn('enabled', service)
|
||||
self.assertTrue(len(service['endpoints']) > 0)
|
||||
for endpoint in service['endpoints']:
|
||||
self.assertIsNotNone(endpoint.get('id'))
|
||||
self.assertIsNotNone(endpoint.get('interface'))
|
||||
self.assertIsNotNone(endpoint.get('url'))
|
||||
self.assertNotIn('enabled', endpoint)
|
||||
self.assertNotIn('legacy_endpoint_id', endpoint)
|
||||
self.assertNotIn('service_id', endpoint)
|
||||
|
||||
# region validation
|
||||
|
||||
def assertValidRegionListResponse(self, resp, *args, **kwargs):
|
||||
|
|
|
@ -24,6 +24,43 @@ from keystone.tests import test_v3
|
|||
class CatalogTestCase(test_v3.RestfulTestCase):
|
||||
"""Test service & endpoint CRUD."""
|
||||
|
||||
def test_get_catalog_project_scoped_token(self):
|
||||
"""Call ``GET /catalog`` with a project-scoped token."""
|
||||
r = self.get(
|
||||
'/catalog',
|
||||
expected_status=200)
|
||||
self.assertValidCatalogResponse(r)
|
||||
|
||||
def test_get_catalog_domain_scoped_token(self):
|
||||
"""Call ``GET /catalog`` with a domain-scoped token."""
|
||||
# grant a domain role to a user
|
||||
self.put(path='/domains/%s/users/%s/roles/%s' % (
|
||||
self.domain['id'], self.user['id'], self.role['id']))
|
||||
|
||||
self.get(
|
||||
'/catalog',
|
||||
auth=self.build_authentication_request(
|
||||
user_id=self.user['id'],
|
||||
password=self.user['password'],
|
||||
domain_id=self.domain['id']),
|
||||
expected_status=403)
|
||||
|
||||
def test_get_catalog_unscoped_token(self):
|
||||
"""Call ``GET /catalog`` with an unscoped token."""
|
||||
self.get(
|
||||
'/catalog',
|
||||
auth=self.build_authentication_request(
|
||||
user_id=self.default_domain_user['id'],
|
||||
password=self.default_domain_user['password']),
|
||||
expected_status=403)
|
||||
|
||||
def test_get_catalog_no_token(self):
|
||||
"""Call ``GET /catalog`` without a token."""
|
||||
self.get(
|
||||
'/catalog',
|
||||
noauth=True,
|
||||
expected_status=401)
|
||||
|
||||
# region crud tests
|
||||
|
||||
def test_create_region_with_id(self):
|
||||
|
@ -358,9 +395,6 @@ class CatalogTestCase(test_v3.RestfulTestCase):
|
|||
body={'endpoint': ref},
|
||||
expected_status=400)
|
||||
|
||||
def assertValidErrorResponse(self, response):
|
||||
self.assertIn(response.status_code, [400, 409])
|
||||
|
||||
def test_create_endpoint_400(self):
|
||||
"""Call ``POST /endpoints``."""
|
||||
ref = self.new_endpoint_ref(service_id=self.service_id)
|
||||
|
|
Loading…
Reference in New Issue