Merge "Remove all v2.0 APIs except the ec2tokens API"

This commit is contained in:
Zuul 2018-02-07 09:01:26 +00:00 committed by Gerrit Code Review
commit bcdbed82ed
12 changed files with 187 additions and 636 deletions

View File

@ -20,7 +20,6 @@ import functools
from oslo_log import log
from keystone.assignment import schema
from keystone.common import authorization
from keystone.common import controller
from keystone.common import provider_api
from keystone.common import validation
@ -35,32 +34,6 @@ LOG = log.getLogger(__name__)
PROVIDERS = provider_api.ProviderAPIs
class TenantAssignment(controller.V2Controller):
"""The V2 Project APIs that are processing assignments."""
@controller.v2_auth_deprecated
def get_projects_for_token(self, request, **kw):
"""Get valid tenants for token based on token used to authenticate.
Pulls the token from the context, validates it and gets the valid
tenants for the user in the token.
Doesn't care about token scopedness.
"""
token_ref = authorization.get_token_ref(request.context_dict)
tenant_refs = (
PROVIDERS.assignment_api.list_projects_for_user(token_ref.user_id))
tenant_refs = [self.v3_to_v2_project(ref) for ref in tenant_refs
if ref['domain_id'] == CONF.identity.default_domain_id]
params = {
'limit': request.params.get('limit'),
'marker': request.params.get('marker'),
}
return self.format_project_list(tenant_refs, **params)
class ProjectAssignmentV3(controller.V3Controller):
"""The V3 Project APIs that are processing assignments."""

View File

@ -46,11 +46,184 @@ from keystone.common import controller
from keystone.common import provider_api
from keystone.common import utils
from keystone.common import wsgi
import keystone.conf
from keystone import exception
from keystone.i18n import _
from keystone.token import controllers as token_controllers
CRED_TYPE_EC2 = 'ec2'
CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs
class V2TokenDataHelper(provider_api.ProviderAPIMixin, object):
"""Create V2 token data."""
def v3_to_v2_token(self, v3_token_data, token_id):
"""Convert v3 token data into v2.0 token data.
This method expects a dictionary generated from
V3TokenDataHelper.get_token_data() and converts it to look like a v2.0
token dictionary.
:param v3_token_data: dictionary formatted for v3 tokens
:param token_id: ID of the token being converted
:returns: dictionary formatted for v2 tokens
:raises keystone.exception.Unauthorized: If a specific token type is
not supported in v2.
"""
token_data = {}
# Build v2 token
v3_token = v3_token_data['token']
# NOTE(lbragstad): Version 2.0 tokens don't know about any domain other
# than the default domain specified in the configuration.
domain_id = v3_token.get('domain', {}).get('id')
if domain_id and CONF.identity.default_domain_id != domain_id:
msg = ('Unable to validate domain-scoped tokens outside of the '
'default domain')
raise exception.Unauthorized(msg)
token = {}
token['expires'] = v3_token.get('expires_at')
token['issued_at'] = v3_token.get('issued_at')
token['audit_ids'] = v3_token.get('audit_ids')
if v3_token.get('bind'):
token['bind'] = v3_token['bind']
token['id'] = token_id
if 'project' in v3_token:
# v3 token_data does not contain all tenant attributes
tenant = PROVIDERS.resource_api.get_project(
v3_token['project']['id'])
# Drop domain specific fields since v2 calls are not domain-aware.
token['tenant'] = controller.V2Controller.v3_to_v2_project(
tenant)
token_data['token'] = token
# Build v2 user
v3_user = v3_token['user']
user = controller.V2Controller.v3_to_v2_user(v3_user)
if 'OS-TRUST:trust' in v3_token:
v3_trust = v3_token['OS-TRUST:trust']
# if token is scoped to trust, both trustor and trustee must
# be in the default domain. Furthermore, the delegated project
# must also be in the default domain
msg = _('Non-default domain is not supported')
if CONF.trust.enabled:
try:
trust_ref = PROVIDERS.trust_api.get_trust(v3_trust['id'])
except exception.TrustNotFound:
raise exception.TokenNotFound(token_id=token_id)
trustee_user_ref = PROVIDERS.identity_api.get_user(
trust_ref['trustee_user_id'])
if (trustee_user_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
trustor_user_ref = PROVIDERS.identity_api.get_user(
trust_ref['trustor_user_id'])
if (trustor_user_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
if trust_ref.get('project_id'):
project_ref = PROVIDERS.resource_api.get_project(
trust_ref['project_id'])
if (project_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
token_data['trust'] = {
'impersonation': v3_trust['impersonation'],
'id': v3_trust['id'],
'trustee_user_id': v3_trust['trustee_user']['id'],
'trustor_user_id': v3_trust['trustor_user']['id']
}
if 'OS-OAUTH1' in v3_token:
msg = ('Unable to validate Oauth tokens using the version v2.0 '
'API.')
raise exception.Unauthorized(msg)
if 'OS-FEDERATION' in v3_token['user']:
msg = _('Unable to validate Federation tokens using the version '
'v2.0 API.')
raise exception.Unauthorized(msg)
# Set user roles
user['roles'] = []
role_ids = []
for role in v3_token.get('roles', []):
role_ids.append(role.pop('id'))
user['roles'].append(role)
user['roles_links'] = []
token_data['user'] = user
# Get and build v2 service catalog
token_data['serviceCatalog'] = []
if 'tenant' in token:
catalog_ref = PROVIDERS.catalog_api.get_catalog(
user['id'], token['tenant']['id'])
if catalog_ref:
token_data['serviceCatalog'] = self.format_catalog(catalog_ref)
# Build v2 metadata
metadata = {}
metadata['roles'] = role_ids
# Setting is_admin to keep consistency in v2 response
metadata['is_admin'] = 0
token_data['metadata'] = metadata
return {'access': token_data}
@classmethod
def format_catalog(cls, catalog_ref):
"""Munge catalogs from internal to output format.
Internal catalogs look like::
{$REGION: {
{$SERVICE: {
$key1: $value1,
...
}
}
}
The legacy api wants them to look like::
[{'name': $SERVICE[name],
'type': $SERVICE,
'endpoints': [{
'tenantId': $tenant_id,
...
'region': $REGION,
}],
'endpoints_links': [],
}]
"""
if not catalog_ref:
return []
services = {}
for region, region_ref in catalog_ref.items():
for service, service_ref in region_ref.items():
new_service_ref = services.get(service, {})
new_service_ref['name'] = service_ref.pop('name')
new_service_ref['type'] = service
new_service_ref['endpoints_links'] = []
service_ref['region'] = region
endpoints_ref = new_service_ref.get('endpoints', [])
endpoints_ref.append(service_ref)
new_service_ref['endpoints'] = endpoints_ref
services[service] = new_service_ref
return list(services.values())
@six.add_metaclass(abc.ABCMeta)
@ -280,7 +453,7 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
token_id, token_data = self.token_provider_api.issue_token(
user_ref['id'], method_names, project_id=project_ref['id'])
v2_helper = token_controllers.V2TokenDataHelper()
v2_helper = V2TokenDataHelper()
token_data = v2_helper.v3_to_v2_token(token_data, token_id)
return token_data

View File

@ -28,7 +28,6 @@ import hmac
import six
from six.moves import http_client
from keystone.common import extension
from keystone.common import json_home
from keystone.common import utils
from keystone.common import wsgi
@ -37,24 +36,6 @@ from keystone import exception
from keystone.i18n import _
EXTENSION_DATA = {
'name': 'OpenStack S3 API',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
's3tokens/v1.0',
'alias': 's3tokens',
'updated': '2013-07-07T12:00:0-00:00',
'description': 'OpenStack S3 API.',
'links': [
{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/'
'api-ref-identity-v2-ext.html',
}
]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
class S3Extension(wsgi.V3ExtensionRouter):
def add_routes(self, mapper):
controller = S3Controller()

View File

@ -15,7 +15,6 @@
import uuid
from keystone.common import cache
from keystone.common import extension
from keystone.common import manager
from keystone.common import provider_api
import keystone.conf
@ -30,20 +29,6 @@ MEMOIZE = cache.get_memoization_decorator(group='federation')
CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs
EXTENSION_DATA = {
'name': 'OpenStack Federation APIs',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
'OS-FEDERATION/v1.0',
'alias': 'OS-FEDERATION',
'updated': '2013-12-17T12:00:0-00:00',
'description': 'OpenStack Identity Providers Mechanism.',
'links': [{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/api-ref-identity-v3-ext.html',
}]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
class Manager(manager.Manager):

View File

@ -22,7 +22,6 @@ import oauthlib.common
from oauthlib import oauth1
from oslo_log import log
from keystone.common import extension
from keystone.common import manager
import keystone.conf
from keystone import exception
@ -58,25 +57,6 @@ def token_generator(*args, **kwargs):
return uuid.uuid4().hex
EXTENSION_DATA = {
'name': 'OpenStack OAUTH1 API',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
'OS-OAUTH1/v1.0',
'alias': 'OS-OAUTH1',
'updated': '2013-07-07T12:00:0-00:00',
'description': 'OpenStack OAuth 1.0a Delegated Auth Mechanism.',
'links': [
{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/'
'api-ref-identity-v3-ext.html',
}
]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
def get_oauth_headers(headers):
parameters = {}

View File

@ -13,7 +13,6 @@
"""Main entry point into the Revoke service."""
from keystone.common import cache
from keystone.common import extension
from keystone.common import manager
import keystone.conf
from keystone import exception
@ -24,25 +23,6 @@ from keystone import notifications
CONF = keystone.conf.CONF
EXTENSION_DATA = {
'name': 'OpenStack Revoke API',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
'OS-REVOKE/v1.0',
'alias': 'OS-REVOKE',
'updated': '2014-02-24T20:51:0-00:00',
'description': 'OpenStack revoked token reporting mechanism.',
'links': [
{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/'
'api-ref-identity-v3-ext.html',
}
]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
# This builds a discrete cache region dedicated to revoke events. The API can
# return a filtered list based upon last fetchtime. This is deprecated but
# must be maintained.

View File

@ -12,6 +12,5 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystone.token import controllers # noqa
from keystone.token import persistence # noqa
from keystone.token import provider # noqa

View File

@ -17,7 +17,6 @@ import functools
import webob
from keystone.common import controller
from keystone.common import extension
from keystone.common import json_home
from keystone.common import wsgi
import keystone.conf
@ -25,23 +24,6 @@ from keystone import exception
CONF = keystone.conf.CONF
EXTENSION_DATA = {
'name': 'OpenStack Simple Certificate API',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
'OS-SIMPLE-CERT/v1.0',
'alias': 'OS-SIMPLE-CERT',
'updated': '2014-01-20T12:00:0-00:00',
'description': 'OpenStack simple certificate retrieval extension',
'links': [
{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/'
'api-ref-identity-v2-ext.html',
}
]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
build_resource_relation = functools.partial(
json_home.build_v3_extension_resource_relation,

View File

@ -1,450 +0,0 @@
# Copyright 2013 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.
from keystone.common import controller
from keystone.common import provider_api
from keystone.common import utils
from keystone.common import wsgi
import keystone.conf
from keystone import exception
from keystone.i18n import _
from keystone.models import token_model
from keystone.token.providers import common
CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs
def authentication_method_generator(request, auth):
"""Given an request return a suitable authentication method.
This is simply a generator to handle matching an authentication request
with the appropriate authentication method.
:param auth: Dictionary containing authentication information from the
request.
:returns: An authentication method class object.
"""
if auth is None:
raise exception.ValidationError(attribute='auth',
target='request body')
if request.environ.get('REMOTE_USER'):
method = ExternalAuthenticationMethod()
elif 'token' in auth:
method = TokenAuthenticationMethod()
elif 'passwordCredentials' in auth:
method = LocalAuthenticationMethod()
else:
raise exception.ValidationError(attribute='auth',
target='request body')
return method
class ExternalAuthNotApplicable(Exception):
"""External authentication is not applicable."""
pass
class BaseAuthenticationMethod(provider_api.ProviderAPIMixin, object):
"""Common utilities/dependencies for all authentication method classes."""
def _get_project_id_from_auth(self, auth):
"""Extract and normalize project information from auth dict.
:param auth: Dictionary representing the authentication request.
:returns: A string representing the project in the authentication
request. If project scope isn't present in the request None
is returned.
"""
project_id = auth.get('tenantId')
project_name = auth.get('tenantName')
if project_id:
if len(project_id) > CONF.max_param_size:
raise exception.ValidationSizeError(
attribute='tenantId', size=CONF.max_param_size
)
elif project_name:
if len(project_name) > CONF.max_param_size:
raise exception.ValidationSizeError(
attribute='tenantName', size=CONF.max_param_size
)
if (CONF.resource.project_name_url_safe == 'strict' and
utils.is_not_url_safe(project_name)):
msg = _('Tenant name cannot contain reserved characters.')
raise exception.Unauthorized(message=msg)
try:
project_id = PROVIDERS.resource_api.get_project_by_name(
project_name, CONF.identity.default_domain_id
)['id']
except exception.ProjectNotFound as e:
raise exception.Unauthorized(e)
else:
project_id = None
return project_id
class TokenAuthenticationMethod(BaseAuthenticationMethod):
"""Authenticate using an existing token."""
def _restrict_scope(self, token_model_ref):
"""Determine if rescoping is allowed based on the token model.
:param token_model_ref: `keystone.models.token.KeystoneToken` object.
"""
# A trust token cannot be used to get another token
if token_model_ref.trust_scoped:
raise exception.Forbidden()
if not CONF.token.allow_rescope_scoped_token:
# Do not allow conversion from scoped tokens.
if token_model_ref.project_scoped or token_model_ref.domain_scoped:
raise exception.Forbidden(action=_('rescope a scoped token'))
def authenticate(self, request, auth):
"""Try to authenticate using an already existing token.
:param request: A request object.
:param auth: Dictionary representing the authentication request.
:returns: A tuple containing the user reference, project identifier,
token expiration, bind information, and original audit
information.
"""
if 'token' not in auth:
raise exception.ValidationError(
attribute='token', target='auth')
if 'id' not in auth['token']:
raise exception.ValidationError(
attribute='id', target='token')
old_token = auth['token']['id']
if len(old_token) > CONF.max_token_size:
raise exception.ValidationSizeError(attribute='token',
size=CONF.max_token_size)
try:
v3_token_data = PROVIDERS.token_provider_api.validate_token(
old_token
)
# NOTE(lbragstad): Even though we are not using the v2.0 token
# reference after we translate it in v3_to_v2_token(), we still
# need to perform that check. We have to do this because
# v3_to_v2_token will ensure we don't use specific tokens only
# attainable via v3 to get new tokens on v2.0. For example, an
# exception would be raised if we passed a federated token to
# v3_to_v2_token, because federated tokens aren't supported by
# v2.0 (the same applies to OAuth tokens, domain-scoped tokens,
# etc..).
v2_helper = V2TokenDataHelper()
v2_helper.v3_to_v2_token(v3_token_data, old_token)
token_model_ref = token_model.KeystoneToken(
token_id=old_token,
token_data=v3_token_data
)
except exception.NotFound as e:
raise exception.Unauthorized(e)
wsgi.validate_token_bind(request.context_dict, token_model_ref)
self._restrict_scope(token_model_ref)
user_id = token_model_ref.user_id
project_id = self._get_project_id_from_auth(auth)
if not CONF.trust.enabled and 'trust_id' in auth:
raise exception.Forbidden('Trusts are disabled.')
elif CONF.trust.enabled and 'trust_id' in auth:
try:
trust_ref = PROVIDERS.trust_api.get_trust(auth['trust_id'])
except exception.TrustNotFound:
raise exception.Forbidden()
# If a trust is being used to obtain access to another project and
# the other project doesn't match the project in the trust, we need
# to bail because trusts are only good up to a single project.
if (trust_ref['project_id'] and
project_id != trust_ref['project_id']):
raise exception.Forbidden()
expiry = token_model_ref.expires
user_ref = PROVIDERS.identity_api.get_user(user_id)
bind = token_model_ref.bind
original_audit_id = token_model_ref.audit_chain_id
return (user_ref, project_id, expiry, bind, original_audit_id)
class LocalAuthenticationMethod(BaseAuthenticationMethod):
"""Authenticate against a local backend using password credentials."""
def authenticate(self, request, auth):
"""Try to authenticate against the identity backend.
:param request: A request object.
:param auth: Dictionary representing the authentication request.
:returns: A tuple containing the user reference, project identifier,
token expiration, bind information, and original audit
information.
"""
if 'password' not in auth['passwordCredentials']:
raise exception.ValidationError(
attribute='password', target='passwordCredentials')
password = auth['passwordCredentials']['password']
if password and len(password) > CONF.identity.max_password_length:
raise exception.ValidationSizeError(
attribute='password', size=CONF.identity.max_password_length)
if (not auth['passwordCredentials'].get('userId') and
not auth['passwordCredentials'].get('username')):
raise exception.ValidationError(
attribute='username or userId',
target='passwordCredentials')
user_id = auth['passwordCredentials'].get('userId')
if user_id and len(user_id) > CONF.max_param_size:
raise exception.ValidationSizeError(attribute='userId',
size=CONF.max_param_size)
username = auth['passwordCredentials'].get('username', '')
if username:
if len(username) > CONF.max_param_size:
raise exception.ValidationSizeError(attribute='username',
size=CONF.max_param_size)
try:
user_ref = PROVIDERS.identity_api.get_user_by_name(
username, CONF.identity.default_domain_id)
user_id = user_ref['id']
except exception.UserNotFound as e:
raise exception.Unauthorized(e)
try:
user_ref = PROVIDERS.identity_api.authenticate(
request,
user_id=user_id,
password=password)
except AssertionError as e:
raise exception.Unauthorized(e.args[0])
project_id = self._get_project_id_from_auth(auth)
expiry = common.default_expire_time()
bind = None
audit_id = None
return (user_ref, project_id, expiry, bind, audit_id)
class ExternalAuthenticationMethod(BaseAuthenticationMethod):
"""Authenticate using an external authentication method."""
def authenticate(self, request, auth):
"""Try to authenticate an external user via REMOTE_USER variable.
:param request: A request object.
:param auth: Dictionary representing the authentication request.
:returns: A tuple containing the user reference, project identifier,
token expiration, bind information, and original audit
information.
"""
username = request.environ.get('REMOTE_USER')
if not username:
raise ExternalAuthNotApplicable()
try:
user_ref = PROVIDERS.identity_api.get_user_by_name(
username, CONF.identity.default_domain_id)
except exception.UserNotFound as e:
raise exception.Unauthorized(e)
tenant_id = self._get_project_id_from_auth(auth)
expiry = common.default_expire_time()
bind = None
if ('kerberos' in CONF.token.bind and
request.environ.get('AUTH_TYPE', '').lower() == 'negotiate'):
bind = {'kerberos': username}
audit_id = None
return (user_ref, tenant_id, expiry, bind, audit_id)
class V2TokenDataHelper(provider_api.ProviderAPIMixin, object):
"""Create V2 token data."""
def v3_to_v2_token(self, v3_token_data, token_id):
"""Convert v3 token data into v2.0 token data.
This method expects a dictionary generated from
V3TokenDataHelper.get_token_data() and converts it to look like a v2.0
token dictionary.
:param v3_token_data: dictionary formatted for v3 tokens
:param token_id: ID of the token being converted
:returns: dictionary formatted for v2 tokens
:raises keystone.exception.Unauthorized: If a specific token type is
not supported in v2.
"""
token_data = {}
# Build v2 token
v3_token = v3_token_data['token']
# NOTE(lbragstad): Version 2.0 tokens don't know about any domain other
# than the default domain specified in the configuration.
domain_id = v3_token.get('domain', {}).get('id')
if domain_id and CONF.identity.default_domain_id != domain_id:
msg = ('Unable to validate domain-scoped tokens outside of the '
'default domain')
raise exception.Unauthorized(msg)
token = {}
token['expires'] = v3_token.get('expires_at')
token['issued_at'] = v3_token.get('issued_at')
token['audit_ids'] = v3_token.get('audit_ids')
if v3_token.get('bind'):
token['bind'] = v3_token['bind']
token['id'] = token_id
if 'project' in v3_token:
# v3 token_data does not contain all tenant attributes
tenant = PROVIDERS.resource_api.get_project(
v3_token['project']['id'])
# Drop domain specific fields since v2 calls are not domain-aware.
token['tenant'] = controller.V2Controller.v3_to_v2_project(
tenant)
token_data['token'] = token
# Build v2 user
v3_user = v3_token['user']
user = controller.V2Controller.v3_to_v2_user(v3_user)
if 'OS-TRUST:trust' in v3_token:
v3_trust = v3_token['OS-TRUST:trust']
# if token is scoped to trust, both trustor and trustee must
# be in the default domain. Furthermore, the delegated project
# must also be in the default domain
msg = _('Non-default domain is not supported')
if CONF.trust.enabled:
try:
trust_ref = PROVIDERS.trust_api.get_trust(v3_trust['id'])
except exception.TrustNotFound:
raise exception.TokenNotFound(token_id=token_id)
trustee_user_ref = PROVIDERS.identity_api.get_user(
trust_ref['trustee_user_id'])
if (trustee_user_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
trustor_user_ref = PROVIDERS.identity_api.get_user(
trust_ref['trustor_user_id'])
if (trustor_user_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
if trust_ref.get('project_id'):
project_ref = PROVIDERS.resource_api.get_project(
trust_ref['project_id'])
if (project_ref['domain_id'] !=
CONF.identity.default_domain_id):
raise exception.Unauthorized(msg)
token_data['trust'] = {
'impersonation': v3_trust['impersonation'],
'id': v3_trust['id'],
'trustee_user_id': v3_trust['trustee_user']['id'],
'trustor_user_id': v3_trust['trustor_user']['id']
}
if 'OS-OAUTH1' in v3_token:
msg = ('Unable to validate Oauth tokens using the version v2.0 '
'API.')
raise exception.Unauthorized(msg)
if 'OS-FEDERATION' in v3_token['user']:
msg = _('Unable to validate Federation tokens using the version '
'v2.0 API.')
raise exception.Unauthorized(msg)
# Set user roles
user['roles'] = []
role_ids = []
for role in v3_token.get('roles', []):
role_ids.append(role.pop('id'))
user['roles'].append(role)
user['roles_links'] = []
token_data['user'] = user
# Get and build v2 service catalog
token_data['serviceCatalog'] = []
if 'tenant' in token:
catalog_ref = PROVIDERS.catalog_api.get_catalog(
user['id'], token['tenant']['id'])
if catalog_ref:
token_data['serviceCatalog'] = self.format_catalog(catalog_ref)
# Build v2 metadata
metadata = {}
metadata['roles'] = role_ids
# Setting is_admin to keep consistency in v2 response
metadata['is_admin'] = 0
token_data['metadata'] = metadata
return {'access': token_data}
@classmethod
def format_catalog(cls, catalog_ref):
"""Munge catalogs from internal to output format.
Internal catalogs look like::
{$REGION: {
{$SERVICE: {
$key1: $value1,
...
}
}
}
The legacy api wants them to look like::
[{'name': $SERVICE[name],
'type': $SERVICE,
'endpoints': [{
'tenantId': $tenant_id,
...
'region': $REGION,
}],
'endpoints_links': [],
}]
"""
if not catalog_ref:
return []
services = {}
for region, region_ref in catalog_ref.items():
for service, service_ref in region_ref.items():
new_service_ref = services.get(service, {})
new_service_ref['name'] = service_ref.pop('name')
new_service_ref['type'] = service
new_service_ref['endpoints_links'] = []
service_ref['region'] = region
endpoints_ref = new_service_ref.get('endpoints', [])
endpoints_ref.append(service_ref)
new_service_ref['endpoints'] = endpoints_ref
services[service] = new_service_ref
return list(services.values())

View File

@ -1,55 +0,0 @@
# 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.
from keystone import assignment
from keystone.common import extension
from keystone.common import wsgi
extension.register_admin_extension(
'OS-KSADM', {
'name': 'OpenStack Keystone Admin',
'namespace': 'https://docs.openstack.org/identity/api/ext/'
'OS-KSADM/v1.0',
'alias': 'OS-KSADM',
'updated': '2013-07-11T17:14:00-00:00',
'description': 'OpenStack extensions to Keystone v2.0 API '
'enabling Administrative Operations.',
'links': [
{
'rel': 'describedby',
'type': 'text/html',
'href': 'https://developer.openstack.org/'
'api-ref-identity-v2-ext.html',
}
]})
class Router(wsgi.ComposableRouter):
"""Previously known as the OS-KSADM extension.
Provides a bunch of CRUD operations for internal data types.
"""
def add_routes(self, mapper):
assignment_tenant_controller = (
assignment.controllers.TenantAssignment())
# Tenant Operations
mapper.connect(
'/tenants/{tenant_id}/users',
controller=assignment_tenant_controller,
action='get_project_users',
conditions=dict(method=['GET']))

View File

@ -36,7 +36,6 @@ from keystone.resource import routers as resource_routers
from keystone.revoke import routers as revoke_routers
from keystone.token import _simple_cert as simple_cert_ext
from keystone.trust import routers as trust_routers
from keystone.v2_crud import admin_crud
from keystone.version import controllers
from keystone.version import routers
@ -82,20 +81,24 @@ def warn_local_conf(f):
@warn_local_conf
def public_app_factory(global_conf, **local_conf):
controllers.register_version('v2.0')
return wsgi.ComposingRouter(routes.Mapper(),
[assignment_routers.Public(),
routers.VersionV2('public'),
routers.Extension(False)])
# NOTE(lbragstad): Only wire up the v2.0 version controller. We should keep
# this here because we still support the ec2tokens API on the v2.0 path
# until T. Once that is removed, we can remove the rest of the v2.0 routers
# and whatnot. The ec2token controller is actually wired up by the paste
# pipeline.
return wsgi.ComposingRouter(routes.Mapper(), [routers.VersionV2('public')])
@fail_gracefully
@warn_local_conf
def admin_app_factory(global_conf, **local_conf):
controllers.register_version('v2.0')
return wsgi.ComposingRouter(routes.Mapper(),
[admin_crud.Router(),
routers.VersionV2('admin'),
routers.Extension()])
# NOTE(lbragstad): Only wire up the v2.0 version controller. We should keep
# this here because we still support the ec2tokens API on the v2.0 path
# until T. Once that is removed, we can remove the rest of the v2.0 routers
# and whatnot. The ec2token controller is actually wired up by the paste
# pipeline.
return wsgi.ComposingRouter(routes.Mapper(), [routers.VersionV2('admin')])
@fail_gracefully