Validate v2 fernet token returns extra attributes
Currently uuid and fernet tokens return different response on token validation. Change-Id: I21ad4c007b0bb1190227a1f4db3e1071a078982c Closes-Bug: #1533794
This commit is contained in:
parent
6b22bd4f2d
commit
33b50a285a
|
@ -0,0 +1,156 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
from keystone.common import validation
|
||||
from keystone.common.validation import parameter_types
|
||||
|
||||
|
||||
_project_v2_properties = {
|
||||
'id': parameter_types.id_string,
|
||||
'name': parameter_types.name,
|
||||
'enabled': parameter_types.boolean,
|
||||
'description': validation.nullable(parameter_types.description),
|
||||
}
|
||||
|
||||
_token_v2_properties = {
|
||||
'audit_ids': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
},
|
||||
'minItems': 1,
|
||||
'maxItems': 2,
|
||||
},
|
||||
'id': {'type': 'string'},
|
||||
'expires': {'type': 'string'},
|
||||
'issued_at': {'type': 'string'},
|
||||
'tenant': {
|
||||
'type': 'object',
|
||||
'properties': _project_v2_properties,
|
||||
'required': ['id', 'name', 'enabled'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_role_v2_properties = {
|
||||
'name': parameter_types.name,
|
||||
}
|
||||
|
||||
_user_v2_properties = {
|
||||
'id': parameter_types.id_string,
|
||||
'name': parameter_types.name,
|
||||
'username': parameter_types.name,
|
||||
'roles': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': _role_v2_properties,
|
||||
'required': ['name'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'roles_links': {
|
||||
'type': 'array',
|
||||
'maxItems': 0,
|
||||
},
|
||||
}
|
||||
|
||||
_metadata_v2_properties = {
|
||||
'is_admin': {'type': 'integer'},
|
||||
'roles': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string'},
|
||||
},
|
||||
}
|
||||
|
||||
_endpoint_v2_properties = {
|
||||
'id': {'type': 'string'},
|
||||
'adminURL': parameter_types.url,
|
||||
'internalURL': parameter_types.url,
|
||||
'publicURL': parameter_types.url,
|
||||
'region': {'type': 'string'},
|
||||
}
|
||||
|
||||
_service_v2_properties = {
|
||||
'type': {'type': 'string'},
|
||||
'name': parameter_types.name,
|
||||
'endpoints_links': {
|
||||
'type': 'array',
|
||||
'maxItems': 0,
|
||||
},
|
||||
'endpoints': {
|
||||
'type': 'array',
|
||||
'minItems': 1,
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': _endpoint_v2_properties,
|
||||
'required': ['id', 'publicURL'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_base_access_v2_properties = {
|
||||
'metadata': {
|
||||
'type': 'object',
|
||||
'properties': _metadata_v2_properties,
|
||||
'required': ['is_admin', 'roles'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
'serviceCatalog': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': _service_v2_properties,
|
||||
'required': ['name', 'type', 'endpoints_links', 'endpoints'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'token': {
|
||||
'type': 'object',
|
||||
'properties': _token_v2_properties,
|
||||
'required': ['audit_ids', 'id', 'expires', 'issued_at'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
'user': {
|
||||
'type': 'object',
|
||||
'properties': _user_v2_properties,
|
||||
'required': ['id', 'name', 'username', 'roles', 'roles_links'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_unscoped_access_v2_properties = copy.deepcopy(_base_access_v2_properties)
|
||||
unscoped_metadata = _unscoped_access_v2_properties['metadata']
|
||||
unscoped_metadata['properties']['roles']['maxItems'] = 0
|
||||
_unscoped_access_v2_properties['user']['properties']['roles']['maxItems'] = 0
|
||||
_unscoped_access_v2_properties['serviceCatalog']['maxItems'] = 0
|
||||
|
||||
_scoped_access_v2_properties = copy.deepcopy(_base_access_v2_properties)
|
||||
_scoped_access_v2_properties['metadata']['properties']['roles']['minItems'] = 1
|
||||
_scoped_access_v2_properties['serviceCatalog']['minItems'] = 1
|
||||
_scoped_access_v2_properties['user']['properties']['roles']['minItems'] = 1
|
||||
|
||||
base_token_v2_schema = {
|
||||
'type': 'object',
|
||||
'required': ['metadata', 'user', 'serviceCatalog', 'token'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
unscoped_token_v2_schema = copy.deepcopy(base_token_v2_schema)
|
||||
unscoped_token_v2_schema['properties'] = _unscoped_access_v2_properties
|
||||
|
||||
scoped_token_v2_schema = copy.deepcopy(base_token_v2_schema)
|
||||
scoped_token_v2_schema['properties'] = _scoped_access_v2_properties
|
|
@ -23,9 +23,11 @@ from six.moves import http_client
|
|||
from testtools import matchers
|
||||
|
||||
from keystone.common import extension as keystone_extension
|
||||
from keystone.common.validation import validators
|
||||
from keystone.tests import unit
|
||||
from keystone.tests.unit import ksfixtures
|
||||
from keystone.tests.unit import rest
|
||||
from keystone.tests.unit import schema
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -1420,6 +1422,34 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
super(TestFernetTokenProviderV2, self).setUp()
|
||||
self.useFixture(ksfixtures.KeyRepository(self.config_fixture))
|
||||
|
||||
# Add catalog data
|
||||
self.region = unit.new_region_ref()
|
||||
self.region_id = self.region['id']
|
||||
self.catalog_api.create_region(self.region)
|
||||
|
||||
self.service = unit.new_service_ref()
|
||||
self.service_id = self.service['id']
|
||||
self.catalog_api.create_service(self.service_id, self.service.copy())
|
||||
|
||||
self.endpoint = unit.new_endpoint_ref(service_id=self.service_id,
|
||||
interface='public',
|
||||
region_id=self.region_id)
|
||||
self.endpoint_id = self.endpoint['id']
|
||||
self.catalog_api.create_endpoint(self.endpoint_id,
|
||||
self.endpoint.copy())
|
||||
|
||||
def assertValidUnscopedTokenResponse(self, r):
|
||||
token = r.json['access']
|
||||
validator_object = validators.SchemaValidator(
|
||||
schema.unscoped_token_v2_schema)
|
||||
validator_object.validate(token)
|
||||
|
||||
def assertValidScopedTokenResponse(self, r):
|
||||
token = r.json['access']
|
||||
validator_object = validators.SchemaValidator(
|
||||
schema.scoped_token_v2_schema)
|
||||
validator_object.validate(token)
|
||||
|
||||
# Used by RestfulTestCase
|
||||
def _get_token_id(self, r):
|
||||
return r.result['access']['token']['id']
|
||||
|
@ -1450,11 +1480,12 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
admin_token = self.get_scoped_token(tenant_id=project_ref['id'])
|
||||
unscoped_token = self.get_unscoped_token()
|
||||
path = ('/v2.0/tokens/%s' % unscoped_token)
|
||||
self.admin_request(
|
||||
resp = self.admin_request(
|
||||
method='GET',
|
||||
path=path,
|
||||
token=admin_token,
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidUnscopedTokenResponse(resp)
|
||||
|
||||
def test_authenticate_scoped_token(self):
|
||||
project_ref = self.new_project_ref()
|
||||
|
@ -1480,11 +1511,12 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
path = ('/v2.0/tokens/%s?belongsTo=%s' % (member_token,
|
||||
project2_ref['id']))
|
||||
# Validate token belongs to project
|
||||
self.admin_request(
|
||||
resp = self.admin_request(
|
||||
method='GET',
|
||||
path=path,
|
||||
token=admin_token,
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidScopedTokenResponse(resp)
|
||||
|
||||
def test_token_authentication_and_validation(self):
|
||||
"""Test token authentication for Fernet token provider.
|
||||
|
@ -1514,11 +1546,12 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
token_id = self._get_token_id(r)
|
||||
path = ('/v2.0/tokens/%s?belongsTo=%s' % (token_id, project_ref['id']))
|
||||
# Validate token belongs to project
|
||||
self.admin_request(
|
||||
resp = self.admin_request(
|
||||
method='GET',
|
||||
path=path,
|
||||
token=self.get_admin_token(),
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidScopedTokenResponse(resp)
|
||||
|
||||
def test_rescoped_tokens_maintain_original_expiration(self):
|
||||
project_ref = self.new_project_ref()
|
||||
|
@ -1560,3 +1593,4 @@ class TestFernetTokenProviderV2(RestfulTestCase):
|
|||
rescoped_expiration = resp.result['access']['token']['expires']
|
||||
self.assertNotEqual(original_token, rescoped_token)
|
||||
self.assertEqual(original_expiration, rescoped_expiration)
|
||||
self.assertValidScopedTokenResponse(resp)
|
||||
|
|
|
@ -943,8 +943,8 @@ class TestPKITokenAPIs(test_v3.RestfulTestCase, TokenAPITests, TokenDataTests):
|
|||
# just need to make sure the non fraction part agrees
|
||||
self.assertIn(v2_token['access']['token']['expires'][:-1],
|
||||
token_data['token']['expires_at'])
|
||||
self.assertEqual(v2_token['access']['user']['roles'][0]['id'],
|
||||
token_data['token']['roles'][0]['id'])
|
||||
self.assertEqual(v2_token['access']['user']['roles'][0]['name'],
|
||||
token_data['token']['roles'][0]['name'])
|
||||
|
||||
|
||||
class TestPKIZTokenAPIs(TestPKITokenAPIs):
|
||||
|
|
|
@ -70,7 +70,8 @@ class V2TokenDataHelper(object):
|
|||
# v3 token_data does not contain all tenant attributes
|
||||
tenant = self.resource_api.get_project(
|
||||
v3_token['project']['id'])
|
||||
token['tenant'] = common_controller.V2Controller.filter_domain_id(
|
||||
# Drop domain specific fields since v2 calls are not domain-aware.
|
||||
token['tenant'] = common_controller.V2Controller.v3_to_v2_project(
|
||||
tenant)
|
||||
token_data['token'] = token
|
||||
|
||||
|
@ -93,6 +94,7 @@ class V2TokenDataHelper(object):
|
|||
user['roles'] = []
|
||||
role_ids = []
|
||||
for role in v3_token.get('roles', []):
|
||||
role_ids.append(role.pop('id'))
|
||||
user['roles'].append(role)
|
||||
user['roles_links'] = []
|
||||
|
||||
|
|
Loading…
Reference in New Issue