Relational API links

- Adds a self-relational link to both individual API entities and
  collections.
- Adds null previous/next links to collections to support pagination in
  the future.
- Temporarily disables pagination (fixes bug 1079661), as the client
  doesn't currently support or expect it. We probably need to return all
  results by default unless the client requests a limited resultset
  anyway.
- Expands test coverage for granting roles to users & groups in projects
  & domains.

Change-Id: Ib5d6d39f5e1eb673c3285ef0b98603c5d375de75
This commit is contained in:
Dolph Mathews 2013-02-07 15:09:18 -06:00
parent 2a8d3e0acb
commit 1573973941
9 changed files with 253 additions and 173 deletions

View File

@ -120,31 +120,34 @@ class Endpoint(controller.V2Controller):
@dependency.requires('catalog_api') @dependency.requires('catalog_api')
class ServiceV3(controller.V3Controller): class ServiceV3(controller.V3Controller):
collection_name = 'services'
member_name = 'service'
@controller.protected @controller.protected
def create_service(self, context, service): def create_service(self, context, service):
ref = self._assign_unique_id(self._normalize_dict(service)) ref = self._assign_unique_id(self._normalize_dict(service))
self._require_attribute(ref, 'type') self._require_attribute(ref, 'type')
ref = self.catalog_api.create_service(context, ref['id'], ref) ref = self.catalog_api.create_service(context, ref['id'], ref)
return {'service': ref} return ServiceV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_services(self, context): def list_services(self, context):
refs = self.catalog_api.list_services(context) refs = self.catalog_api.list_services(context)
refs = self._filter_by_attribute(context, refs, 'type') refs = self._filter_by_attribute(context, refs, 'type')
return {'services': self._paginate(context, refs)} return ServiceV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_service(self, context, service_id): def get_service(self, context, service_id):
ref = self.catalog_api.get_service(context, service_id) ref = self.catalog_api.get_service(context, service_id)
return {'service': ref} return ServiceV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_service(self, context, service_id, service): def update_service(self, context, service_id, service):
self._require_matching_id(service_id, service) self._require_matching_id(service_id, service)
ref = self.catalog_api.update_service(context, service_id, service) ref = self.catalog_api.update_service(context, service_id, service)
return {'service': ref} return ServiceV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_service(self, context, service_id): def delete_service(self, context, service_id):
@ -153,6 +156,9 @@ class ServiceV3(controller.V3Controller):
@dependency.requires('catalog_api') @dependency.requires('catalog_api')
class EndpointV3(controller.V3Controller): class EndpointV3(controller.V3Controller):
collection_name = 'endpoints'
member_name = 'endpoint'
@controller.protected @controller.protected
def create_endpoint(self, context, endpoint): def create_endpoint(self, context, endpoint):
ref = self._assign_unique_id(self._normalize_dict(endpoint)) ref = self._assign_unique_id(self._normalize_dict(endpoint))
@ -161,19 +167,19 @@ class EndpointV3(controller.V3Controller):
self.catalog_api.get_service(context, ref['service_id']) self.catalog_api.get_service(context, ref['service_id'])
ref = self.catalog_api.create_endpoint(context, ref['id'], ref) ref = self.catalog_api.create_endpoint(context, ref['id'], ref)
return {'endpoint': ref} return EndpointV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_endpoints(self, context): def list_endpoints(self, context):
refs = self.catalog_api.list_endpoints(context) refs = self.catalog_api.list_endpoints(context)
refs = self._filter_by_attribute(context, refs, 'service_id') refs = self._filter_by_attribute(context, refs, 'service_id')
refs = self._filter_by_attribute(context, refs, 'interface') refs = self._filter_by_attribute(context, refs, 'interface')
return {'endpoints': self._paginate(context, refs)} return EndpointV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_endpoint(self, context, endpoint_id): def get_endpoint(self, context, endpoint_id):
ref = self.catalog_api.get_endpoint(context, endpoint_id) ref = self.catalog_api.get_endpoint(context, endpoint_id)
return {'endpoint': ref} return EndpointV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_endpoint(self, context, endpoint_id, endpoint): def update_endpoint(self, context, endpoint_id, endpoint):
@ -183,7 +189,7 @@ class EndpointV3(controller.V3Controller):
self.catalog_api.get_service(context, endpoint['service_id']) self.catalog_api.get_service(context, endpoint['service_id'])
ref = self.catalog_api.update_endpoint(context, endpoint_id, endpoint) ref = self.catalog_api.update_endpoint(context, endpoint_id, endpoint)
return {'endpoint': ref} return EndpointV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_endpoint(self, context, endpoint_id): def delete_endpoint(self, context, endpoint_id):

View File

@ -4,10 +4,12 @@ import uuid
from keystone.common import dependency from keystone.common import dependency
from keystone.common import logging from keystone.common import logging
from keystone.common import wsgi from keystone.common import wsgi
from keystone import config
from keystone import exception from keystone import exception
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF = config.CONF
def protected(f): def protected(f):
@ -68,10 +70,63 @@ class V2Controller(wsgi.Application):
class V3Controller(V2Controller): class V3Controller(V2Controller):
"""Base controller class for Identity API v3.""" """Base controller class for Identity API v3.
def _paginate(self, context, refs): Child classes should set the ``collection_name`` and ``member_name`` class
attributes, representing the collection of entities they are exposing to
the API. This is required for supporting self-referential links,
pagination, etc.
"""
collection_name = 'entities'
member_name = 'entity'
@classmethod
def base_url(cls, path=None):
endpoint = CONF.public_endpoint % CONF
# allow a missing trailing slash in the config
if endpoint[-1] != '/':
endpoint += '/'
url = endpoint + 'v3'
if path:
return url + path
else:
return url + '/' + cls.collection_name
@classmethod
def _add_self_referential_link(cls, ref):
ref.setdefault('links', {})
ref['links']['self'] = cls.base_url() + '/' + ref['id']
@classmethod
def wrap_member(cls, context, ref):
cls._add_self_referential_link(ref)
return {cls.member_name: ref}
@classmethod
def wrap_collection(cls, context, refs):
refs = cls.paginate(context, refs)
for ref in refs:
cls.wrap_member(context, ref)
container = {cls.collection_name: refs}
container['links'] = {
'next': None,
'self': cls.base_url(path=context['path']),
'previous': None}
return container
@classmethod
def paginate(cls, context, refs):
"""Paginates a list of references by page & per_page query strings.""" """Paginates a list of references by page & per_page query strings."""
# FIXME(dolph): client needs to support pagination first
return refs
page = context['query_string'].get('page', 1) page = context['query_string'].get('page', 1)
per_page = context['query_string'].get('per_page', 30) per_page = context['query_string'].get('per_page', 30)
return refs[per_page * (page - 1):per_page * page] return refs[per_page * (page - 1):per_page * page]

View File

@ -211,6 +211,7 @@ class Application(BaseApplication):
# allow middleware up the stack to provide context & params # allow middleware up the stack to provide context & params
context = req.environ.get(CONTEXT_ENV, {}) context = req.environ.get(CONTEXT_ENV, {})
context['query_string'] = dict(req.params.iteritems()) context['query_string'] = dict(req.params.iteritems())
context['path'] = req.environ['PATH_INFO']
params = req.environ.get(PARAMS_ENV, {}) params = req.environ.get(PARAMS_ENV, {})
if 'REMOTE_USER' in req.environ: if 'REMOTE_USER' in req.environ:
context['REMOTE_USER'] = req.environ['REMOTE_USER'] context['REMOTE_USER'] = req.environ['REMOTE_USER']

View File

@ -391,21 +391,24 @@ class Role(controller.V2Controller):
class DomainV3(controller.V3Controller): class DomainV3(controller.V3Controller):
collection_name = 'domains'
member_name = 'domain'
@controller.protected @controller.protected
def create_domain(self, context, domain): def create_domain(self, context, domain):
ref = self._assign_unique_id(self._normalize_dict(domain)) ref = self._assign_unique_id(self._normalize_dict(domain))
ref = self.identity_api.create_domain(context, ref['id'], ref) ref = self.identity_api.create_domain(context, ref['id'], ref)
return {'domain': ref} return DomainV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_domains(self, context): def list_domains(self, context):
refs = self.identity_api.list_domains(context) refs = self.identity_api.list_domains(context)
return {'domains': self._paginate(context, refs)} return DomainV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_domain(self, context, domain_id): def get_domain(self, context, domain_id):
ref = self.identity_api.get_domain(context, domain_id) ref = self.identity_api.get_domain(context, domain_id)
return {'domain': ref} return DomainV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_domain(self, context, domain_id, domain): def update_domain(self, context, domain_id, domain):
@ -441,7 +444,7 @@ class DomainV3(controller.V3Controller):
user_id=user['id'], user_id=user['id'],
tenant_id=project['id']) tenant_id=project['id'])
return {'domain': ref} return DomainV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_domain(self, context, domain_id): def delete_domain(self, context, domain_id):
@ -455,33 +458,36 @@ class DomainV3(controller.V3Controller):
class ProjectV3(controller.V3Controller): class ProjectV3(controller.V3Controller):
collection_name = 'projects'
member_name = 'project'
@controller.protected @controller.protected
def create_project(self, context, project): def create_project(self, context, project):
ref = self._assign_unique_id(self._normalize_dict(project)) ref = self._assign_unique_id(self._normalize_dict(project))
ref = self.identity_api.create_project(context, ref['id'], ref) ref = self.identity_api.create_project(context, ref['id'], ref)
return {'project': ref} return ProjectV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_projects(self, context): def list_projects(self, context):
refs = self.identity_api.list_projects(context) refs = self.identity_api.list_projects(context)
return {'projects': self._paginate(context, refs)} return ProjectV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def list_user_projects(self, context, user_id): def list_user_projects(self, context, user_id):
refs = self.identity_api.list_user_projects(context, user_id) refs = self.identity_api.list_user_projects(context, user_id)
return {'projects': self._paginate(context, refs)} return ProjectV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_project(self, context, project_id): def get_project(self, context, project_id):
ref = self.identity_api.get_project(context, project_id) ref = self.identity_api.get_project(context, project_id)
return {'project': ref} return ProjectV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_project(self, context, project_id, project): def update_project(self, context, project_id, project):
self._require_matching_id(project_id, project) self._require_matching_id(project_id, project)
ref = self.identity_api.update_project(context, project_id, project) ref = self.identity_api.update_project(context, project_id, project)
return {'project': ref} return ProjectV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_project(self, context, project_id): def delete_project(self, context, project_id):
@ -489,26 +495,29 @@ class ProjectV3(controller.V3Controller):
class UserV3(controller.V3Controller): class UserV3(controller.V3Controller):
collection_name = 'users'
member_name = 'user'
@controller.protected @controller.protected
def create_user(self, context, user): def create_user(self, context, user):
ref = self._assign_unique_id(self._normalize_dict(user)) ref = self._assign_unique_id(self._normalize_dict(user))
ref = self.identity_api.create_user(context, ref['id'], ref) ref = self.identity_api.create_user(context, ref['id'], ref)
return {'user': ref} return UserV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_users(self, context): def list_users(self, context):
refs = self.identity_api.list_users(context) refs = self.identity_api.list_users(context)
return {'users': self._paginate(context, refs)} return UserV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def list_users_in_group(self, context, group_id): def list_users_in_group(self, context, group_id):
refs = self.identity_api.list_users_in_group(context, group_id) refs = self.identity_api.list_users_in_group(context, group_id)
return {'users': self._paginate(context, refs)} return UserV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_user(self, context, user_id): def get_user(self, context, user_id):
ref = self.identity_api.get_user(context, user_id) ref = self.identity_api.get_user(context, user_id)
return {'user': ref} return UserV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_user(self, context, user_id, user): def update_user(self, context, user_id, user):
@ -522,7 +531,7 @@ class UserV3(controller.V3Controller):
context, context,
user_id=user['id']) user_id=user['id'])
return {'user': ref} return UserV3.wrap_member(context, ref)
@controller.protected @controller.protected
def add_user_to_group(self, context, user_id, group_id): def add_user_to_group(self, context, user_id, group_id):
@ -545,33 +554,36 @@ class UserV3(controller.V3Controller):
class GroupV3(controller.V3Controller): class GroupV3(controller.V3Controller):
collection_name = 'groups'
member_name = 'group'
@controller.protected @controller.protected
def create_group(self, context, group): def create_group(self, context, group):
ref = self._assign_unique_id(self._normalize_dict(group)) ref = self._assign_unique_id(self._normalize_dict(group))
ref = self.identity_api.create_group(context, ref['id'], ref) ref = self.identity_api.create_group(context, ref['id'], ref)
return {'group': ref} return GroupV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_groups(self, context): def list_groups(self, context):
refs = self.identity_api.list_groups(context) refs = self.identity_api.list_groups(context)
return {'groups': self._paginate(context, refs)} return GroupV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def list_groups_for_user(self, context, user_id): def list_groups_for_user(self, context, user_id):
refs = self.identity_api.list_groups_for_user(context, user_id) refs = self.identity_api.list_groups_for_user(context, user_id)
return {'groups': self._paginate(context, refs)} return GroupV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_group(self, context, group_id): def get_group(self, context, group_id):
ref = self.identity_api.get_group(context, group_id) ref = self.identity_api.get_group(context, group_id)
return {'group': ref} return GroupV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_group(self, context, group_id, group): def update_group(self, context, group_id, group):
self._require_matching_id(group_id, group) self._require_matching_id(group_id, group)
ref = self.identity_api.update_group(context, group_id, group) ref = self.identity_api.update_group(context, group_id, group)
return {'group': ref} return GroupV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_group(self, context, group_id): def delete_group(self, context, group_id):
@ -579,21 +591,24 @@ class GroupV3(controller.V3Controller):
class CredentialV3(controller.V3Controller): class CredentialV3(controller.V3Controller):
collection_name = 'credentials'
member_name = 'credential'
@controller.protected @controller.protected
def create_credential(self, context, credential): def create_credential(self, context, credential):
ref = self._assign_unique_id(self._normalize_dict(credential)) ref = self._assign_unique_id(self._normalize_dict(credential))
ref = self.identity_api.create_credential(context, ref['id'], ref) ref = self.identity_api.create_credential(context, ref['id'], ref)
return {'credential': ref} return CredentialV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_credentials(self, context): def list_credentials(self, context):
refs = self.identity_api.list_credentials(context) refs = self.identity_api.list_credentials(context)
return {'credentials': self._paginate(context, refs)} return CredentialV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_credential(self, context, credential_id): def get_credential(self, context, credential_id):
ref = self.identity_api.get_credential(context, credential_id) ref = self.identity_api.get_credential(context, credential_id)
return {'credential': ref} return CredentialV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_credential(self, context, credential_id, credential): def update_credential(self, context, credential_id, credential):
@ -603,7 +618,7 @@ class CredentialV3(controller.V3Controller):
context, context,
credential_id, credential_id,
credential) credential)
return {'credential': ref} return CredentialV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_credential(self, context, credential_id): def delete_credential(self, context, credential_id):
@ -611,28 +626,31 @@ class CredentialV3(controller.V3Controller):
class RoleV3(controller.V3Controller): class RoleV3(controller.V3Controller):
collection_name = 'roles'
member_name = 'role'
@controller.protected @controller.protected
def create_role(self, context, role): def create_role(self, context, role):
ref = self._assign_unique_id(self._normalize_dict(role)) ref = self._assign_unique_id(self._normalize_dict(role))
ref = self.identity_api.create_role(context, ref['id'], ref) ref = self.identity_api.create_role(context, ref['id'], ref)
return {'role': ref} return RoleV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_roles(self, context): def list_roles(self, context):
refs = self.identity_api.list_roles(context) refs = self.identity_api.list_roles(context)
return {'roles': self._paginate(context, refs)} return RoleV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_role(self, context, role_id): def get_role(self, context, role_id):
ref = self.identity_api.get_role(context, role_id) ref = self.identity_api.get_role(context, role_id)
return {'role': ref} return RoleV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_role(self, context, role_id, role): def update_role(self, context, role_id, role):
self._require_matching_id(role_id, role) self._require_matching_id(role_id, role)
ref = self.identity_api.update_role(context, role_id, role) ref = self.identity_api.update_role(context, role_id, role)
return {'role': ref} return RoleV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_role(self, context, role_id): def delete_role(self, context, role_id):
@ -655,7 +673,7 @@ class RoleV3(controller.V3Controller):
self._require_domain_xor_project(domain_id, project_id) self._require_domain_xor_project(domain_id, project_id)
self._require_user_xor_group(user_id, group_id) self._require_user_xor_group(user_id, group_id)
return self.identity_api.create_grant( self.identity_api.create_grant(
context, role_id, user_id, group_id, domain_id, project_id) context, role_id, user_id, group_id, domain_id, project_id)
@controller.protected @controller.protected
@ -665,8 +683,9 @@ class RoleV3(controller.V3Controller):
self._require_domain_xor_project(domain_id, project_id) self._require_domain_xor_project(domain_id, project_id)
self._require_user_xor_group(user_id, group_id) self._require_user_xor_group(user_id, group_id)
return self.identity_api.list_grants( refs = self.identity_api.list_grants(
context, user_id, group_id, domain_id, project_id) context, user_id, group_id, domain_id, project_id)
return RoleV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def check_grant(self, context, role_id, user_id=None, group_id=None, def check_grant(self, context, role_id, user_id=None, group_id=None,

View File

@ -18,6 +18,9 @@ from keystone.common import controller
class PolicyV3(controller.V3Controller): class PolicyV3(controller.V3Controller):
collection_name = 'policies'
member_name = 'policy'
@controller.protected @controller.protected
def create_policy(self, context, policy): def create_policy(self, context, policy):
ref = self._assign_unique_id(self._normalize_dict(policy)) ref = self._assign_unique_id(self._normalize_dict(policy))
@ -25,23 +28,23 @@ class PolicyV3(controller.V3Controller):
self._require_attribute(ref, 'type') self._require_attribute(ref, 'type')
ref = self.policy_api.create_policy(context, ref['id'], ref) ref = self.policy_api.create_policy(context, ref['id'], ref)
return {'policy': ref} return PolicyV3.wrap_member(context, ref)
@controller.protected @controller.protected
def list_policies(self, context): def list_policies(self, context):
refs = self.policy_api.list_policies(context) refs = self.policy_api.list_policies(context)
refs = self._filter_by_attribute(context, refs, 'type') refs = self._filter_by_attribute(context, refs, 'type')
return {'policies': self._paginate(context, refs)} return PolicyV3.wrap_collection(context, refs)
@controller.protected @controller.protected
def get_policy(self, context, policy_id): def get_policy(self, context, policy_id):
ref = self.policy_api.get_policy(context, policy_id) ref = self.policy_api.get_policy(context, policy_id)
return {'policy': ref} return PolicyV3.wrap_member(context, ref)
@controller.protected @controller.protected
def update_policy(self, context, policy_id, policy): def update_policy(self, context, policy_id, policy):
ref = self.policy_api.update_policy(context, policy_id, policy) ref = self.policy_api.update_policy(context, policy_id, policy)
return {'policy': ref} return PolicyV3.wrap_member(context, ref)
@controller.protected @controller.protected
def delete_policy(self, context, policy_id): def delete_policy(self, context, policy_id):

View File

@ -2,11 +2,12 @@ import uuid
from keystone.common.sql import util as sql_util from keystone.common.sql import util as sql_util
from keystone import test from keystone import test
from keystone import config
import test_content_types import test_content_types
BASE_URL = 'http://127.0.0.1:35357/v3' CONF = config.CONF
class RestfulTestCase(test_content_types.RestfulTestCase): class RestfulTestCase(test_content_types.RestfulTestCase):
@ -133,7 +134,8 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
def delete(self, path, **kwargs): def delete(self, path, **kwargs):
return self.v3_request(method='DELETE', path=path, **kwargs) return self.v3_request(method='DELETE', path=path, **kwargs)
def assertValidListResponse(self, resp, key, entity_validator, ref=None): def assertValidListResponse(self, resp, key, entity_validator, ref=None,
expected_length=None):
"""Make assertions common to all API list responses. """Make assertions common to all API list responses.
If a reference is provided, it's ID will be searched for in the If a reference is provided, it's ID will be searched for in the
@ -142,7 +144,20 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
""" """
entities = resp.body.get(key) entities = resp.body.get(key)
self.assertIsNotNone(entities) self.assertIsNotNone(entities)
self.assertTrue(len(entities))
if expected_length is not None:
self.assertEqual(len(entities), expected_length)
elif ref is not None:
# we're at least expecting the ref
self.assertTrue(len(entities))
# collections should have relational links
self.assertIsNotNone(resp.body.get('links'))
self.assertIn('previous', resp.body['links'])
self.assertIn('self', resp.body['links'])
self.assertIn('next', resp.body['links'])
self.assertIn(CONF.public_endpoint % CONF, resp.body['links']['self'])
for entity in entities: for entity in entities:
self.assertIsNotNone(entity) self.assertIsNotNone(entity)
self.assertValidEntity(entity) self.assertValidEntity(entity)
@ -173,10 +188,10 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
msg = '%s unnexpectedly None in %s' % (k, entity) msg = '%s unnexpectedly None in %s' % (k, entity)
self.assertIsNotNone(entity.get(k), msg) self.assertIsNotNone(entity.get(k), msg)
# FIXME(dolph): need to test this in v3 self.assertIsNotNone(entity.get('links'))
# self.assertIsNotNone(entity.get('link')) self.assertIsNotNone(entity['links'].get('self'))
# self.assertIsNotNone(entity['link'].get('href')) self.assertIn(CONF.public_endpoint % CONF, entity['links']['self'])
# self.assertEquals(entity['link'].get('rel'), 'self') self.assertIn(entity['id'], entity['links']['self'])
if ref: if ref:
for k in keys: for k in keys:

View File

@ -25,12 +25,12 @@ class CatalogTestCase(test_v3.RestfulTestCase):
# service validation # service validation
def assertValidServiceListResponse(self, resp, ref): def assertValidServiceListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'services', 'services',
self.assertValidService, self.assertValidService,
ref) **kwargs)
def assertValidServiceResponse(self, resp, ref): def assertValidServiceResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -47,12 +47,12 @@ class CatalogTestCase(test_v3.RestfulTestCase):
# endpoint validation # endpoint validation
def assertValidEndpointListResponse(self, resp, ref): def assertValidEndpointListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'endpoints', 'endpoints',
self.assertValidEndpoint, self.assertValidEndpoint,
ref) **kwargs)
def assertValidEndpointResponse(self, resp, ref): def assertValidEndpointResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -82,7 +82,7 @@ class CatalogTestCase(test_v3.RestfulTestCase):
def test_list_services(self): def test_list_services(self):
"""GET /services""" """GET /services"""
r = self.get('/services') r = self.get('/services')
self.assertValidServiceListResponse(r, self.service) self.assertValidServiceListResponse(r, ref=self.service)
def test_get_service(self): def test_get_service(self):
"""GET /services/{service_id}""" """GET /services/{service_id}"""
@ -109,7 +109,7 @@ class CatalogTestCase(test_v3.RestfulTestCase):
def test_list_endpoints(self): def test_list_endpoints(self):
"""GET /endpoints""" """GET /endpoints"""
r = self.get('/endpoints') r = self.get('/endpoints')
self.assertValidEndpointListResponse(r, self.endpoint) self.assertValidEndpointListResponse(r, ref=self.endpoint)
def test_create_endpoint(self): def test_create_endpoint(self):
"""POST /endpoints""" """POST /endpoints"""

View File

@ -49,12 +49,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# domain validation # domain validation
def assertValidDomainListResponse(self, resp, ref): def assertValidDomainListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'domains', 'domains',
self.assertValidDomain, self.assertValidDomain,
ref) **kwargs)
def assertValidDomainResponse(self, resp, ref): def assertValidDomainResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -70,12 +70,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# project validation # project validation
def assertValidProjectListResponse(self, resp, ref): def assertValidProjectListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'projects', 'projects',
self.assertValidProject, self.assertValidProject,
ref) **kwargs)
def assertValidProjectResponse(self, resp, ref): def assertValidProjectResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -92,12 +92,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# user validation # user validation
def assertValidUserListResponse(self, resp, ref): def assertValidUserListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'users', 'users',
self.assertValidUser, self.assertValidUser,
ref) **kwargs)
def assertValidUserResponse(self, resp, ref): def assertValidUserResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -117,12 +117,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# group validation # group validation
def assertValidGroupListResponse(self, resp, ref): def assertValidGroupListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'groups', 'groups',
self.assertValidGroup, self.assertValidGroup,
ref) **kwargs)
def assertValidGroupResponse(self, resp, ref): def assertValidGroupResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -139,12 +139,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# credential validation # credential validation
def assertValidCredentialListResponse(self, resp, ref): def assertValidCredentialListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'credentials', 'credentials',
self.assertValidCredential, self.assertValidCredential,
ref) **kwargs)
def assertValidCredentialResponse(self, resp, ref): def assertValidCredentialResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -166,12 +166,12 @@ class IdentityTestCase(test_v3.RestfulTestCase):
# role validation # role validation
def assertValidRoleListResponse(self, resp, ref): def assertValidRoleListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'roles', 'roles',
self.assertValidRole, self.assertValidRole,
ref) **kwargs)
def assertValidRoleResponse(self, resp, ref): def assertValidRoleResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -186,27 +186,6 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.assertEqual(ref['name'], entity['name']) self.assertEqual(ref['name'], entity['name'])
return entity return entity
# grant validation
def assertValidGrantListResponse(self, resp, ref):
entities = resp.body
self.assertIsNotNone(entities)
self.assertTrue(len(entities))
for i, entity in enumerate(entities):
self.assertValidEntity(entity)
self.assertValidGrant(entity, ref)
if ref and entity['id'] == ref['id'][0]:
self.assertValidEntity(entity, ref)
self.assertValidGrant(entity, ref)
def assertValidGrant(self, entity, ref=None):
self.assertIsNotNone(entity.get('id'))
self.assertIsNotNone(entity.get('name'))
if ref:
self.assertEqual(ref['id'], entity['id'])
self.assertEqual(ref['name'], entity['name'])
return entity
# domain crud tests # domain crud tests
def test_create_domain(self): def test_create_domain(self):
@ -220,7 +199,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_domains(self): def test_list_domains(self):
"""GET /domains""" """GET /domains"""
r = self.get('/domains') r = self.get('/domains')
self.assertValidDomainListResponse(r, self.domain) self.assertValidDomainListResponse(r, ref=self.domain)
def test_get_domain(self): def test_get_domain(self):
"""GET /domains/{domain_id}""" """GET /domains/{domain_id}"""
@ -268,7 +247,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_projects(self): def test_list_projects(self):
"""GET /projects""" """GET /projects"""
r = self.get('/projects') r = self.get('/projects')
self.assertValidProjectListResponse(r, self.project) self.assertValidProjectListResponse(r, ref=self.project)
def test_create_project(self): def test_create_project(self):
"""POST /projects""" """POST /projects"""
@ -314,7 +293,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_users(self): def test_list_users(self):
"""GET /users""" """GET /users"""
r = self.get('/users') r = self.get('/users')
self.assertValidUserListResponse(r, self.user) self.assertValidUserListResponse(r, ref=self.user)
def test_get_user(self): def test_get_user(self):
"""GET /users/{user_id}""" """GET /users/{user_id}"""
@ -340,7 +319,9 @@ class IdentityTestCase(test_v3.RestfulTestCase):
'group_id': self.group_id, 'user_id': self.user_id}) 'group_id': self.group_id, 'user_id': self.user_id})
r = self.get('/groups/%(group_id)s/users' % { r = self.get('/groups/%(group_id)s/users' % {
'group_id': self.group_id}) 'group_id': self.group_id})
self.assertValidUserListResponse(r, self.user) self.assertValidUserListResponse(r, ref=self.user)
self.assertIn('/groups/%(group_id)s/users' % {
'group_id': self.group_id}, r.body['links']['self'])
def test_remove_user_from_group(self): def test_remove_user_from_group(self):
"""DELETE /groups/{group_id}/users/{user_id}""" """DELETE /groups/{group_id}/users/{user_id}"""
@ -376,7 +357,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_groups(self): def test_list_groups(self):
"""GET /groups""" """GET /groups"""
r = self.get('/groups') r = self.get('/groups')
self.assertValidGroupListResponse(r, self.group) self.assertValidGroupListResponse(r, ref=self.group)
def test_get_group(self): def test_get_group(self):
"""GET /groups/{group_id}""" """GET /groups/{group_id}"""
@ -403,7 +384,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_credentials(self): def test_list_credentials(self):
"""GET /credentials""" """GET /credentials"""
r = self.get('/credentials') r = self.get('/credentials')
self.assertValidCredentialListResponse(r, self.credential) self.assertValidCredentialListResponse(r, ref=self.credential)
def test_create_credential(self): def test_create_credential(self):
"""POST /credentials""" """POST /credentials"""
@ -451,7 +432,7 @@ class IdentityTestCase(test_v3.RestfulTestCase):
def test_list_roles(self): def test_list_roles(self):
"""GET /roles""" """GET /roles"""
r = self.get('/roles') r = self.get('/roles')
self.assertValidRoleListResponse(r, self.role) self.assertValidRoleListResponse(r, ref=self.role)
def test_get_role(self): def test_get_role(self):
"""GET /roles/{role_id}""" """GET /roles/{role_id}"""
@ -473,82 +454,82 @@ class IdentityTestCase(test_v3.RestfulTestCase):
self.delete('/roles/%(role_id)s' % { self.delete('/roles/%(role_id)s' % {
'role_id': self.role_id}) 'role_id': self.role_id})
def test_create_user_project_grant(self): def test_crud_user_project_role_grants(self):
"""PUT /projects/{project_id}/users/{user_id}/roles/{role_id}""" collection_url = (
self.put('/projects/%(project_id)s/users/%(user_id)s/roles/' '/projects/%(project_id)s/users/%(user_id)s/roles' % {
'%(role_id)s' % { 'project_id': self.project_id,
'project_id': self.project_id, 'user_id': self.user_id})
'user_id': self.user_id, member_url = '%(collection_url)s/%(role_id)s' % {
'role_id': self.role_id}) 'collection_url': collection_url,
self.head('/projects/%(project_id)s/users/%(user_id)s/roles/' 'role_id': self.role_id}
'%(role_id)s' % {
'project_id': self.project_id,
'user_id': self.user_id,
'role_id': self.role_id})
def test_create_group_project_grant(self): self.put(member_url)
"""PUT /projects/{project_id}/groups/{group_id}/roles/{role_id}""" self.head(member_url)
self.put('/projects/%(project_id)s/groups/%(group_id)s/roles/' r = self.get(collection_url)
'%(role_id)s' % { self.assertValidRoleListResponse(r, ref=self.role)
'project_id': self.project_id, self.assertIn(collection_url, r.body['links']['self'])
'group_id': self.group_id,
'role_id': self.role_id})
self.head('/projects/%(project_id)s/groups/%(group_id)s/roles/'
'%(role_id)s' % {
'project_id': self.project_id,
'group_id': self.group_id,
'role_id': self.role_id})
def test_create_group_domain_grant(self): self.delete(member_url)
"""PUT /domains/{domain_id}/groups/{group_id}/roles/{role_id}""" r = self.get(collection_url)
self.put('/domains/%(domain_id)s/groups/%(group_id)s/roles/' self.assertValidRoleListResponse(r, expected_length=0)
'%(role_id)s' % { self.assertIn(collection_url, r.body['links']['self'])
'domain_id': self.domain_id,
'group_id': self.group_id,
'role_id': self.role_id})
self.head('/domains/%(domain_id)s/groups/%(group_id)s/roles/'
'%(role_id)s' % {
'domain_id': self.domain_id,
'group_id': self.group_id,
'role_id': self.role_id})
def test_list_user_project_grants(self): def test_crud_user_domain_role_grants(self):
"""GET /projects/{project_id}/users/{user_id}/roles""" collection_url = (
self.put('/projects/%(project_id)s/users/%(user_id)s/roles/' '/domains/%(domain_id)s/users/%(user_id)s/roles' % {
'%(role_id)s' % { 'domain_id': self.domain_id,
'project_id': self.project_id, 'user_id': self.user_id})
'user_id': self.user_id, member_url = '%(collection_url)s/%(role_id)s' % {
'role_id': self.role_id}) 'collection_url': collection_url,
r = self.get('/projects/%(project_id)s/users/%(user_id)s/roles' % { 'role_id': self.role_id}
'project_id': self.project_id,
'user_id': self.user_id})
self.assertValidGrantListResponse(r, self.role)
def test_list_group_project_grants(self): self.put(member_url)
"""GET /projects/{project_id}/groups/{group_id}/roles""" self.head(member_url)
self.put('/projects/%(project_id)s/groups/%(group_id)s/roles/' r = self.get(collection_url)
'%(role_id)s' % { self.assertValidRoleListResponse(r, ref=self.role)
'project_id': self.project_id, self.assertIn(collection_url, r.body['links']['self'])
'group_id': self.group_id,
'role_id': self.role_id})
r = self.get('/projects/%(project_id)s/groups/%(group_id)s/roles' % {
'project_id': self.project_id,
'group_id': self.group_id})
self.assertValidGrantListResponse(r, self.role)
def test_delete_group_project_grant(self): self.delete(member_url)
"""DELETE /projects/{project_id}/groups/{group_id}/roles/{role_id}""" r = self.get(collection_url)
self.put('/projects/%(project_id)s/groups/%(group_id)s/roles/' self.assertValidRoleListResponse(r, expected_length=0)
'%(role_id)s' % { self.assertIn(collection_url, r.body['links']['self'])
'project_id': self.project_id,
'group_id': self.group_id, def test_crud_group_project_role_grants(self):
'role_id': self.role_id}) collection_url = (
self.delete('/projects/%(project_id)s/groups/%(group_id)s/roles/' '/projects/%(project_id)s/groups/%(group_id)s/roles' % {
'%(role_id)s' % { 'project_id': self.project_id,
'project_id': self.project_id, 'group_id': self.group_id})
'group_id': self.group_id, member_url = '%(collection_url)s/%(role_id)s' % {
'role_id': self.role_id}) 'collection_url': collection_url,
r = self.get('/projects/%(project_id)s/groups/%(group_id)s/roles' % { 'role_id': self.role_id}
'project_id': self.project_id,
'group_id': self.group_id}) self.put(member_url)
self.assertEquals(len(r.body), 0) self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
self.assertIn(collection_url, r.body['links']['self'])
self.delete(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0)
self.assertIn(collection_url, r.body['links']['self'])
def test_crud_group_domain_role_grants(self):
collection_url = (
'/domains/%(domain_id)s/groups/%(group_id)s/roles' % {
'domain_id': self.domain_id,
'group_id': self.group_id})
member_url = '%(collection_url)s/%(role_id)s' % {
'collection_url': collection_url,
'role_id': self.role_id}
self.put(member_url)
self.head(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, ref=self.role)
self.assertIn(collection_url, r.body['links']['self'])
self.delete(member_url)
r = self.get(collection_url)
self.assertValidRoleListResponse(r, expected_length=0)
self.assertIn(collection_url, r.body['links']['self'])

View File

@ -17,12 +17,12 @@ class PolicyTestCase(test_v3.RestfulTestCase):
# policy validation # policy validation
def assertValidPolicyListResponse(self, resp, ref): def assertValidPolicyListResponse(self, resp, **kwargs):
return self.assertValidListResponse( return self.assertValidListResponse(
resp, resp,
'policies', 'policies',
self.assertValidPolicy, self.assertValidPolicy,
ref) **kwargs)
def assertValidPolicyResponse(self, resp, ref): def assertValidPolicyResponse(self, resp, ref):
return self.assertValidResponse( return self.assertValidResponse(
@ -52,7 +52,7 @@ class PolicyTestCase(test_v3.RestfulTestCase):
def test_list_policies(self): def test_list_policies(self):
"""GET /policies""" """GET /policies"""
r = self.get('/policies') r = self.get('/policies')
self.assertValidPolicyListResponse(r, self.policy) self.assertValidPolicyListResponse(r, ref=self.policy)
def test_get_policy(self): def test_get_policy(self):
"""GET /policies/{policy_id}""" """GET /policies/{policy_id}"""