Add HEAD API to auth

This commit adds HEAD counterparts to all GET APIs within the
authentication API.

There was also some inconsistencies in the existing documention
saying a few GET APIs return 204 No Content when the response
was actually 200 OK. This corrects that as well.

Change-Id: Ic1f74b73e27a14428b129bed610d3d1d3d18f24e
Partial-Bug: 1696574
This commit is contained in:
Lance Bragstad 2017-06-10 04:27:43 +00:00
parent c528539879
commit e156d337a7
4 changed files with 105 additions and 25 deletions

View File

@ -582,7 +582,7 @@ to based on the X-Auth-Token provided in the request.
The structure of the response is exactly the same as listing projects
for a user.
Normal response codes: 204
Normal response codes: 200
Error response codes: 413,415,405,404,403,401,400,503,409
@ -625,7 +625,7 @@ to based on the X-Auth-Token provided in the request.
The structure is the same as listing domains.
Normal response codes: 204
Normal response codes: 200
Error response codes: 413,415,405,404,403,401,400,503,409

View File

@ -34,24 +34,24 @@ class Routers(wsgi.RoutersBase):
self._add_resource(
mapper, auth_controller,
path='/auth/tokens/OS-PKI/revoked',
get_action='revocation_list',
get_head_action='revocation_list',
rel=json_home.build_v3_extension_resource_relation(
'OS-PKI', '1.0', 'revocations'))
self._add_resource(
mapper, auth_controller,
path='/auth/catalog',
get_action='get_auth_catalog',
get_head_action='get_auth_catalog',
rel=json_home.build_v3_resource_relation('auth_catalog'))
self._add_resource(
mapper, auth_controller,
path='/auth/projects',
get_action='get_auth_projects',
get_head_action='get_auth_projects',
rel=json_home.build_v3_resource_relation('auth_projects'))
self._add_resource(
mapper, auth_controller,
path='/auth/domains',
get_action='get_auth_domains',
get_head_action='get_auth_domains',
rel=json_home.build_v3_resource_relation('auth_domains'))

View File

@ -19,22 +19,49 @@ auth_policies = [
name=base.IDENTITY % 'get_auth_catalog',
check_str='',
description='Get service catalog.',
operations=[{'path': '/v3/auth/catalog',
'method': 'GET'}]),
operations=[
{
'path': '/v3/auth/catalog',
'method': 'GET'
},
{
'path': '/v3/auth/catalog',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_auth_projects',
check_str='',
description=('List all projects a user has access to via role '
'assignments.'),
operations=[{'path': '/v3/auth/projects',
'method': 'GET'}]),
operations=[
{
'path': '/v3/auth/projects',
'method': 'GET'
},
{
'path': '/v3/auth/projects',
'method': 'HEAD'
}
]
),
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_auth_domains',
check_str='',
description=('List all domains a user has access to via role '
'assignments.'),
operations=[{'path': '/v3/auth/domains',
'method': 'GET'}]),
operations=[
{
'path': '/v3/auth/domains',
'method': 'GET'
},
{
'path': '/v3/auth/domains',
'method': 'HEAD'
}
]
)
]

View File

@ -3733,10 +3733,14 @@ class TestTokenRevokeApi(TestTokenRevokeById):
events_response,
audit_id=response['audit_ids'][0])
def test_revoke_by_id_false_returns_gone(self):
def test_get_revoke_by_id_false_returns_gone(self):
self.get('/auth/tokens/OS-PKI/revoked',
expected_status=http_client.GONE)
def test_head_revoke_by_id_false_returns_gone(self):
self.head('/auth/tokens/OS-PKI/revoked',
expected_status=http_client.GONE)
def test_list_delete_project_shows_in_event_list(self):
self.role_data_fixtures()
events = self.get('/OS-REVOKE/events').json_body['events']
@ -5043,12 +5047,16 @@ class TestAuthContext(unit.TestCase):
class TestAuthSpecificData(test_v3.RestfulTestCase):
def test_get_catalog_project_scoped_token(self):
def test_get_catalog_with_project_scoped_token(self):
"""Call ``GET /auth/catalog`` with a project-scoped token."""
r = self.get('/auth/catalog')
r = self.get('/auth/catalog', expected_status=http_client.OK)
self.assertValidCatalogResponse(r)
def test_get_catalog_domain_scoped_token(self):
def test_head_catalog_with_project_scoped_token(self):
"""Call ``HEAD /auth/catalog`` with a project-scoped token."""
self.head('/auth/catalog', expected_status=http_client.OK)
def test_get_catalog_with_domain_scoped_token(self):
"""Call ``GET /auth/catalog`` with a domain-scoped token."""
# grant a domain role to a user
self.put(path='/domains/%s/users/%s/roles/%s' % (
@ -5062,7 +5070,21 @@ class TestAuthSpecificData(test_v3.RestfulTestCase):
domain_id=self.domain['id']),
expected_status=http_client.FORBIDDEN)
def test_get_catalog_unscoped_token(self):
def test_head_catalog_with_domain_scoped_token(self):
"""Call ``HEAD /auth/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.head(
'/auth/catalog',
auth=self.build_authentication_request(
user_id=self.user['id'],
password=self.user['password'],
domain_id=self.domain['id']),
expected_status=http_client.FORBIDDEN)
def test_get_catalog_with_unscoped_token(self):
"""Call ``GET /auth/catalog`` with an unscoped token."""
self.get(
'/auth/catalog',
@ -5071,26 +5093,51 @@ class TestAuthSpecificData(test_v3.RestfulTestCase):
password=self.default_domain_user['password']),
expected_status=http_client.FORBIDDEN)
def test_get_catalog_no_token(self):
"""Call ``GET /auth/catalog`` without a token."""
def test_head_catalog_with_unscoped_token(self):
"""Call ``HEAD /auth/catalog`` with an unscoped token."""
self.head(
'/auth/catalog',
auth=self.build_authentication_request(
user_id=self.default_domain_user['id'],
password=self.default_domain_user['password']),
expected_status=http_client.FORBIDDEN)
def test_get_head_catalog_no_token(self):
"""Call ``GET & HEAD /auth/catalog`` without a token."""
self.get(
'/auth/catalog',
noauth=True,
expected_status=http_client.UNAUTHORIZED)
expected_status=http_client.UNAUTHORIZED
)
def test_get_projects_project_scoped_token(self):
r = self.get('/auth/projects')
self.head(
'/auth/catalog',
noauth=True,
expected_status=http_client.UNAUTHORIZED
)
def test_get_projects_with_project_scoped_token(self):
r = self.get('/auth/projects', expected_status=http_client.OK)
self.assertThat(r.json['projects'], matchers.HasLength(1))
self.assertValidProjectListResponse(r)
def test_get_domains_project_scoped_token(self):
def test_head_projects_with_project_scoped_token(self):
self.head('/auth/projects', expected_status=http_client.OK)
def test_get_domains_with_project_scoped_token(self):
self.put(path='/domains/%s/users/%s/roles/%s' % (
self.domain['id'], self.user['id'], self.role['id']))
r = self.get('/auth/domains')
r = self.get('/auth/domains', expected_status=http_client.OK)
self.assertThat(r.json['domains'], matchers.HasLength(1))
self.assertValidDomainListResponse(r)
def test_head_domains_with_project_scoped_token(self):
self.put(path='/domains/%s/users/%s/roles/%s' % (
self.domain['id'], self.user['id'], self.role['id']))
self.head('/auth/domains', expected_status=http_client.OK)
class TestTrustAuthFernetTokenProvider(TrustAPIBehavior, TestTrustChain):
def config_overrides(self):
@ -5299,7 +5346,7 @@ class TestFetchRevocationList(object):
super(TestFetchRevocationList, self).config_overrides()
self.config_fixture.config(group='token', revoke_by_id=True)
def test_ids_no_tokens(self):
def test_get_ids_no_tokens(self):
# When there's no revoked tokens the response is an empty list, and
# the response is signed.
res = self.get('/auth/tokens/OS-PKI/revoked')
@ -5309,6 +5356,12 @@ class TestFetchRevocationList(object):
payload = json.loads(clear)
self.assertEqual({'revoked': []}, payload)
def test_head_ids_no_tokens(self):
self.head(
'/auth/tokens/OS-PKI/revoked',
expected_status=http_client.OK
)
def test_ids_token(self):
# When there's a revoked token, it's in the response, and the response
# is signed.