Add HEAD APIs to federated API

This commit ensures that all Federated GET APIs have a corresponding
HEAD API if not already specified. This is to be consistent with
other APIs in keystone.

Change-Id: Ifc7c9d73eca9f715a0f3f25701bcee389ddc354d
Partial-Bug: 1696574
This commit is contained in:
Lance Bragstad 2017-06-12 21:57:54 +00:00
parent c528539879
commit d7b13fd368
5 changed files with 135 additions and 37 deletions

View File

@ -25,14 +25,32 @@ identity_provider_policies = [
name=base.IDENTITY % 'list_identity_providers',
check_str=base.RULE_ADMIN_REQUIRED,
description='List identity providers.',
operations=[{'path': '/v3/OS-FEDERATION/identity_providers',
'method': 'GET'}]),
operations=[
{
'path': '/v3/OS-FEDERATION/identity_providers',
'method': 'GET'
},
{
'path': '/v3/OS-FEDERATION/identity_providers',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_identity_providers',
check_str=base.RULE_ADMIN_REQUIRED,
description='Get identity provider.',
operations=[{'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
'method': 'GET'}]),
operations=[
{
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
'method': 'GET'
},
{
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_identity_provider',
check_str=base.RULE_ADMIN_REQUIRED,

View File

@ -26,14 +26,32 @@ mapping_policies = [
name=base.IDENTITY % 'get_mapping',
check_str=base.RULE_ADMIN_REQUIRED,
description='Get a federated mapping.',
operations=[{'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
'method': 'GET'}]),
operations=[
{
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
'method': 'GET'
},
{
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_mappings',
check_str=base.RULE_ADMIN_REQUIRED,
description='List federated mappings.',
operations=[{'path': '/v3/OS-FEDERATION/mappings',
'method': 'GET'}]),
operations=[
{
'path': '/v3/OS-FEDERATION/mappings',
'method': 'GET'
},
{
'path': '/v3/OS-FEDERATION/mappings',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_mapping',
check_str=base.RULE_ADMIN_REQUIRED,

View File

@ -26,15 +26,34 @@ service_provider_policies = [
name=base.IDENTITY % 'list_service_providers',
check_str=base.RULE_ADMIN_REQUIRED,
description='List federated service providers.',
operations=[{'path': '/v3/OS-FEDERATION/service_providers',
'method': 'GET'}]),
operations=[
{
'path': '/v3/OS-FEDERATION/service_providers',
'method': 'GET'
},
{
'path': '/v3/OS-FEDERATION/service_providers',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_service_provider',
check_str=base.RULE_ADMIN_REQUIRED,
description='Get federated service provider.',
operations=[{'path': ('/v3/OS-FEDERATION/service_providers/'
'{service_provider_id}'),
'method': 'GET'}]),
operations=[
{
'path': ('/v3/OS-FEDERATION/service_providers/'
'{service_provider_id}'),
'method': 'GET'
},
{
'path': ('/v3/OS-FEDERATION/service_providers/'
'{service_provider_id}'),
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'update_service_provider',
check_str=base.RULE_ADMIN_REQUIRED,

View File

@ -38,7 +38,9 @@ class Routers(wsgi.RoutersBase):
PUT /OS-FEDERATION/identity_providers/{idp_id}
GET /OS-FEDERATION/identity_providers
HEAD /OS-FEDERATION/identity_providers
GET /OS-FEDERATION/identity_providers/{idp_id}
HEAD /OS-FEDERATION/identity_providers/{idp_id}
DELETE /OS-FEDERATION/identity_providers/{idp_id}
PATCH /OS-FEDERATION/identity_providers/{idp_id}
@ -46,8 +48,12 @@ class Routers(wsgi.RoutersBase):
{idp_id}/protocols/{protocol_id}
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols
HEAD /OS-FEDERATION/identity_providers/
{idp_id}/protocols
GET /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
HEAD /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
PATCH /OS-FEDERATION/identity_providers/
{idp_id}/protocols/{protocol_id}
DELETE /OS-FEDERATION/identity_providers/
@ -55,16 +61,22 @@ class Routers(wsgi.RoutersBase):
PUT /OS-FEDERATION/mappings
GET /OS-FEDERATION/mappings
HEAD /OS-FEDERATION/mappings
PATCH /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/mappings/{mapping_id}
HEAD /OS-FEDERATION/mappings/{mapping_id}
DELETE /OS-FEDERATION/mappings/{mapping_id}
GET /OS-FEDERATION/projects
HEAD /OS-FEDERATION/projects
GET /OS-FEDERATION/domains
HEAD /OS-FEDERATION/domains
PUT /OS-FEDERATION/service_providers/{sp_id}
GET /OS-FEDERATION/service_providers
HEAD /OS-FEDERATION/service_providers
GET /OS-FEDERATION/service_providers/{sp_id}
HEAD /OS-FEDERATION/service_providers/{sp_id}
DELETE /OS-FEDERATION/service_providers/{sp_id}
PATCH /OS-FEDERATION/service_providers/{sp_id}
@ -83,6 +95,7 @@ class Routers(wsgi.RoutersBase):
POST /auth/OS-FEDERATION/saml2
POST /auth/OS-FEDERATION/saml2/ecp
GET /OS-FEDERATION/saml2/metadata
HEAD /OS-FEDERATION/saml2/metadata
GET /auth/OS-FEDERATION/websso/{protocol_id}
?origin=https%3A//horizon.example.com
@ -110,7 +123,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers/{idp_id}'),
get_action='get_identity_provider',
get_head_action='get_identity_provider',
put_action='create_identity_provider',
patch_action='update_identity_provider',
delete_action='delete_identity_provider',
@ -121,7 +134,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, idp_controller,
path=self._construct_url('identity_providers'),
get_action='list_identity_providers',
get_head_action='list_identity_providers',
rel=build_resource_relation(resource_name='identity_providers'))
# Protocol CRUD operations
@ -130,7 +143,7 @@ class Routers(wsgi.RoutersBase):
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols/'
'{protocol_id}'),
get_action='get_protocol',
get_head_action='get_protocol',
put_action='create_protocol',
patch_action='update_protocol',
delete_action='delete_protocol',
@ -143,7 +156,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, protocol_controller,
path=self._construct_url('identity_providers/{idp_id}/protocols'),
get_action='list_protocols',
get_head_action='list_protocols',
rel=build_resource_relation(
resource_name='identity_provider_protocols'),
path_vars={
@ -155,7 +168,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings/{mapping_id}'),
get_action='get_mapping',
get_head_action='get_mapping',
put_action='create_mapping',
patch_action='update_mapping',
delete_action='delete_mapping',
@ -167,7 +180,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, mapping_controller,
path=self._construct_url('mappings'),
get_action='list_mappings',
get_head_action='list_mappings',
rel=build_resource_relation(resource_name='mappings'))
# Service Providers CRUD operations
@ -175,7 +188,7 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers/{sp_id}'),
get_action='get_service_provider',
get_head_action='get_service_provider',
put_action='create_service_provider',
patch_action='update_service_provider',
delete_action='delete_service_provider',
@ -187,20 +200,20 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, sp_controller,
path=self._construct_url('service_providers'),
get_action='list_service_providers',
get_head_action='list_service_providers',
rel=build_resource_relation(resource_name='service_providers'))
self._add_resource(
mapper, domain_controller,
path=self._construct_url('domains'),
new_path='/auth/domains',
get_action='list_domains_for_user',
get_head_action='list_domains_for_user',
rel=build_resource_relation(resource_name='domains'))
self._add_resource(
mapper, project_controller,
path=self._construct_url('projects'),
new_path='/auth/projects',
get_action='list_projects_for_user',
get_head_action='list_projects_for_user',
rel=build_resource_relation(resource_name='projects'))
# Auth operations
@ -248,5 +261,5 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, saml_metadata_controller,
path=self._construct_url('saml2/metadata'),
get_action='get_metadata',
get_head_action='get_metadata',
rel=build_resource_relation(resource_name='metadata'))

View File

@ -1137,7 +1137,7 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
self.assertIn('Duplicate remote ID',
resp_data['error']['message'])
def test_list_idps(self, iterations=5):
def test_list_head_idps(self, iterations=5):
"""List all available IdentityProviders.
This test collects ids of created IdPs and
@ -1170,7 +1170,9 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
ids_intersection = entities_ids.intersection(ids)
self.assertEqual(ids_intersection, ids)
def test_filter_list_idp_by_id(self):
self.head(url, expected_status=http_client.OK)
def test_filter_list_head_idp_by_id(self):
def get_id(resp):
r = self._fetch_attribute_from_response(resp,
'identity_provider')
@ -1194,7 +1196,9 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
self.assertThat(filtered_service_list, matchers.HasLength(1))
self.assertEqual(idp1_id, filtered_service_list[0].get('id'))
def test_filter_list_idp_by_enabled(self):
self.head(url, expected_status=http_client.OK)
def test_filter_list_head_idp_by_enabled(self):
def get_id(resp):
r = self._fetch_attribute_from_response(resp,
'identity_provider')
@ -1221,6 +1225,8 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
self.assertThat(filtered_service_list, matchers.HasLength(1))
self.assertEqual(idp1_id, filtered_service_list[0].get('id'))
self.head(url, expected_status=http_client.OK)
def test_check_idp_uniqueness(self):
"""Add same IdP twice.
@ -1241,7 +1247,7 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
self.assertIn('Duplicate entry',
resp_data.get('error', {}).get('message'))
def test_get_idp(self):
def test_get_head_idp(self):
"""Create and later fetch IdP."""
body = self._http_idp_input()
domain = unit.new_domain_ref()
@ -1260,6 +1266,8 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
dummy_validator, keys_to_check=body_keys,
ref=body)
self.head(url, expected_status=http_client.OK)
def test_get_nonexisting_idp(self):
"""Fetch nonexisting IdP entity.
@ -1446,7 +1454,7 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
validate=False,
**kwargs)
def test_get_protocol(self):
def test_get_head_protocol(self):
"""Create and later fetch protocol tied to IdP."""
resp, idp_id, proto = self._assign_protocol_to_idp(
expected_status=http_client.CREATED)
@ -1465,7 +1473,9 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
keys_to_check=reference_keys,
ref=reference)
def test_list_protocols(self):
self.head(url, expected_status=http_client.OK)
def test_list_head_protocols(self):
"""Create set of protocols and later list them.
Compare input and output id sets.
@ -1494,6 +1504,8 @@ class FederatedIdentityProviderTests(test_v3.RestfulTestCase):
protocols_intersection = entities.intersection(protocol_ids)
self.assertEqual(protocols_intersection, set(protocol_ids))
self.head(url, expected_status=http_client.OK)
def test_update_protocols_attribute(self):
"""Update protocol's attribute."""
resp, idp_id, proto = self._assign_protocol_to_idp(
@ -1573,7 +1585,7 @@ class MappingCRUDTests(test_v3.RestfulTestCase):
resp = self._create_default_mapping_entry()
self.assertValidMappingResponse(resp, mapping_fixtures.MAPPING_LARGE)
def test_mapping_list(self):
def test_mapping_list_head(self):
url = self.MAPPING_URL
self._create_default_mapping_entry()
resp = self.get(url)
@ -1582,6 +1594,7 @@ class MappingCRUDTests(test_v3.RestfulTestCase):
self.assertResponseStatus(resp, http_client.OK)
self.assertValidListLinks(resp.result.get('links'))
self.assertEqual(1, len(entities))
self.head(url, expected_status=http_client.OK)
def test_mapping_delete(self):
url = self.MAPPING_URL + '%(mapping_id)s'
@ -1592,13 +1605,14 @@ class MappingCRUDTests(test_v3.RestfulTestCase):
self.assertResponseStatus(resp, http_client.NO_CONTENT)
self.get(url, expected_status=http_client.NOT_FOUND)
def test_mapping_get(self):
def test_mapping_get_head(self):
url = self.MAPPING_URL + '%(mapping_id)s'
resp = self._create_default_mapping_entry()
mapping_id = self._get_id_from_response(resp)
url = url % {'mapping_id': mapping_id}
resp = self.get(url)
self.assertValidMappingResponse(resp, mapping_fixtures.MAPPING_LARGE)
self.head(url, expected_status=http_client.OK)
def test_mapping_update(self):
url = self.MAPPING_URL + '%(mapping_id)s'
@ -3052,7 +3066,7 @@ class FederatedUserTests(test_v3.RestfulTestCase, FederatedSetupMixin):
# compare
self.assertItemsEqual(auth_domains, fed_domains)
def test_list_domains_for_user_duplicates(self):
def test_list_head_domains_for_user_duplicates(self):
# create role
role_ref = unit.new_role_ref()
self.role_api.create_role(role_ref['id'], role_ref)
@ -3065,6 +3079,12 @@ class FederatedUserTests(test_v3.RestfulTestCase, FederatedSetupMixin):
group_domains = r.result['domains']
domain_from_group = group_domains[0]
self.head(
'/OS-FEDERATION/domains',
token=unscoped_token,
expected_status=http_client.OK
)
# assign group domain and role to user, this should create a
# duplicate domain
self.assignment_api.create_grant(role_ref['id'],
@ -3079,7 +3099,7 @@ class FederatedUserTests(test_v3.RestfulTestCase, FederatedSetupMixin):
self.assertNotIn(domain['id'], user_domain_ids)
user_domain_ids.append(domain['id'])
def test_list_projects_for_user_duplicates(self):
def test_list_head_projects_for_user_duplicates(self):
# create role
role_ref = unit.new_role_ref()
self.role_api.create_role(role_ref['id'], role_ref)
@ -3092,6 +3112,12 @@ class FederatedUserTests(test_v3.RestfulTestCase, FederatedSetupMixin):
group_projects = r.result['projects']
project_from_group = group_projects[0]
self.head(
'/OS-FEDERATION/projects',
token=unscoped_token,
expected_status=http_client.OK
)
# assign group project and role to user, this should create a
# duplicate project
self.assignment_api.add_role_to_user_and_project(
@ -4040,9 +4066,10 @@ class IdPMetadataGenerationTests(test_v3.RestfulTestCase):
self.get(self.METADATA_URL,
expected_status=http_client.INTERNAL_SERVER_ERROR)
def test_get_metadata(self):
def test_get_head_metadata(self):
self.config_fixture.config(
group='saml', idp_metadata_path=XMLDIR + '/idp_saml2_metadata.xml')
self.head(self.METADATA_URL, expected_status=http_client.OK)
r = self.get(self.METADATA_URL, response_content_type='text/xml')
self.assertEqual('text/xml', r.headers.get('Content-Type'))
@ -4096,11 +4123,12 @@ class ServiceProviderTests(test_v3.RestfulTestCase):
expected_status=http_client.CREATED)
return resp
def test_get_service_provider(self):
def test_get_head_service_provider(self):
url = self.base_url(suffix=self.SERVICE_PROVIDER_ID)
resp = self.get(url)
self.assertValidEntity(resp.result['service_provider'],
keys_to_check=self.SP_KEYS)
resp = self.head(url, expected_status=http_client.OK)
def test_get_service_provider_fail(self):
url = self.base_url(suffix=uuid.uuid4().hex)
@ -4244,7 +4272,7 @@ class ServiceProviderTests(test_v3.RestfulTestCase):
self.put(url, body={'service_provider': sp},
expected_status=http_client.BAD_REQUEST)
def test_list_service_providers(self):
def test_list_head_service_providers(self):
"""Test listing of service provider objects.
Add two new service providers. List all available service providers.
@ -4277,6 +4305,8 @@ class ServiceProviderTests(test_v3.RestfulTestCase):
service_provider, ref=ref_service_providers[id],
keys_to_check=self.SP_KEYS)
self.head(url, expected_status=http_client.OK)
def test_update_service_provider(self):
"""Update existing service provider.