aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/token/providers/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/token/providers/common.py')
-rw-r--r--keystone-moon/keystone/token/providers/common.py157
1 files changed, 93 insertions, 64 deletions
diff --git a/keystone-moon/keystone/token/providers/common.py b/keystone-moon/keystone/token/providers/common.py
index 717e1495..b71458cd 100644
--- a/keystone-moon/keystone/token/providers/common.py
+++ b/keystone-moon/keystone/token/providers/common.py
@@ -14,17 +14,17 @@
from oslo_config import cfg
from oslo_log import log
+from oslo_log import versionutils
from oslo_serialization import jsonutils
-from oslo_utils import timeutils
import six
from six.moves.urllib import parse
from keystone.common import controller as common_controller
from keystone.common import dependency
-from keystone.contrib import federation
+from keystone.common import utils
+from keystone.contrib.federation import constants as federation_constants
from keystone import exception
from keystone.i18n import _, _LE
-from keystone.openstack.common import versionutils
from keystone import token
from keystone.token import provider
@@ -37,18 +37,33 @@ CONF = cfg.CONF
class V2TokenDataHelper(object):
"""Creates V2 token data."""
- def v3_to_v2_token(self, token_id, v3_token_data):
+ def v3_to_v2_token(self, v3_token_data):
token_data = {}
# Build v2 token
v3_token = v3_token_data['token']
token = {}
- token['id'] = token_id
token['expires'] = v3_token.get('expires_at')
token['issued_at'] = v3_token.get('issued_at')
token['audit_ids'] = v3_token.get('audit_ids')
+ # Bail immediately if this is a domain-scoped token, which is not
+ # supported by the v2 API at all.
+ if 'domain' in v3_token:
+ raise exception.Unauthorized(_(
+ 'Domains are not supported by the v2 API. Please use the v3 '
+ 'API instead.'))
+
+ # Bail if this is a project-scoped token outside the default domain,
+ # which may result in a namespace collision with a project inside the
+ # default domain.
if 'project' in v3_token:
+ if (v3_token['project']['domain']['id'] !=
+ CONF.identity.default_domain_id):
+ raise exception.Unauthorized(_(
+ 'Project not found in the default domain (please use the '
+ 'v3 API instead): %s') % v3_token['project']['id'])
+
# v3 token_data does not contain all tenant attributes
tenant = self.resource_api.get_project(
v3_token['project']['id'])
@@ -58,14 +73,32 @@ class V2TokenDataHelper(object):
# Build v2 user
v3_user = v3_token['user']
+
+ # Bail if this is a token outside the default domain,
+ # which may result in a namespace collision with a project inside the
+ # default domain.
+ if ('domain' in v3_user and v3_user['domain']['id'] !=
+ CONF.identity.default_domain_id):
+ raise exception.Unauthorized(_(
+ 'User not found in the default domain (please use the v3 API '
+ 'instead): %s') % v3_user['id'])
+
user = common_controller.V2Controller.v3_to_v2_user(v3_user)
+ # Maintain Trust Data
+ if 'OS-TRUST:trust' in v3_token:
+ v3_trust_data = v3_token['OS-TRUST:trust']
+ token_data['trust'] = {
+ 'trustee_user_id': v3_trust_data['trustee_user']['id'],
+ 'id': v3_trust_data['id'],
+ 'trustor_user_id': v3_trust_data['trustor_user']['id'],
+ 'impersonation': v3_trust_data['impersonation']
+ }
+
# Set user roles
user['roles'] = []
role_ids = []
for role in v3_token.get('roles', []):
- # Filter role id since it's not included in v2 token response
- role_ids.append(role.pop('id'))
user['roles'].append(role)
user['roles_links'] = []
@@ -99,7 +132,7 @@ class V2TokenDataHelper(object):
expires = token_ref.get('expires', provider.default_expire_time())
if expires is not None:
if not isinstance(expires, six.text_type):
- expires = timeutils.isotime(expires)
+ expires = utils.isotime(expires)
token_data = token_ref.get('token_data')
if token_data:
@@ -112,7 +145,7 @@ class V2TokenDataHelper(object):
o = {'access': {'token': {'id': token_ref['id'],
'expires': expires,
- 'issued_at': timeutils.strtime(),
+ 'issued_at': utils.strtime(),
'audit_ids': audit_info
},
'user': {'id': user_ref['id'],
@@ -181,8 +214,8 @@ class V2TokenDataHelper(object):
return []
services = {}
- for region, region_ref in six.iteritems(catalog_ref):
- for service, service_ref in six.iteritems(region_ref):
+ 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
@@ -195,7 +228,7 @@ class V2TokenDataHelper(object):
new_service_ref['endpoints'] = endpoints_ref
services[service] = new_service_ref
- return services.values()
+ return list(services.values())
@dependency.requires('assignment_api', 'catalog_api', 'federation_api',
@@ -239,10 +272,26 @@ class V3TokenDataHelper(object):
user_id, project_id)
return [self.role_api.get_role(role_id) for role_id in roles]
- def _populate_roles_for_groups(self, group_ids,
- project_id=None, domain_id=None,
- user_id=None):
- def _check_roles(roles, user_id, project_id, domain_id):
+ def populate_roles_for_groups(self, token_data, group_ids,
+ project_id=None, domain_id=None,
+ user_id=None):
+ """Populate roles basing on provided groups and project/domain
+
+ Used for ephemeral users with dynamically assigned groups.
+ This method does not return anything, yet it modifies token_data in
+ place.
+
+ :param token_data: a dictionary used for building token response
+ :group_ids: list of group IDs a user is a member of
+ :project_id: project ID to scope to
+ :domain_id: domain ID to scope to
+ :user_id: user ID
+
+ :raises: exception.Unauthorized - when no roles were found for a
+ (group_ids, project_id) or (group_ids, domain_id) pairs.
+
+ """
+ def check_roles(roles, user_id, project_id, domain_id):
# User was granted roles so simply exit this function.
if roles:
return
@@ -264,8 +313,8 @@ class V3TokenDataHelper(object):
roles = self.assignment_api.get_roles_for_groups(group_ids,
project_id,
domain_id)
- _check_roles(roles, user_id, project_id, domain_id)
- return roles
+ check_roles(roles, user_id, project_id, domain_id)
+ token_data['roles'] = roles
def _populate_user(self, token_data, user_id, trust):
if 'user' in token_data:
@@ -393,10 +442,10 @@ class V3TokenDataHelper(object):
if not expires:
expires = provider.default_expire_time()
if not isinstance(expires, six.string_types):
- expires = timeutils.isotime(expires, subsecond=True)
+ expires = utils.isotime(expires, subsecond=True)
token_data['expires_at'] = expires
token_data['issued_at'] = (issued_at or
- timeutils.isotime(subsecond=True))
+ utils.isotime(subsecond=True))
def _populate_audit_info(self, token_data, audit_info=None):
if audit_info is None or isinstance(audit_info, six.string_types):
@@ -420,7 +469,7 @@ class V3TokenDataHelper(object):
versionutils.deprecated(
what='passing token data with "extras"',
as_of=versionutils.deprecated.KILO,
- in_favor_of='well-defined APIs')
+ in_favor_of='well-defined APIs')(lambda: None)()
token_data = {'methods': method_names,
'extras': extras}
@@ -490,13 +539,21 @@ class BaseProvider(provider.Provider):
return token_id, token_data
def _is_mapped_token(self, auth_context):
- return (federation.IDENTITY_PROVIDER in auth_context and
- federation.PROTOCOL in auth_context)
+ return (federation_constants.IDENTITY_PROVIDER in auth_context and
+ federation_constants.PROTOCOL in auth_context)
def issue_v3_token(self, user_id, method_names, expires_at=None,
project_id=None, domain_id=None, auth_context=None,
trust=None, metadata_ref=None, include_catalog=True,
parent_audit_id=None):
+ if auth_context and auth_context.get('bind'):
+ # NOTE(lbragstad): Check if the token provider being used actually
+ # supports bind authentication methods before proceeding.
+ if not self._supports_bind_authentication:
+ raise exception.NotImplemented(_(
+ 'The configured token provider does not support bind '
+ 'authentication.'))
+
# for V2, trust is stashed in metadata_ref
if (CONF.trust.enabled and not trust and metadata_ref and
'trust_id' in metadata_ref):
@@ -530,38 +587,30 @@ class BaseProvider(provider.Provider):
return token_id, token_data
def _handle_mapped_tokens(self, auth_context, project_id, domain_id):
- def get_federated_domain():
- return (CONF.federation.federated_domain_name or
- federation.FEDERATED_DOMAIN_KEYWORD)
-
- federated_domain = get_federated_domain()
user_id = auth_context['user_id']
group_ids = auth_context['group_ids']
- idp = auth_context[federation.IDENTITY_PROVIDER]
- protocol = auth_context[federation.PROTOCOL]
+ idp = auth_context[federation_constants.IDENTITY_PROVIDER]
+ protocol = auth_context[federation_constants.PROTOCOL]
token_data = {
'user': {
'id': user_id,
'name': parse.unquote(user_id),
- federation.FEDERATION: {
+ federation_constants.FEDERATION: {
+ 'groups': [{'id': x} for x in group_ids],
'identity_provider': {'id': idp},
'protocol': {'id': protocol}
},
'domain': {
- 'id': federated_domain,
- 'name': federated_domain
+ 'id': CONF.federation.federated_domain_name,
+ 'name': CONF.federation.federated_domain_name
}
}
}
if project_id or domain_id:
- roles = self.v3_token_data_helper._populate_roles_for_groups(
- group_ids, project_id, domain_id, user_id)
- token_data.update({'roles': roles})
- else:
- token_data['user'][federation.FEDERATION].update({
- 'groups': [{'id': x} for x in group_ids]
- })
+ self.v3_token_data_helper.populate_roles_for_groups(
+ token_data, group_ids, project_id, domain_id, user_id)
+
return token_data
def _verify_token_ref(self, token_ref):
@@ -637,30 +686,10 @@ class BaseProvider(provider.Provider):
# management layer is now pluggable, one can always provide
# their own implementation to suit their needs.
token_data = token_ref.get('token_data')
- if (not token_data or
- self.get_token_version(token_data) !=
- token.provider.V2):
- # token is created by old v2 logic
- metadata_ref = token_ref['metadata']
- roles_ref = []
- for role_id in metadata_ref.get('roles', []):
- roles_ref.append(self.role_api.get_role(role_id))
-
- # Get a service catalog if possible
- # This is needed for on-behalf-of requests
- catalog_ref = None
- if token_ref.get('tenant'):
- catalog_ref = self.catalog_api.get_catalog(
- token_ref['user']['id'],
- token_ref['tenant']['id'])
-
- trust_ref = None
- if CONF.trust.enabled and 'trust_id' in metadata_ref:
- trust_ref = self.trust_api.get_trust(
- metadata_ref['trust_id'])
-
- token_data = self.v2_token_data_helper.format_token(
- token_ref, roles_ref, catalog_ref, trust_ref)
+ if (self.get_token_version(token_data) != token.provider.V2):
+ # Validate the V3 token as V2
+ token_data = self.v2_token_data_helper.v3_to_v2_token(
+ token_data)
trust_id = token_data['access'].get('trust', {}).get('id')
if trust_id: