From 92d11d139e9f76d4fd76859aea78643fc32ef36b Mon Sep 17 00:00:00 2001 From: asteroide Date: Thu, 24 Sep 2015 16:27:16 +0200 Subject: Update Keystone code from repository. Change-Id: Ib3d0a06b10902fcc6d520f58e85aa617bc326d00 --- keystone-moon/keystone/middleware/core.py | 119 ++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 7 deletions(-) (limited to 'keystone-moon/keystone/middleware/core.py') diff --git a/keystone-moon/keystone/middleware/core.py b/keystone-moon/keystone/middleware/core.py index 62ff291a..75be5b27 100644 --- a/keystone-moon/keystone/middleware/core.py +++ b/keystone-moon/keystone/middleware/core.py @@ -13,16 +13,21 @@ # under the License. from oslo_config import cfg +from oslo_context import context as oslo_context from oslo_log import log from oslo_log import versionutils from oslo_middleware import sizelimit from oslo_serialization import jsonutils from keystone.common import authorization +from keystone.common import tokenless_auth from keystone.common import wsgi +from keystone.contrib.federation import constants as federation_constants +from keystone.contrib.federation import utils from keystone import exception -from keystone.i18n import _LW +from keystone.i18n import _, _LI, _LW from keystone.models import token_model +from keystone.token.providers import common CONF = cfg.CONF @@ -194,17 +199,117 @@ class AuthContextMiddleware(wsgi.Middleware): LOG.warning(_LW('RBAC: Invalid token')) raise exception.Unauthorized() + def _build_tokenless_auth_context(self, env): + """Build the authentication context. + + The context is built from the attributes provided in the env, + such as certificate and scope attributes. + """ + tokenless_helper = tokenless_auth.TokenlessAuthHelper(env) + + (domain_id, project_id, trust_ref, unscoped) = ( + tokenless_helper.get_scope()) + user_ref = tokenless_helper.get_mapped_user( + project_id, + domain_id) + + # NOTE(gyee): if it is an ephemeral user, the + # given X.509 SSL client cert does not need to map to + # an existing user. + if user_ref['type'] == utils.UserType.EPHEMERAL: + auth_context = {} + auth_context['group_ids'] = user_ref['group_ids'] + auth_context[federation_constants.IDENTITY_PROVIDER] = ( + user_ref[federation_constants.IDENTITY_PROVIDER]) + auth_context[federation_constants.PROTOCOL] = ( + user_ref[federation_constants.PROTOCOL]) + if domain_id and project_id: + msg = _('Scoping to both domain and project is not allowed') + raise ValueError(msg) + if domain_id: + auth_context['domain_id'] = domain_id + if project_id: + auth_context['project_id'] = project_id + auth_context['roles'] = user_ref['roles'] + else: + # it's the local user, so token data is needed. + token_helper = common.V3TokenDataHelper() + token_data = token_helper.get_token_data( + user_id=user_ref['id'], + method_names=[CONF.tokenless_auth.protocol], + domain_id=domain_id, + project_id=project_id) + + auth_context = {'user_id': user_ref['id']} + auth_context['is_delegated_auth'] = False + if domain_id: + auth_context['domain_id'] = domain_id + if project_id: + auth_context['project_id'] = project_id + auth_context['roles'] = [role['name'] for role + in token_data['token']['roles']] + return auth_context + + def _validate_trusted_issuer(self, env): + """To further filter the certificates that are trusted. + + If the config option 'trusted_issuer' is absent or does + not contain the trusted issuer DN, no certificates + will be allowed in tokenless authorization. + + :param env: The env contains the client issuer's attributes + :type env: dict + :returns: True if client_issuer is trusted; otherwise False + """ + + if not CONF.tokenless_auth.trusted_issuer: + return False + + client_issuer = env.get(CONF.tokenless_auth.issuer_attribute) + if not client_issuer: + msg = _LI('Cannot find client issuer in env by the ' + 'issuer attribute - %s.') + LOG.info(msg, CONF.tokenless_auth.issuer_attribute) + return False + + if client_issuer in CONF.tokenless_auth.trusted_issuer: + return True + + msg = _LI('The client issuer %(client_issuer)s does not match with ' + 'the trusted issuer %(trusted_issuer)s') + LOG.info( + msg, {'client_issuer': client_issuer, + 'trusted_issuer': CONF.tokenless_auth.trusted_issuer}) + + return False + def process_request(self, request): - if AUTH_TOKEN_HEADER not in request.headers: - LOG.debug(('Auth token not in the request header. ' - 'Will not build auth context.')) - return + + # The request context stores itself in thread-local memory for logging. + oslo_context.RequestContext( + request_id=request.environ.get('openstack.request_id')) if authorization.AUTH_CONTEXT_ENV in request.environ: - msg = _LW('Auth context already exists in the request environment') + msg = _LW('Auth context already exists in the request ' + 'environment; it will be used for authorization ' + 'instead of creating a new one.') LOG.warning(msg) return - auth_context = self._build_auth_context(request) + # NOTE(gyee): token takes precedence over SSL client certificates. + # This will preserve backward compatibility with the existing + # behavior. Tokenless authorization with X.509 SSL client + # certificate is effectively disabled if no trusted issuers are + # provided. + if AUTH_TOKEN_HEADER in request.headers: + auth_context = self._build_auth_context(request) + elif self._validate_trusted_issuer(request.environ): + auth_context = self._build_tokenless_auth_context( + request.environ) + else: + LOG.debug('There is either no auth token in the request or ' + 'the certificate issuer is not trusted. No auth ' + 'context will be set.') + return LOG.debug('RBAC: auth_context: %s', auth_context) request.environ[authorization.AUTH_CONTEXT_ENV] = auth_context -- cgit 1.2.3-korg