diff --git a/keystone/assignment/routers.py b/keystone/assignment/routers.py index 9bef401eb2..83af6b11be 100644 --- a/keystone/assignment/routers.py +++ b/keystone/assignment/routers.py @@ -63,7 +63,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, project_controller, path='/users/{user_id}/projects', - get_action='list_user_projects', + get_head_action='list_user_projects', rel=json_home.build_v3_resource_relation('user_projects'), path_vars={ 'user_id': json_home.Parameters.USER_ID, @@ -137,7 +137,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, grant_controller, path='/projects/{project_id}/users/{user_id}/roles', - get_action='list_grants', + get_head_action='list_grants', rel=json_home.build_v3_resource_relation('project_user_roles'), path_vars={ 'project_id': json_home.Parameters.PROJECT_ID, @@ -146,7 +146,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, grant_controller, path='/projects/{project_id}/groups/{group_id}/roles', - get_action='list_grants', + get_head_action='list_grants', rel=json_home.build_v3_resource_relation('project_group_roles'), path_vars={ 'group_id': json_home.Parameters.GROUP_ID, @@ -179,7 +179,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, grant_controller, path='/domains/{domain_id}/users/{user_id}/roles', - get_action='list_grants', + get_head_action='list_grants', rel=json_home.build_v3_resource_relation('domain_user_roles'), path_vars={ 'domain_id': json_home.Parameters.DOMAIN_ID, @@ -188,7 +188,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, grant_controller, path='/domains/{domain_id}/groups/{group_id}/roles', - get_action='list_grants', + get_head_action='list_grants', rel=json_home.build_v3_resource_relation('domain_group_roles'), path_vars={ 'domain_id': json_home.Parameters.DOMAIN_ID, @@ -198,7 +198,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, controllers.RoleAssignmentV3(), path='/role_assignments', - get_action='list_role_assignments_wrapper', + get_head_action='list_role_assignments_wrapper', rel=json_home.build_v3_resource_relation('role_assignments')) if CONF.os_inherit.enabled: diff --git a/keystone/common/router.py b/keystone/common/router.py index 74e03ad26e..113c2b8b7e 100644 --- a/keystone/common/router.py +++ b/keystone/common/router.py @@ -44,12 +44,12 @@ class Router(wsgi.ComposableRouter): collection_path, controller=self.controller, action=self.method_template % 'list_%s' % self.collection_key, - conditions=dict(method=['GET'])) + conditions=dict(method=['GET', 'HEAD'])) mapper.connect( entity_path, controller=self.controller, action=self.method_template % 'get_%s' % self.key, - conditions=dict(method=['GET'])) + conditions=dict(method=['GET', 'HEAD'])) mapper.connect( entity_path, controller=self.controller, diff --git a/keystone/identity/routers.py b/keystone/identity/routers.py index e274d6f444..90cbb267c9 100644 --- a/keystone/identity/routers.py +++ b/keystone/identity/routers.py @@ -50,7 +50,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, user_controller, path='/groups/{group_id}/users', - get_action='list_users_in_group', + get_head_action='list_users_in_group', rel=json_home.build_v3_resource_relation('group_users'), path_vars={ 'group_id': json_home.Parameters.GROUP_ID, @@ -77,7 +77,7 @@ class Routers(wsgi.RoutersBase): self._add_resource( mapper, group_controller, path='/users/{user_id}/groups', - get_action='list_groups_for_user', + get_head_action='list_groups_for_user', rel=json_home.build_v3_resource_relation('user_groups'), path_vars={ 'user_id': json_home.Parameters.USER_ID, diff --git a/keystone/tests/unit/test_v3_assignment.py b/keystone/tests/unit/test_v3_assignment.py index 86fb9f74a3..99017d1a27 100644 --- a/keystone/tests/unit/test_v3_assignment.py +++ b/keystone/tests/unit/test_v3_assignment.py @@ -51,18 +51,21 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.post('/roles', body={'role': {}}, expected_status=http_client.BAD_REQUEST) - def test_list_roles(self): - """Call ``GET /roles``.""" + def test_list_head_roles(self): + """Call ``GET & HEAD /roles``.""" resource_url = '/roles' r = self.get(resource_url) self.assertValidRoleListResponse(r, ref=self.role, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) - def test_get_role(self): - """Call ``GET /roles/{role_id}``.""" - r = self.get('/roles/%(role_id)s' % { - 'role_id': self.role_id}) + def test_get_head_role(self): + """Call ``GET & HEAD /roles/{role_id}``.""" + resource_url = '/roles/%(role_id)s' % { + 'role_id': self.role_id} + r = self.get(resource_url) self.assertValidRoleResponse(r, self.role) + self.head(resource_url, expected_status=http_client.OK) def test_update_role(self): """Call ``PATCH /roles/{role_id}``.""" @@ -115,11 +118,13 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.assertValidRoleListResponse(r, ref=role, resource_url=collection_url, expected_length=2) + self.head(collection_url, expected_status=http_client.OK) self.delete(member_url) r = self.get(collection_url) self.assertValidRoleListResponse(r, ref=self.role, expected_length=1) self.assertIn(collection_url, r.result['links']['self']) + self.head(collection_url, expected_status=http_client.OK) def test_crud_user_project_role_grants_no_user(self): """Grant role on a project to a user that doesn't exist. @@ -153,11 +158,13 @@ class AssignmentTestCase(test_v3.RestfulTestCase, r = self.get(collection_url) self.assertValidRoleListResponse(r, ref=self.role, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) self.delete(member_url) r = self.get(collection_url) self.assertValidRoleListResponse(r, expected_length=0, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) def test_crud_user_domain_role_grants_no_user(self): """Grant role on a domain to a user that doesn't exist. @@ -191,11 +198,13 @@ class AssignmentTestCase(test_v3.RestfulTestCase, r = self.get(collection_url) self.assertValidRoleListResponse(r, ref=self.role, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) self.delete(member_url) r = self.get(collection_url) self.assertValidRoleListResponse(r, expected_length=0, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) def test_crud_group_project_role_grants_no_group(self): """Grant role on a project to a group that doesn't exist. @@ -230,11 +239,13 @@ class AssignmentTestCase(test_v3.RestfulTestCase, r = self.get(collection_url) self.assertValidRoleListResponse(r, ref=self.role, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) self.delete(member_url) r = self.get(collection_url) self.assertValidRoleListResponse(r, expected_length=0, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) def test_crud_group_domain_role_grants_no_group(self): """Grant role on a domain to a group that doesn't exist. @@ -457,8 +468,8 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # Role Assignments tests - def test_get_role_assignments(self): - """Call ``GET /role_assignments``. + def test_get_head_role_assignments(self): + """Call ``GET & HEAD /role_assignments``. The sample data set up already has a user, group and project that is part of self.domain. We use these plus a new user @@ -493,6 +504,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, r = self.get(collection_url) self.assertValidRoleAssignmentListResponse(r, resource_url=collection_url) + self.head(collection_url, expected_status=http_client.OK) existing_assignments = len(r.result.get('role_assignments')) # Now add one of each of the four types of assignment, making sure @@ -507,6 +519,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, expected_length=existing_assignments + 1, resource_url=collection_url) self.assertRoleAssignmentInListResponse(r, gd_entity) + self.head(collection_url, expected_status=http_client.OK) ud_entity = self.build_role_assignment_entity(domain_id=self.domain_id, user_id=user1['id'], @@ -518,6 +531,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, expected_length=existing_assignments + 2, resource_url=collection_url) self.assertRoleAssignmentInListResponse(r, ud_entity) + self.head(collection_url, expected_status=http_client.OK) gp_entity = self.build_role_assignment_entity( project_id=self.project_id, group_id=self.group_id, @@ -529,6 +543,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, expected_length=existing_assignments + 3, resource_url=collection_url) self.assertRoleAssignmentInListResponse(r, gp_entity) + self.head(collection_url, expected_status=http_client.OK) up_entity = self.build_role_assignment_entity( project_id=self.project_id, user_id=user1['id'], @@ -540,6 +555,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, expected_length=existing_assignments + 4, resource_url=collection_url) self.assertRoleAssignmentInListResponse(r, up_entity) + self.head(collection_url, expected_status=http_client.OK) # Now delete the four we added and make sure they are removed # from the collection. @@ -557,6 +573,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.assertRoleAssignmentNotInListResponse(r, ud_entity) self.assertRoleAssignmentNotInListResponse(r, gp_entity) self.assertRoleAssignmentNotInListResponse(r, up_entity) + self.head(collection_url, expected_status=http_client.OK) def test_get_effective_role_assignments(self): """Call ``GET /role_assignments?effective``. @@ -2808,7 +2825,7 @@ class ListUserProjectsTestCase(test_v3.RestfulTestCase): self.roles.append(role) self.users.append(user) - def test_list_all(self): + def test_list_head_all(self): for i in range(len(self.users)): user = self.users[i] auth = self.auths[i] @@ -2818,6 +2835,7 @@ class ListUserProjectsTestCase(test_v3.RestfulTestCase): projects_result = result.json['projects'] self.assertEqual(1, len(projects_result)) self.assertEqual(self.projects[i]['id'], projects_result[0]['id']) + self.head(url, auth=auth, expected_status=http_client.OK) def test_list_enabled(self): for i in range(len(self.users)): diff --git a/keystone/tests/unit/test_v3_catalog.py b/keystone/tests/unit/test_v3_catalog.py index 2eb9db1462..5e5679d521 100644 --- a/keystone/tests/unit/test_v3_catalog.py +++ b/keystone/tests/unit/test_v3_catalog.py @@ -161,10 +161,12 @@ class CatalogTestCase(test_v3.RestfulTestCase): body={'region': ref}, expected_status=http_client.BAD_REQUEST) - def test_list_regions(self): - """Call ``GET /regions``.""" - r = self.get('/regions') + def test_list_head_regions(self): + """Call ``GET & HEAD /regions``.""" + resource_url = '/regions' + r = self.get(resource_url) self.assertValidRegionListResponse(r, ref=self.region) + self.head(resource_url, expected_status=http_client.OK) def _create_region_with_parent_id(self, parent_id=None): ref = unit.new_region_ref(parent_region_id=parent_id) @@ -185,11 +187,13 @@ class CatalogTestCase(test_v3.RestfulTestCase): for region in r.result['regions']: self.assertEqual(parent_id, region['parent_region_id']) - def test_get_region(self): - """Call ``GET /regions/{region_id}``.""" - r = self.get('/regions/%(region_id)s' % { - 'region_id': self.region_id}) + def test_get_head_region(self): + """Call ``GET & HEAD /regions/{region_id}``.""" + resource_url = '/regions/%(region_id)s' % { + 'region_id': self.region_id} + r = self.get(resource_url) self.assertValidRegionResponse(r, self.region) + self.head(resource_url, expected_status=http_client.OK) def test_update_region(self): """Call ``PATCH /regions/{region_id}``.""" @@ -308,10 +312,12 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post('/services', body={'service': ref}, expected_status=http_client.BAD_REQUEST) - def test_list_services(self): - """Call ``GET /services``.""" - r = self.get('/services') + def test_list_head_services(self): + """Call ``GET & HEAD /services``.""" + resource_url = '/services' + r = self.get(resource_url) self.assertValidServiceListResponse(r, ref=self.service) + self.head(resource_url, expected_status=http_client.OK) def _create_random_service(self): ref = unit.new_service_ref() @@ -354,11 +360,13 @@ class CatalogTestCase(test_v3.RestfulTestCase): filtered_service = filtered_service_list[0] self.assertEqual(target_ref['name'], filtered_service['name']) - def test_get_service(self): - """Call ``GET /services/{service_id}``.""" - r = self.get('/services/%(service_id)s' % { - 'service_id': self.service_id}) + def test_get_head_service(self): + """Call ``GET & HEAD /services/{service_id}``.""" + resource_url = '/services/%(service_id)s' % { + 'service_id': self.service_id} + r = self.get(resource_url) self.assertValidServiceResponse(r, self.service) + self.head(resource_url, expected_status=http_client.OK) def test_update_service(self): """Call ``PATCH /services/{service_id}``.""" @@ -376,10 +384,12 @@ class CatalogTestCase(test_v3.RestfulTestCase): # endpoint crud tests - def test_list_endpoints(self): - """Call ``GET /endpoints``.""" - r = self.get('/endpoints') + def test_list_head_endpoints(self): + """Call ``GET & HEAD /endpoints``.""" + resource_url = '/endpoints' + r = self.get(resource_url) self.assertValidEndpointListResponse(r, ref=self.endpoint) + self.head(resource_url, expected_status=http_client.OK) def _create_random_endpoint(self, interface='public', parent_region_id=None): @@ -591,12 +601,13 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post('/endpoints', body={'endpoint': ref}, expected_status=http_client.BAD_REQUEST) - def test_get_endpoint(self): - """Call ``GET /endpoints/{endpoint_id}``.""" - r = self.get( - '/endpoints/%(endpoint_id)s' % { - 'endpoint_id': self.endpoint_id}) + def test_get_head_endpoint(self): + """Call ``GET & HEAD /endpoints/{endpoint_id}``.""" + resource_url = '/endpoints/%(endpoint_id)s' % { + 'endpoint_id': self.endpoint_id} + r = self.get(resource_url) self.assertValidEndpointResponse(r, self.endpoint) + self.head(resource_url, expected_status=http_client.OK) def test_update_endpoint(self): """Call ``PATCH /endpoints/{endpoint_id}``.""" diff --git a/keystone/tests/unit/test_v3_identity.py b/keystone/tests/unit/test_v3_identity.py index d853afc337..9cfcc391cc 100644 --- a/keystone/tests/unit/test_v3_identity.py +++ b/keystone/tests/unit/test_v3_identity.py @@ -219,12 +219,13 @@ class IdentityTestCase(test_v3.RestfulTestCase): self.post('/users', body={'user': {}}, expected_status=http_client.BAD_REQUEST) - def test_list_users(self): - """Call ``GET /users``.""" + def test_list_head_users(self): + """Call ``GET & HEAD /users``.""" resource_url = '/users' r = self.get(resource_url) self.assertValidUserListResponse(r, ref=self.user, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) def test_list_users_with_multiple_backends(self): """Call ``GET /users`` when multiple backends is enabled. @@ -291,11 +292,13 @@ class IdentityTestCase(test_v3.RestfulTestCase): self.assertValidUserListResponse(r, ref=user, resource_url=resource_url) - def test_get_user(self): - """Call ``GET /users/{user_id}``.""" - r = self.get('/users/%(user_id)s' % { - 'user_id': self.user['id']}) + def test_get_head_user(self): + """Call ``GET & HEAD /users/{user_id}``.""" + resource_url = '/users/%(user_id)s' % { + 'user_id': self.user['id']} + r = self.get(resource_url) self.assertValidUserResponse(r, self.user) + self.head(resource_url, expected_status=http_client.OK) def test_get_user_with_default_project(self): """Call ``GET /users/{user_id}`` making sure of default_project_id.""" @@ -310,8 +313,8 @@ class IdentityTestCase(test_v3.RestfulTestCase): self.put('/groups/%(group_id)s/users/%(user_id)s' % { 'group_id': self.group_id, 'user_id': self.user['id']}) - def test_list_groups_for_user(self): - """Call ``GET /users/{user_id}/groups``.""" + def test_list_head_groups_for_user(self): + """Call ``GET & HEAD /users/{user_id}/groups``.""" user1 = unit.create_user(self.identity_api, domain_id=self.domain['id']) user2 = unit.create_user(self.identity_api, @@ -331,6 +334,7 @@ class IdentityTestCase(test_v3.RestfulTestCase): r = self.get(resource_url, auth=auth) self.assertValidGroupListResponse(r, ref=self.group, resource_url=resource_url) + self.head(resource_url, auth=auth, expected_status=http_client.OK) # Administrator is allowed to list others' groups resource_url = ('/users/%(user_id)s/groups' % @@ -338,14 +342,18 @@ class IdentityTestCase(test_v3.RestfulTestCase): r = self.get(resource_url) self.assertValidGroupListResponse(r, ref=self.group, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) # Ordinary users should not be allowed to list other's groups auth = self.build_authentication_request( user_id=user2['id'], password=user2['password']) - r = self.get('/users/%(user_id)s/groups' % { - 'user_id': user1['id']}, auth=auth, - expected_status=exception.ForbiddenAction.code) + resource_url = '/users/%(user_id)s/groups' % { + 'user_id': user1['id']} + self.get(resource_url, auth=auth, + expected_status=exception.ForbiddenAction.code) + self.head(resource_url, auth=auth, + expected_status=exception.ForbiddenAction.code) def test_check_user_in_group(self): """Call ``HEAD /groups/{group_id}/users/{user_id}``.""" @@ -354,8 +362,8 @@ class IdentityTestCase(test_v3.RestfulTestCase): self.head('/groups/%(group_id)s/users/%(user_id)s' % { 'group_id': self.group_id, 'user_id': self.user['id']}) - def test_list_users_in_group(self): - """Call ``GET /groups/{group_id}/users``.""" + def test_list_head_users_in_group(self): + """Call ``GET & HEAD /groups/{group_id}/users``.""" self.put('/groups/%(group_id)s/users/%(user_id)s' % { 'group_id': self.group_id, 'user_id': self.user['id']}) resource_url = ('/groups/%(group_id)s/users' % @@ -365,6 +373,7 @@ class IdentityTestCase(test_v3.RestfulTestCase): resource_url=resource_url) self.assertIn('/groups/%(group_id)s/users' % { 'group_id': self.group_id}, r.result['links']['self']) + self.head(resource_url, expected_status=http_client.OK) def test_remove_user_from_group(self): """Call ``DELETE /groups/{group_id}/users/{user_id}``.""" @@ -515,18 +524,21 @@ class IdentityTestCase(test_v3.RestfulTestCase): self.post('/groups', body={'group': {}}, expected_status=http_client.BAD_REQUEST) - def test_list_groups(self): - """Call ``GET /groups``.""" + def test_list_head_groups(self): + """Call ``GET & HEAD /groups``.""" resource_url = '/groups' r = self.get(resource_url) self.assertValidGroupListResponse(r, ref=self.group, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) - def test_get_group(self): - """Call ``GET /groups/{group_id}``.""" - r = self.get('/groups/%(group_id)s' % { - 'group_id': self.group_id}) + def test_get_head_group(self): + """Call ``GET & HEAD /groups/{group_id}``.""" + resource_url = '/groups/%(group_id)s' % { + 'group_id': self.group_id} + r = self.get(resource_url) self.assertValidGroupResponse(r, self.group) + self.head(resource_url, expected_status=http_client.OK) def test_update_group(self): """Call ``PATCH /groups/{group_id}``.""" diff --git a/keystone/tests/unit/test_v3_policy.py b/keystone/tests/unit/test_v3_policy.py index 76a5208898..6146536bac 100644 --- a/keystone/tests/unit/test_v3_policy.py +++ b/keystone/tests/unit/test_v3_policy.py @@ -15,6 +15,8 @@ import json import uuid +from six.moves import http_client + from keystone.tests import unit from keystone.tests.unit import test_v3 @@ -38,16 +40,20 @@ class PolicyTestCase(test_v3.RestfulTestCase): r = self.post('/policies', body={'policy': ref}) return self.assertValidPolicyResponse(r, ref) - def test_list_policies(self): - """Call ``GET /policies``.""" - r = self.get('/policies') + def test_list_head_policies(self): + """Call ``GET & HEAD /policies``.""" + resource_url = '/policies' + r = self.get(resource_url) self.assertValidPolicyListResponse(r, ref=self.policy) + self.head(resource_url, expected_status=http_client.OK) - def test_get_policy(self): - """Call ``GET /policies/{policy_id}``.""" - r = self.get( - '/policies/%(policy_id)s' % {'policy_id': self.policy_id}) + def test_get_head_policy(self): + """Call ``GET & HEAD /policies/{policy_id}``.""" + resource_url = ('/policies/%(policy_id)s' % + {'policy_id': self.policy_id}) + r = self.get(resource_url) self.assertValidPolicyResponse(r, self.policy) + self.head(resource_url, expected_status=http_client.OK) def test_update_policy(self): """Call ``PATCH /policies/{policy_id}``.""" diff --git a/keystone/tests/unit/test_v3_resource.py b/keystone/tests/unit/test_v3_resource.py index f54fcb5749..8c0d77eef9 100644 --- a/keystone/tests/unit/test_v3_resource.py +++ b/keystone/tests/unit/test_v3_resource.py @@ -129,18 +129,21 @@ class ResourceTestCase(test_v3.RestfulTestCase, self.assertValidDomainResponse(r) self.assertIsNotNone(r.result['domain']) - def test_list_domains(self): - """Call ``GET /domains``.""" + def test_list_head_domains(self): + """Call ``GET & HEAD /domains``.""" resource_url = '/domains' r = self.get(resource_url) self.assertValidDomainListResponse(r, ref=self.domain, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) - def test_get_domain(self): + def test_get_head_domain(self): """Call ``GET /domains/{domain_id}``.""" - r = self.get('/domains/%(domain_id)s' % { - 'domain_id': self.domain_id}) + resource_url = '/domains/%(domain_id)s' % { + 'domain_id': self.domain_id} + r = self.get(resource_url) self.assertValidDomainResponse(r, self.domain) + self.head(resource_url, expected_status=http_client.OK) def test_update_domain(self): """Call ``PATCH /domains/{domain_id}``.""" @@ -541,12 +544,13 @@ class ResourceTestCase(test_v3.RestfulTestCase, # Project CRUD tests - def test_list_projects(self): - """Call ``GET /projects``.""" + def test_list_head_projects(self): + """Call ``GET & HEAD /projects``.""" resource_url = '/projects' r = self.get(resource_url) self.assertValidProjectListResponse(r, ref=self.project, resource_url=resource_url) + self.head(resource_url, expected_status=http_client.OK) def test_create_project(self): """Call ``POST /projects``.""" @@ -752,12 +756,13 @@ class ResourceTestCase(test_v3.RestfulTestCase, """Call ``POST /projects``.""" self._create_projects_hierarchy() - def test_get_project(self): - """Call ``GET /projects/{project_id}``.""" - r = self.get( - '/projects/%(project_id)s' % { - 'project_id': self.project_id}) + def test_get_head_project(self): + """Call ``GET & HEAD /projects/{project_id}``.""" + resource_url = '/projects/%(project_id)s' % { + 'project_id': self.project_id} + r = self.get(resource_url) self.assertValidProjectResponse(r, self.project) + self.head(resource_url, expected_status=http_client.OK) def test_get_project_with_parents_as_list_with_invalid_id(self): """Call ``GET /projects/{project_id}?parents_as_list``."""