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:
Jorge Munoz 2016-03-07 23:06:18 +00:00
parent 6b22bd4f2d
commit 33b50a285a
4 changed files with 198 additions and 6 deletions

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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'] = []