d7a2dd48e4
This reverts commit 8d31705806
.
In the Train PTG[1] we agreed to defer this feature until we had some
kind of traceability or discoverability for APIs and that this wasn't
feasible or useful until then.
This change was merged to master but never released, so I submit that
it is safe to revert.
[1] https://etherpad.openstack.org/p/keystone-train-ptg-application-credentials
Change-Id: Ibb48983f7edef1c2df50ba998f6a95741f933168
927 lines
39 KiB
Python
927 lines
39 KiB
Python
# Copyright 2012 OpenStack Foundation
|
|
#
|
|
# 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
|
|
import functools
|
|
import random
|
|
|
|
from oslo_serialization import jsonutils
|
|
from six.moves import http_client
|
|
from testtools import matchers as tt_matchers
|
|
import webob
|
|
|
|
from keystone.api import discovery
|
|
from keystone.common import json_home
|
|
from keystone.tests import unit
|
|
|
|
|
|
v3_MEDIA_TYPES = [
|
|
{
|
|
"base": "application/json",
|
|
"type": "application/"
|
|
"vnd.openstack.identity-v3+json"
|
|
}
|
|
]
|
|
|
|
v3_EXPECTED_RESPONSE = {
|
|
"id": "v3.12",
|
|
"status": "stable",
|
|
"updated": "2019-01-22T00:00:00Z",
|
|
"links": [
|
|
{
|
|
"rel": "self",
|
|
"href": "", # Will get filled in after initialization
|
|
}
|
|
],
|
|
"media-types": v3_MEDIA_TYPES
|
|
}
|
|
|
|
v3_VERSION_RESPONSE = {
|
|
"version": v3_EXPECTED_RESPONSE
|
|
}
|
|
|
|
VERSIONS_RESPONSE = {
|
|
"versions": {
|
|
"values": [
|
|
v3_EXPECTED_RESPONSE,
|
|
]
|
|
}
|
|
}
|
|
|
|
_build_ec2tokens_relation = functools.partial(
|
|
json_home.build_v3_extension_resource_relation, extension_name='OS-EC2',
|
|
extension_version='1.0')
|
|
|
|
REVOCATIONS_RELATION = json_home.build_v3_extension_resource_relation(
|
|
'OS-PKI', '1.0', 'revocations')
|
|
|
|
_build_simple_cert_relation = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-SIMPLE-CERT', extension_version='1.0')
|
|
|
|
_build_trust_relation = functools.partial(
|
|
json_home.build_v3_extension_resource_relation, extension_name='OS-TRUST',
|
|
extension_version='1.0')
|
|
|
|
_build_federation_rel = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-FEDERATION',
|
|
extension_version='1.0')
|
|
|
|
_build_oauth1_rel = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-OAUTH1', extension_version='1.0')
|
|
|
|
_build_ep_policy_rel = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-ENDPOINT-POLICY', extension_version='1.0')
|
|
|
|
_build_ep_filter_rel = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-EP-FILTER', extension_version='1.0')
|
|
|
|
_build_os_inherit_rel = functools.partial(
|
|
json_home.build_v3_extension_resource_relation,
|
|
extension_name='OS-INHERIT', extension_version='1.0')
|
|
|
|
TRUST_ID_PARAMETER_RELATION = json_home.build_v3_extension_parameter_relation(
|
|
'OS-TRUST', '1.0', 'trust_id')
|
|
|
|
IDP_ID_PARAMETER_RELATION = json_home.build_v3_extension_parameter_relation(
|
|
'OS-FEDERATION', '1.0', 'idp_id')
|
|
|
|
PROTOCOL_ID_PARAM_RELATION = json_home.build_v3_extension_parameter_relation(
|
|
'OS-FEDERATION', '1.0', 'protocol_id')
|
|
|
|
MAPPING_ID_PARAM_RELATION = json_home.build_v3_extension_parameter_relation(
|
|
'OS-FEDERATION', '1.0', 'mapping_id')
|
|
|
|
SP_ID_PARAMETER_RELATION = json_home.build_v3_extension_parameter_relation(
|
|
'OS-FEDERATION', '1.0', 'sp_id')
|
|
|
|
CONSUMER_ID_PARAMETER_RELATION = (
|
|
json_home.build_v3_extension_parameter_relation(
|
|
'OS-OAUTH1', '1.0', 'consumer_id'))
|
|
|
|
REQUEST_TOKEN_ID_PARAMETER_RELATION = (
|
|
json_home.build_v3_extension_parameter_relation(
|
|
'OS-OAUTH1', '1.0', 'request_token_id'))
|
|
|
|
ACCESS_TOKEN_ID_PARAMETER_RELATION = (
|
|
json_home.build_v3_extension_parameter_relation(
|
|
'OS-OAUTH1', '1.0', 'access_token_id'))
|
|
|
|
ENDPOINT_GROUP_ID_PARAMETER_RELATION = (
|
|
json_home.build_v3_extension_parameter_relation(
|
|
'OS-EP-FILTER', '1.0', 'endpoint_group_id'))
|
|
|
|
BASE_IDP_PROTOCOL = '/OS-FEDERATION/identity_providers/{idp_id}/protocols'
|
|
BASE_EP_POLICY = '/policies/{policy_id}/OS-ENDPOINT-POLICY'
|
|
BASE_EP_FILTER_PREFIX = '/OS-EP-FILTER'
|
|
BASE_EP_FILTER = BASE_EP_FILTER_PREFIX + '/endpoint_groups/{endpoint_group_id}'
|
|
BASE_ACCESS_TOKEN = (
|
|
'/users/{user_id}/OS-OAUTH1/access_tokens/{access_token_id}')
|
|
|
|
FEDERATED_AUTH_URL = ('/OS-FEDERATION/identity_providers/{idp_id}'
|
|
'/protocols/{protocol_id}/auth')
|
|
FEDERATED_IDP_SPECIFIC_WEBSSO = ('/auth/OS-FEDERATION/identity_providers/'
|
|
'{idp_id}/protocols/{protocol_id}/websso')
|
|
|
|
APPLICATION_CREDENTIAL = ('/users/{user_id}/application_credentials/'
|
|
'{application_credential_id}')
|
|
APPLICATION_CREDENTIALS = '/users/{user_id}/application_credentials'
|
|
APPLICATION_CREDENTIAL_RELATION = (
|
|
json_home.build_v3_parameter_relation('application_credential_id'))
|
|
|
|
V3_JSON_HOME_RESOURCES = {
|
|
json_home.build_v3_resource_relation('auth_tokens'): {
|
|
'href': '/auth/tokens'},
|
|
json_home.build_v3_resource_relation('auth_catalog'): {
|
|
'href': '/auth/catalog'},
|
|
json_home.build_v3_resource_relation('auth_projects'): {
|
|
'href': '/auth/projects'},
|
|
json_home.build_v3_resource_relation('auth_domains'): {
|
|
'href': '/auth/domains'},
|
|
json_home.build_v3_resource_relation('auth_system'): {
|
|
'href': '/auth/system'},
|
|
json_home.build_v3_resource_relation('credential'): {
|
|
'href-template': '/credentials/{credential_id}',
|
|
'href-vars': {
|
|
'credential_id':
|
|
json_home.build_v3_parameter_relation('credential_id')}},
|
|
json_home.build_v3_resource_relation('credentials'): {
|
|
'href': '/credentials'},
|
|
json_home.build_v3_resource_relation('system_user_role'): {
|
|
'href-template': '/system/users/{user_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'user_id': json_home.Parameters.USER_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID
|
|
}
|
|
},
|
|
json_home.build_v3_resource_relation('system_user_roles'): {
|
|
'href-template': '/system/users/{user_id}/roles',
|
|
'href-vars': {
|
|
'user_id': json_home.Parameters.USER_ID
|
|
}
|
|
},
|
|
json_home.build_v3_resource_relation('system_group_role'): {
|
|
'href-template': '/system/groups/{group_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID
|
|
}
|
|
},
|
|
json_home.build_v3_resource_relation('system_group_roles'): {
|
|
'href-template': '/system/groups/{group_id}/roles',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID
|
|
}
|
|
},
|
|
json_home.build_v3_resource_relation('domain'): {
|
|
'href-template': '/domains/{domain_id}',
|
|
'href-vars': {'domain_id': json_home.Parameters.DOMAIN_ID, }},
|
|
json_home.build_v3_resource_relation('domain_group_role'): {
|
|
'href-template':
|
|
'/domains/{domain_id}/groups/{group_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID, }},
|
|
json_home.build_v3_resource_relation('domain_group_roles'): {
|
|
'href-template': '/domains/{domain_id}/groups/{group_id}/roles',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group_id': json_home.Parameters.GROUP_ID}},
|
|
json_home.build_v3_resource_relation('domain_user_role'): {
|
|
'href-template':
|
|
'/domains/{domain_id}/users/{user_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('domain_user_roles'): {
|
|
'href-template': '/domains/{domain_id}/users/{user_id}/roles',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('domains'): {'href': '/domains'},
|
|
json_home.build_v3_resource_relation('endpoint'): {
|
|
'href-template': '/endpoints/{endpoint_id}',
|
|
'href-vars': {
|
|
'endpoint_id':
|
|
json_home.build_v3_parameter_relation('endpoint_id'), }},
|
|
json_home.build_v3_resource_relation('endpoints'): {
|
|
'href': '/endpoints'},
|
|
_build_ec2tokens_relation(resource_name='ec2tokens'): {
|
|
'href': '/ec2tokens'},
|
|
_build_ec2tokens_relation(resource_name='user_credential'): {
|
|
'href-template': '/users/{user_id}/credentials/OS-EC2/{credential_id}',
|
|
'href-vars': {
|
|
'credential_id':
|
|
json_home.build_v3_parameter_relation('credential_id'),
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
_build_ec2tokens_relation(resource_name='user_credentials'): {
|
|
'href-template': '/users/{user_id}/credentials/OS-EC2',
|
|
'href-vars': {
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
REVOCATIONS_RELATION: {
|
|
'href': '/auth/tokens/OS-PKI/revoked'},
|
|
'https://docs.openstack.org/api/openstack-identity/3/ext/OS-REVOKE/1.0/rel'
|
|
'/events': {
|
|
'href': '/OS-REVOKE/events'},
|
|
_build_simple_cert_relation(resource_name='ca_certificate'): {
|
|
'href': '/OS-SIMPLE-CERT/ca'},
|
|
_build_simple_cert_relation(resource_name='certificates'): {
|
|
'href': '/OS-SIMPLE-CERT/certificates'},
|
|
_build_trust_relation(resource_name='trust'):
|
|
{
|
|
'href-template': '/OS-TRUST/trusts/{trust_id}',
|
|
'href-vars': {'trust_id': TRUST_ID_PARAMETER_RELATION, }},
|
|
_build_trust_relation(resource_name='trust_role'): {
|
|
'href-template': '/OS-TRUST/trusts/{trust_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'trust_id': TRUST_ID_PARAMETER_RELATION, }},
|
|
_build_trust_relation(resource_name='trust_roles'): {
|
|
'href-template': '/OS-TRUST/trusts/{trust_id}/roles',
|
|
'href-vars': {'trust_id': TRUST_ID_PARAMETER_RELATION, }},
|
|
_build_trust_relation(resource_name='trusts'): {
|
|
'href': '/OS-TRUST/trusts'},
|
|
'https://docs.openstack.org/api/openstack-identity/3/ext/s3tokens/1.0/rel/'
|
|
's3tokens': {
|
|
'href': '/s3tokens'},
|
|
json_home.build_v3_resource_relation('group'): {
|
|
'href-template': '/groups/{group_id}',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID, }},
|
|
json_home.build_v3_resource_relation('group_user'): {
|
|
'href-template': '/groups/{group_id}/users/{user_id}',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('group_users'): {
|
|
'href-template': '/groups/{group_id}/users',
|
|
'href-vars': {'group_id': json_home.Parameters.GROUP_ID, }},
|
|
json_home.build_v3_resource_relation('groups'): {'href': '/groups'},
|
|
json_home.build_v3_resource_relation('policies'): {
|
|
'href': '/policies'},
|
|
json_home.build_v3_resource_relation('policy'): {
|
|
'href-template': '/policies/{policy_id}',
|
|
'href-vars': {
|
|
'policy_id':
|
|
json_home.build_v3_parameter_relation('policy_id'), }},
|
|
json_home.build_v3_resource_relation('project'): {
|
|
'href-template': '/projects/{project_id}',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID, }},
|
|
json_home.build_v3_resource_relation('project_group_role'): {
|
|
'href-template':
|
|
'/projects/{project_id}/groups/{group_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID, }},
|
|
json_home.build_v3_resource_relation('project_group_roles'): {
|
|
'href-template': '/projects/{project_id}/groups/{group_id}/roles',
|
|
'href-vars': {
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'project_id': json_home.Parameters.PROJECT_ID, }},
|
|
json_home.build_v3_resource_relation('project_tags'): {
|
|
'href-template': '/projects/{project_id}/tags/{value}',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'value': json_home.Parameters.TAG_VALUE}},
|
|
json_home.build_v3_resource_relation('project_user_role'): {
|
|
'href-template':
|
|
'/projects/{project_id}/users/{user_id}/roles/{role_id}',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('project_user_roles'): {
|
|
'href-template': '/projects/{project_id}/users/{user_id}/roles',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('projects'): {
|
|
'href': '/projects'},
|
|
json_home.build_v3_resource_relation('region'): {
|
|
'href-template': '/regions/{region_id}',
|
|
'href-vars': {
|
|
'region_id':
|
|
json_home.build_v3_parameter_relation('region_id'), }},
|
|
json_home.build_v3_resource_relation('regions'): {'href': '/regions'},
|
|
json_home.build_v3_resource_relation('role'): {
|
|
'href-template': '/roles/{role_id}',
|
|
'href-vars': {
|
|
'role_id': json_home.Parameters.ROLE_ID, }},
|
|
json_home.build_v3_resource_relation('implied_roles'): {
|
|
'href-template': '/roles/{prior_role_id}/implies',
|
|
'href-vars': {
|
|
'prior_role_id': json_home.Parameters.ROLE_ID}
|
|
},
|
|
json_home.build_v3_resource_relation('implied_role'): {
|
|
'href-template':
|
|
'/roles/{prior_role_id}/implies/{implied_role_id}',
|
|
'href-vars': {
|
|
'prior_role_id': json_home.Parameters.ROLE_ID,
|
|
'implied_role_id': json_home.Parameters.ROLE_ID,
|
|
},
|
|
},
|
|
json_home.build_v3_resource_relation('role_inferences'): {
|
|
'href': '/role_inferences',
|
|
},
|
|
json_home.build_v3_resource_relation('role_assignments'): {
|
|
'href': '/role_assignments'},
|
|
json_home.build_v3_resource_relation('roles'): {'href': '/roles'},
|
|
json_home.build_v3_resource_relation('service'): {
|
|
'href-template': '/services/{service_id}',
|
|
'href-vars': {
|
|
'service_id':
|
|
json_home.build_v3_parameter_relation('service_id')}},
|
|
json_home.build_v3_resource_relation('services'): {
|
|
'href': '/services'},
|
|
json_home.build_v3_resource_relation('user'): {
|
|
'href-template': '/users/{user_id}',
|
|
'href-vars': {
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('user_change_password'): {
|
|
'href-template': '/users/{user_id}/password',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('user_groups'): {
|
|
'href-template': '/users/{user_id}/groups',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('user_projects'): {
|
|
'href-template': '/users/{user_id}/projects',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID, }},
|
|
json_home.build_v3_resource_relation('users'): {'href': '/users'},
|
|
_build_federation_rel(resource_name='domains'): {
|
|
'href': '/auth/domains'},
|
|
_build_federation_rel(resource_name='websso'): {
|
|
'href-template': '/auth/OS-FEDERATION/websso/{protocol_id}',
|
|
'href-vars': {
|
|
'protocol_id': PROTOCOL_ID_PARAM_RELATION, }},
|
|
_build_federation_rel(resource_name='projects'): {
|
|
'href': '/auth/projects'},
|
|
_build_federation_rel(resource_name='saml2'): {
|
|
'href': '/auth/OS-FEDERATION/saml2'},
|
|
_build_federation_rel(resource_name='ecp'): {
|
|
'href': '/auth/OS-FEDERATION/saml2/ecp'},
|
|
_build_federation_rel(resource_name='metadata'): {
|
|
'href': '/OS-FEDERATION/saml2/metadata'},
|
|
_build_federation_rel(resource_name='identity_providers'): {
|
|
'href': '/OS-FEDERATION/identity_providers'},
|
|
_build_federation_rel(resource_name='service_providers'): {
|
|
'href': '/OS-FEDERATION/service_providers'},
|
|
_build_federation_rel(resource_name='mappings'): {
|
|
'href': '/OS-FEDERATION/mappings'},
|
|
_build_federation_rel(resource_name='identity_provider'):
|
|
{
|
|
'href-template': '/OS-FEDERATION/identity_providers/{idp_id}',
|
|
'href-vars': {'idp_id': IDP_ID_PARAMETER_RELATION, }},
|
|
_build_federation_rel(resource_name='identity_providers_websso'): {
|
|
'href-template': FEDERATED_IDP_SPECIFIC_WEBSSO,
|
|
'href-vars': {
|
|
'idp_id': IDP_ID_PARAMETER_RELATION,
|
|
'protocol_id': PROTOCOL_ID_PARAM_RELATION, }},
|
|
_build_federation_rel(resource_name='service_provider'):
|
|
{
|
|
'href-template': '/OS-FEDERATION/service_providers/{sp_id}',
|
|
'href-vars': {'sp_id': SP_ID_PARAMETER_RELATION, }},
|
|
_build_federation_rel(resource_name='mapping'):
|
|
{
|
|
'href-template': '/OS-FEDERATION/mappings/{mapping_id}',
|
|
'href-vars': {'mapping_id': MAPPING_ID_PARAM_RELATION, }},
|
|
_build_federation_rel(resource_name='identity_provider_protocol'): {
|
|
'href-template': BASE_IDP_PROTOCOL + '/{protocol_id}',
|
|
'href-vars': {
|
|
'idp_id': IDP_ID_PARAMETER_RELATION,
|
|
'protocol_id': PROTOCOL_ID_PARAM_RELATION, }},
|
|
_build_federation_rel(resource_name='identity_provider_protocols'): {
|
|
'href-template': BASE_IDP_PROTOCOL,
|
|
'href-vars': {
|
|
'idp_id': IDP_ID_PARAMETER_RELATION}},
|
|
_build_federation_rel(resource_name='identity_provider_protocol_auth'): {
|
|
'href-template': FEDERATED_AUTH_URL,
|
|
'href-vars': {
|
|
'idp_id': IDP_ID_PARAMETER_RELATION,
|
|
'protocol_id': PROTOCOL_ID_PARAM_RELATION, }},
|
|
_build_oauth1_rel(resource_name='access_tokens'): {
|
|
'href': '/OS-OAUTH1/access_token'},
|
|
_build_oauth1_rel(resource_name='request_tokens'): {
|
|
'href': '/OS-OAUTH1/request_token'},
|
|
_build_oauth1_rel(resource_name='consumers'): {
|
|
'href': '/OS-OAUTH1/consumers'},
|
|
_build_oauth1_rel(resource_name='authorize_request_token'):
|
|
{
|
|
'href-template': '/OS-OAUTH1/authorize/{request_token_id}',
|
|
'href-vars': {'request_token_id':
|
|
REQUEST_TOKEN_ID_PARAMETER_RELATION, }},
|
|
_build_oauth1_rel(resource_name='consumer'):
|
|
{
|
|
'href-template': '/OS-OAUTH1/consumers/{consumer_id}',
|
|
'href-vars': {'consumer_id': CONSUMER_ID_PARAMETER_RELATION, }},
|
|
_build_oauth1_rel(resource_name='user_access_token'):
|
|
{
|
|
'href-template': BASE_ACCESS_TOKEN,
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID,
|
|
'access_token_id':
|
|
ACCESS_TOKEN_ID_PARAMETER_RELATION, }},
|
|
_build_oauth1_rel(resource_name='user_access_tokens'):
|
|
{
|
|
'href-template': '/users/{user_id}/OS-OAUTH1/access_tokens',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID, }},
|
|
_build_oauth1_rel(resource_name='user_access_token_role'):
|
|
{
|
|
'href-template': BASE_ACCESS_TOKEN + '/roles/{role_id}',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'access_token_id':
|
|
ACCESS_TOKEN_ID_PARAMETER_RELATION, }},
|
|
_build_oauth1_rel(resource_name='user_access_token_roles'):
|
|
{
|
|
'href-template': BASE_ACCESS_TOKEN + '/roles',
|
|
'href-vars': {'user_id': json_home.Parameters.USER_ID,
|
|
'access_token_id':
|
|
ACCESS_TOKEN_ID_PARAMETER_RELATION, }},
|
|
_build_ep_policy_rel(resource_name='endpoint_policy'):
|
|
{
|
|
'href-template': '/endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/policy',
|
|
'href-vars': {'endpoint_id': json_home.Parameters.ENDPOINT_ID, }},
|
|
_build_ep_policy_rel(resource_name='endpoint_policy_association'):
|
|
{
|
|
'href-template': BASE_EP_POLICY + '/endpoints/{endpoint_id}',
|
|
'href-vars': {'endpoint_id': json_home.Parameters.ENDPOINT_ID,
|
|
'policy_id': json_home.Parameters.POLICY_ID, }},
|
|
_build_ep_policy_rel(resource_name='policy_endpoints'):
|
|
{
|
|
'href-template': BASE_EP_POLICY + '/endpoints',
|
|
'href-vars': {'policy_id': json_home.Parameters.POLICY_ID, }},
|
|
_build_ep_policy_rel(
|
|
resource_name='region_and_service_policy_association'):
|
|
{
|
|
'href-template': (BASE_EP_POLICY +
|
|
'/services/{service_id}/regions/{region_id}'),
|
|
'href-vars': {'policy_id': json_home.Parameters.POLICY_ID,
|
|
'service_id': json_home.Parameters.SERVICE_ID,
|
|
'region_id': json_home.Parameters.REGION_ID, }},
|
|
_build_ep_policy_rel(resource_name='service_policy_association'):
|
|
{
|
|
'href-template': BASE_EP_POLICY + '/services/{service_id}',
|
|
'href-vars': {'policy_id': json_home.Parameters.POLICY_ID,
|
|
'service_id': json_home.Parameters.SERVICE_ID, }},
|
|
_build_ep_filter_rel(resource_name='endpoint_group'):
|
|
{
|
|
'href-template': '/OS-EP-FILTER/endpoint_groups/{endpoint_group_id}',
|
|
'href-vars': {'endpoint_group_id':
|
|
ENDPOINT_GROUP_ID_PARAMETER_RELATION, }},
|
|
_build_ep_filter_rel(
|
|
resource_name='endpoint_group_to_project_association'):
|
|
{
|
|
'href-template': BASE_EP_FILTER + '/projects/{project_id}',
|
|
'href-vars': {'endpoint_group_id':
|
|
ENDPOINT_GROUP_ID_PARAMETER_RELATION,
|
|
'project_id': json_home.Parameters.PROJECT_ID, }},
|
|
_build_ep_filter_rel(resource_name='endpoint_groups'):
|
|
{'href': '/OS-EP-FILTER/endpoint_groups'},
|
|
_build_ep_filter_rel(resource_name='endpoint_projects'):
|
|
{
|
|
'href-template': '/OS-EP-FILTER/endpoints/{endpoint_id}/projects',
|
|
'href-vars': {'endpoint_id': json_home.Parameters.ENDPOINT_ID, }},
|
|
_build_ep_filter_rel(resource_name='endpoints_in_endpoint_group'):
|
|
{
|
|
'href-template': BASE_EP_FILTER + '/endpoints',
|
|
'href-vars': {'endpoint_group_id':
|
|
ENDPOINT_GROUP_ID_PARAMETER_RELATION, }},
|
|
_build_ep_filter_rel(resource_name='project_endpoint_groups'):
|
|
{
|
|
'href-template': (BASE_EP_FILTER_PREFIX + '/projects/{project_id}' +
|
|
'/endpoint_groups'),
|
|
'href-vars': {'project_id':
|
|
json_home.Parameters.PROJECT_ID, }},
|
|
_build_ep_filter_rel(resource_name='project_endpoint'):
|
|
{
|
|
'href-template': ('/OS-EP-FILTER/projects/{project_id}'
|
|
'/endpoints/{endpoint_id}'),
|
|
'href-vars': {'endpoint_id': json_home.Parameters.ENDPOINT_ID,
|
|
'project_id': json_home.Parameters.PROJECT_ID, }},
|
|
_build_ep_filter_rel(resource_name='project_endpoints'):
|
|
{
|
|
'href-template': '/OS-EP-FILTER/projects/{project_id}/endpoints',
|
|
'href-vars': {'project_id': json_home.Parameters.PROJECT_ID, }},
|
|
_build_ep_filter_rel(
|
|
resource_name='projects_associated_with_endpoint_group'):
|
|
{
|
|
'href-template': BASE_EP_FILTER + '/projects',
|
|
'href-vars': {'endpoint_group_id':
|
|
ENDPOINT_GROUP_ID_PARAMETER_RELATION, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='domain_user_role_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/domains/{domain_id}/users/'
|
|
'{user_id}/roles/{role_id}/inherited_to_projects',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='domain_group_role_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/domains/{domain_id}/groups/'
|
|
'{group_id}/roles/{role_id}/inherited_to_projects',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='domain_user_roles_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/domains/{domain_id}/users/'
|
|
'{user_id}/roles/inherited_to_projects',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='domain_group_roles_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/domains/{domain_id}/groups/'
|
|
'{group_id}/roles/inherited_to_projects',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group_id': json_home.Parameters.GROUP_ID, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='project_user_role_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/projects/{project_id}/users/'
|
|
'{user_id}/roles/{role_id}/inherited_to_projects',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID,
|
|
'user_id': json_home.Parameters.USER_ID, }},
|
|
_build_os_inherit_rel(
|
|
resource_name='project_group_role_inherited_to_projects'):
|
|
{
|
|
'href-template': '/OS-INHERIT/projects/{project_id}/groups/'
|
|
'{group_id}/roles/{role_id}/inherited_to_projects',
|
|
'href-vars': {
|
|
'project_id': json_home.Parameters.PROJECT_ID,
|
|
'group_id': json_home.Parameters.GROUP_ID,
|
|
'role_id': json_home.Parameters.ROLE_ID, }},
|
|
json_home.build_v3_resource_relation('domain_config'): {
|
|
'href-template':
|
|
'/domains/{domain_id}/config',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID}},
|
|
json_home.build_v3_resource_relation('domain_config_group'): {
|
|
'href-template':
|
|
'/domains/{domain_id}/config/{group}',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group': json_home.build_v3_parameter_relation('config_group')}},
|
|
json_home.build_v3_resource_relation('domain_config_option'): {
|
|
'href-template':
|
|
'/domains/{domain_id}/config/{group}/{option}',
|
|
'href-vars': {
|
|
'domain_id': json_home.Parameters.DOMAIN_ID,
|
|
'group': json_home.build_v3_parameter_relation('config_group'),
|
|
'option': json_home.build_v3_parameter_relation('config_option')}},
|
|
json_home.build_v3_resource_relation('domain_config_default'): {
|
|
'href': '/domains/config/default'},
|
|
json_home.build_v3_resource_relation('domain_config_default_group'): {
|
|
'href-template': '/domains/config/{group}/default',
|
|
'href-vars': {
|
|
'group': json_home.build_v3_parameter_relation('config_group')}},
|
|
json_home.build_v3_resource_relation('domain_config_default_option'): {
|
|
'href-template': '/domains/config/{group}/{option}/default',
|
|
'href-vars': {
|
|
'group': json_home.build_v3_parameter_relation('config_group'),
|
|
'option': json_home.build_v3_parameter_relation('config_option')}},
|
|
json_home.build_v3_resource_relation('registered_limits'): {
|
|
'hints': {'status': 'experimental'},
|
|
'href': '/registered_limits'},
|
|
json_home.build_v3_resource_relation('registered_limit'): {
|
|
'href-template': '/registered_limits/{registered_limit_id}',
|
|
'href-vars': {
|
|
'registered_limit_id': json_home.build_v3_parameter_relation(
|
|
'registered_limit_id')
|
|
},
|
|
'hints': {'status': 'experimental'}
|
|
},
|
|
json_home.build_v3_resource_relation('limits'): {
|
|
'hints': {'status': 'experimental'},
|
|
'href': '/limits'},
|
|
json_home.build_v3_resource_relation('limit'): {
|
|
'href-template': '/limits/{limit_id}',
|
|
'href-vars': {
|
|
'limit_id': json_home.build_v3_parameter_relation('limit_id')
|
|
},
|
|
'hints': {'status': 'experimental'}
|
|
},
|
|
json_home.build_v3_resource_relation('limit_model'): {
|
|
'href': '/limits/model',
|
|
'hints': {'status': 'experimental'}
|
|
},
|
|
json_home.build_v3_resource_relation('application_credentials'): {
|
|
'href-template': APPLICATION_CREDENTIALS,
|
|
'href-vars': {
|
|
'user_id': json_home.build_v3_parameter_relation('user_id')}},
|
|
json_home.build_v3_resource_relation('application_credential'): {
|
|
'href-template': APPLICATION_CREDENTIAL,
|
|
'href-vars': {
|
|
'application_credential_id': APPLICATION_CREDENTIAL_RELATION,
|
|
'user_id': json_home.build_v3_parameter_relation('user_id')}}
|
|
}
|
|
|
|
|
|
class TestClient(object):
|
|
def __init__(self, app=None, token=None):
|
|
self.app = app
|
|
self.token = token
|
|
|
|
def request(self, method, path, headers=None, body=None):
|
|
if headers is None:
|
|
headers = {}
|
|
|
|
if self.token:
|
|
headers.setdefault('X-Auth-Token', self.token)
|
|
|
|
req = webob.Request.blank(path)
|
|
req.method = method
|
|
for k, v in headers.items():
|
|
req.headers[k] = v
|
|
if body:
|
|
req.body = body
|
|
return req.get_response(self.app)
|
|
|
|
def get(self, path, headers=None):
|
|
return self.request('GET', path=path, headers=headers)
|
|
|
|
def post(self, path, headers=None, body=None):
|
|
return self.request('POST', path=path, headers=headers, body=body)
|
|
|
|
def put(self, path, headers=None, body=None):
|
|
return self.request('PUT', path=path, headers=headers, body=body)
|
|
|
|
|
|
class _VersionsEqual(tt_matchers.MatchesListwise):
|
|
def __init__(self, expected):
|
|
super(_VersionsEqual, self).__init__([
|
|
tt_matchers.KeysEqual(expected),
|
|
tt_matchers.KeysEqual(expected['versions']),
|
|
tt_matchers.HasLength(len(expected['versions']['values'])),
|
|
tt_matchers.ContainsAll(expected['versions']['values']),
|
|
])
|
|
|
|
def match(self, other):
|
|
return super(_VersionsEqual, self).match([
|
|
other,
|
|
other['versions'],
|
|
other['versions']['values'],
|
|
other['versions']['values'],
|
|
])
|
|
|
|
|
|
class VersionTestCase(unit.TestCase):
|
|
def setUp(self):
|
|
super(VersionTestCase, self).setUp()
|
|
self.load_backends()
|
|
self.public_app = self.loadapp('public')
|
|
self.public_port = random.randint(40000, 60000)
|
|
|
|
self.config_fixture.config(
|
|
public_endpoint='http://localhost:%d' % self.public_port)
|
|
|
|
def config_overrides(self):
|
|
super(VersionTestCase, self).config_overrides()
|
|
|
|
def _paste_in_port(self, response, port):
|
|
for link in response['links']:
|
|
if link['rel'] == 'self':
|
|
link['href'] = port
|
|
|
|
def test_public_versions(self):
|
|
client = TestClient(self.public_app)
|
|
resp = client.get('/')
|
|
self.assertEqual(300, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = VERSIONS_RESPONSE
|
|
for version in expected['versions']['values']:
|
|
if version['id'].startswith('v3'):
|
|
self._paste_in_port(
|
|
version, 'http://localhost:%s/v3/' % self.public_port)
|
|
self.assertThat(data, _VersionsEqual(expected))
|
|
|
|
def test_use_site_url_if_endpoint_unset(self):
|
|
self.config_fixture.config(public_endpoint=None)
|
|
|
|
for app in (self.public_app,):
|
|
client = TestClient(app)
|
|
resp = client.get('/')
|
|
self.assertEqual(300, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = VERSIONS_RESPONSE
|
|
for version in expected['versions']['values']:
|
|
# localhost happens to be the site url for tests
|
|
if version['id'].startswith('v3'):
|
|
self._paste_in_port(
|
|
version, 'http://localhost/v3/')
|
|
self.assertThat(data, _VersionsEqual(expected))
|
|
|
|
def test_public_version_v3(self):
|
|
client = TestClient(self.public_app)
|
|
resp = client.get('/v3/')
|
|
self.assertEqual(http_client.OK, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = v3_VERSION_RESPONSE
|
|
self._paste_in_port(expected['version'],
|
|
'http://localhost:%s/v3/' % self.public_port)
|
|
self.assertEqual(expected, data)
|
|
|
|
def test_use_site_url_if_endpoint_unset_v3(self):
|
|
self.config_fixture.config(public_endpoint=None)
|
|
for app in (self.public_app,):
|
|
client = TestClient(app)
|
|
resp = client.get('/v3/')
|
|
self.assertEqual(http_client.OK, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = v3_VERSION_RESPONSE
|
|
self._paste_in_port(expected['version'], 'http://localhost/v3/')
|
|
self.assertEqual(expected, data)
|
|
|
|
def test_v2_disabled(self):
|
|
# NOTE(morgan): This test should be kept, v2.0 is removed and should
|
|
# never return, this prevents regression[s]/v2.0 discovery doc
|
|
# slipping back in.
|
|
client = TestClient(self.public_app)
|
|
# request to /v2.0 should fail
|
|
resp = client.get('/v2.0/')
|
|
# NOTE(morgan): getting a 418 here is indicative of a 404, but from
|
|
# the flask app itself (not a handled 404 such as UserNotFound)
|
|
self.assertEqual(418, resp.status_int)
|
|
|
|
# request to /v3 should pass
|
|
resp = client.get('/v3/')
|
|
self.assertEqual(http_client.OK, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = v3_VERSION_RESPONSE
|
|
self._paste_in_port(expected['version'],
|
|
'http://localhost:%s/v3/' % self.public_port)
|
|
self.assertEqual(expected, data)
|
|
|
|
# only v3 information should be displayed by requests to /
|
|
v3_only_response = {
|
|
"versions": {
|
|
"values": [
|
|
v3_EXPECTED_RESPONSE
|
|
]
|
|
}
|
|
}
|
|
self._paste_in_port(v3_only_response['versions']['values'][0],
|
|
'http://localhost:%s/v3/' % self.public_port)
|
|
resp = client.get('/')
|
|
self.assertEqual(300, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
self.assertEqual(v3_only_response, data)
|
|
|
|
def _test_json_home(self, path, exp_json_home_data):
|
|
client = TestClient(self.public_app)
|
|
resp = client.get(path, headers={'Accept': 'application/json-home'})
|
|
|
|
self.assertThat(resp.status, tt_matchers.Equals('200 OK'))
|
|
self.assertThat(resp.headers['Content-Type'],
|
|
tt_matchers.Equals('application/json-home'))
|
|
maxDiff = self.maxDiff
|
|
self.maxDiff = None
|
|
# NOTE(morgan): Changed from tt_matchers.Equals to make it easier to
|
|
# determine issues. Reset maxDiff to the original value at the end
|
|
# of the assert.
|
|
self.assertDictEqual(exp_json_home_data,
|
|
jsonutils.loads(resp.body))
|
|
self.maxDiff = maxDiff
|
|
|
|
def test_json_home_v3(self):
|
|
# If the request is /v3 and the Accept header is application/json-home
|
|
# then the server responds with a JSON Home document.
|
|
|
|
exp_json_home_data = {
|
|
'resources': V3_JSON_HOME_RESOURCES}
|
|
|
|
self._test_json_home('/v3', exp_json_home_data)
|
|
|
|
def test_json_home_root(self):
|
|
# If the request is / and the Accept header is application/json-home
|
|
# then the server responds with a JSON Home document.
|
|
|
|
exp_json_home_data = copy.deepcopy({
|
|
'resources': V3_JSON_HOME_RESOURCES})
|
|
json_home.translate_urls(exp_json_home_data, '/v3')
|
|
|
|
self._test_json_home('/', exp_json_home_data)
|
|
|
|
def test_accept_type_handling(self):
|
|
# Accept headers with multiple types and qvalues are handled.
|
|
|
|
def make_request(accept_types=None):
|
|
client = TestClient(self.public_app)
|
|
headers = None
|
|
if accept_types:
|
|
headers = {'Accept': accept_types}
|
|
resp = client.get('/v3', headers=headers)
|
|
self.assertThat(resp.status, tt_matchers.Equals('200 OK'))
|
|
return resp.headers['Content-Type']
|
|
|
|
JSON = discovery.MimeTypes.JSON
|
|
JSON_HOME = discovery.MimeTypes.JSON_HOME
|
|
|
|
JSON_MATCHER = tt_matchers.Equals(JSON)
|
|
JSON_HOME_MATCHER = tt_matchers.Equals(JSON_HOME)
|
|
|
|
# Default is JSON.
|
|
self.assertThat(make_request(), JSON_MATCHER)
|
|
|
|
# Can request JSON and get JSON.
|
|
self.assertThat(make_request(JSON), JSON_MATCHER)
|
|
|
|
# Can request JSONHome and get JSONHome.
|
|
self.assertThat(make_request(JSON_HOME), JSON_HOME_MATCHER)
|
|
|
|
# If request JSON, JSON Home get JSON.
|
|
accept_types = '%s, %s' % (JSON, JSON_HOME)
|
|
self.assertThat(make_request(accept_types), JSON_MATCHER)
|
|
|
|
# If request JSON Home, JSON get JSON.
|
|
accept_types = '%s, %s' % (JSON_HOME, JSON)
|
|
self.assertThat(make_request(accept_types), JSON_MATCHER)
|
|
|
|
# If request JSON Home, JSON;q=0.5 get JSON Home.
|
|
accept_types = '%s, %s;q=0.5' % (JSON_HOME, JSON)
|
|
self.assertThat(make_request(accept_types), JSON_HOME_MATCHER)
|
|
|
|
# If request some unknown mime-type, get JSON.
|
|
self.assertThat(make_request(self.getUniqueString()), JSON_MATCHER)
|
|
|
|
|
|
class VersionSingleAppTestCase(unit.TestCase):
|
|
"""Test running with a single application loaded.
|
|
|
|
These are important because when Keystone is running in Apache httpd
|
|
there's only one application loaded for each instance.
|
|
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(VersionSingleAppTestCase, self).setUp()
|
|
self.load_backends()
|
|
|
|
self.public_port = random.randint(40000, 60000)
|
|
|
|
self.config_fixture.config(
|
|
public_endpoint='http://localhost:%d' % self.public_port)
|
|
|
|
def config_overrides(self):
|
|
super(VersionSingleAppTestCase, self).config_overrides()
|
|
|
|
def _paste_in_port(self, response, port):
|
|
for link in response['links']:
|
|
if link['rel'] == 'self':
|
|
link['href'] = port
|
|
|
|
def _test_version(self, app_name):
|
|
app = self.loadapp(app_name)
|
|
client = TestClient(app)
|
|
resp = client.get('/')
|
|
self.assertEqual(300, resp.status_int)
|
|
data = jsonutils.loads(resp.body)
|
|
expected = VERSIONS_RESPONSE
|
|
url_with_port = 'http://localhost:%s/v3/' % self.public_port
|
|
for version in expected['versions']['values']:
|
|
# TODO(morgan): Eliminate the need to do the "paste-in-port" part
|
|
# of the tests. Ultimately, this is very hacky and shows we are
|
|
# not setting up the test case sanely.
|
|
if version['id'].startswith('v3'):
|
|
self._paste_in_port(
|
|
version, url_with_port)
|
|
# Explicitly check that a location header is set and it is pointing
|
|
# to v3 (The preferred location for now)!
|
|
self.assertIn('Location', resp.headers)
|
|
self.assertEqual(url_with_port, resp.headers['Location'])
|
|
self.assertThat(data, _VersionsEqual(expected))
|
|
|
|
def test_public(self):
|
|
self._test_version('public')
|
|
|
|
def test_admin(self):
|
|
self._test_version('admin')
|