Ejemplo n.º 1
0
    def _get_sso_origin_host(self, request):
        """Validate and return originating dashboard URL.

        Make sure the parameter is specified in the request's URL as well its
        value belongs to a list of trusted dashboards.

        :param context: request's context
        :raises keystone.exception.ValidationError: ``origin`` query parameter
            was not specified. The URL is deemed invalid.
        :raises keystone.exception.Unauthorized: URL specified in origin query
            parameter does not exist in list of websso trusted dashboards.
        :returns: URL with the originating dashboard

        """
        origin = request.params.get('origin')

        if not origin:
            msg = _('Request must have an origin query parameter')
            LOG.error(msg)
            raise exception.ValidationError(msg)

        host = urllib.parse.unquote_plus(origin)

        # change trusted_dashboard hostnames to lowercase before comparison
        trusted_dashboards = [
            k_utils.lower_case_hostname(trusted)
            for trusted in CONF.federation.trusted_dashboard
        ]

        if host not in trusted_dashboards:
            msg = _('%(host)s is not a trusted dashboard host')
            msg = msg % {'host': host}
            LOG.error(msg)
            raise exception.Unauthorized(msg)

        return host
Ejemplo n.º 2
0
    def _get_domain_id_for_list_request(self, context):
        """Get the domain_id for a v3 list call.

        If we running with multiple domain drivers, then the caller must
        specify a domain_id either as a filter or as part of the token scope.

        """
        if not CONF.identity.domain_specific_drivers_enabled:
            # We don't need to specify a domain ID in this case
            return

        if context['query_string'].get('domain_id') is not None:
            return context['query_string'].get('domain_id')

        token_ref = utils.get_token_ref(context)

        if token_ref.domain_scoped:
            return token_ref.domain_id
        elif token_ref.project_scoped:
            return token_ref.project_domain_id
        else:
            LOG.warning(
                _LW('No domain information specified as part of list request'))
            raise exception.Unauthorized()
Ejemplo n.º 3
0
 def lookup_user(self, user_info):
     user_id = user_info.get('id')
     user_name = user_info.get('name')
     user_ref = None
     if not user_id and not user_name:
         raise exception.ValidationError(attribute='id or name',
                                         target='user')
     try:
         if user_name:
             if 'domain' not in user_info:
                 raise exception.ValidationError(attribute='domain',
                                                 target='user')
             domain_ref = self._lookup_domain(user_info['domain'])
             user_ref = self.identity_api.get_user_by_name(
                 context=self.context, user_name=user_name,
                 domain_id=domain_ref['id'])
         else:
             user_ref = self.identity_api.get_user(
                 context=self.context, user_id=user_id)
     except exception.UserNotFound as e:
         LOG.exception(e)
         raise exception.Unauthorized(e)
     self._assert_user_is_enabled(user_ref)
     return user_ref
Ejemplo n.º 4
0
 def __setitem__(self, key, val):
     """Override __setitem__ to prevent conflicting values."""
     if key in self.IDENTITY_ATTRIBUTES and key in self:
         existing_val = self[key]
         if key == 'expires_at':
             # special treatment for 'expires_at', we are going to take
             # the earliest expiration instead.
             if existing_val != val:
                 LOG.info('"expires_at" has conflicting values '
                          '%(existing)s and %(new)s.  Will use the '
                          'earliest value.',
                          {'existing': existing_val, 'new': val})
             if existing_val is None or val is None:
                 val = existing_val or val
             else:
                 val = min(existing_val, val)
         elif existing_val != val:
             msg = _('Unable to reconcile identity attribute %(attribute)s '
                     'as it has conflicting values %(new)s and %(old)s') % (
                         {'attribute': key,
                          'new': val,
                          'old': existing_val})
             raise exception.Unauthorized(msg)
     return super(AuthContext, self).__setitem__(key, val)
Ejemplo n.º 5
0
    def validate_v2_token(self, token_ref):
        """Validate a V2 formatted token.

        :param token_ref: reference describing the token to validate. Note that
                          token_ref is going to be a token ID.
        :returns: the token data
        :raises keystone.exception.TokenNotFound: if token format is invalid
        :raises keystone.exception.Unauthorized: if v3 token is used

        """
        try:
            (user_id, methods,
             audit_ids, domain_id,
             project_id, trust_id,
             federated_info, created_at,
             expires_at) = self.token_formatter.validate_token(token_ref)
        except exception.ValidationError:
            raise exception.TokenNotFound(token_id=token_ref)

        if trust_id or domain_id or federated_info:
            msg = _('This is not a v2.0 Fernet token. Use v3 for trust, '
                    'domain, or federated tokens.')
            raise exception.Unauthorized(msg)

        v3_token_data = self.v3_token_data_helper.get_token_data(
            user_id,
            methods,
            project_id=project_id,
            expires=expires_at,
            issued_at=created_at,
            token=token_ref,
            include_catalog=False,
            audit_info=audit_ids)
        token_data = self.v2_token_data_helper.v3_to_v2_token(v3_token_data)
        token_data['access']['token']['id'] = token_ref
        return token_data
Ejemplo n.º 6
0
 def _lookup_project(self, project_info):
     project_id = project_info.get('id')
     project_name = project_info.get('name')
     project_ref = None
     if not project_id and not project_name:
         raise exception.ValidationError(attribute='id or name',
                                         target='project')
     try:
         if project_name:
             if 'domain' not in project_info:
                 raise exception.ValidationError(attribute='domain',
                                                 target='project')
             domain_ref = self._lookup_domain(project_info['domain'])
             project_ref = self.identity_api.get_project_by_name(
                 context=self.context, tenant_name=project_name,
                 domain_id=domain_ref['id'])
         else:
             project_ref = self.identity_api.get_project(
                 context=self.context, tenant_id=project_id)
     except exception.ProjectNotFound as e:
         LOG.exception(e)
         raise exception.Unauthorized(e)
     self._assert_project_is_enabled(project_ref)
     return project_ref
Ejemplo n.º 7
0
    def _validate_v2_token(self, token_id, belongs_to=None, **kwargs):
        try:
            token_ref = self._verify_token(token_id, belongs_to=belongs_to)
            self._assert_default_domain(token_ref)
            # FIXME(gyee): performance or correctness? Should we return the
            # cached token or reconstruct it? Obviously if we are going with
            # the cached token, any role, project, or domain name changes
            # will not be reflected. One may argue that with PKI tokens,
            # we are essentially doing cached token validation anyway.
            # Lets go with the cached token strategy. Since token
            # 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']
                role_refs = []
                for role_id in metadata_ref.get('roles', []):
                    role_refs.append(self.identity_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'],
                        metadata_ref)
                token_data = self.v2_token_data_helper.get_token_data(
                    token_ref=token_ref,
                    roles_ref=role_refs,
                    catalog_ref=catalog_ref)
            return token_data
        except AssertionError as e:
            LOG.exception(_('Failed to validate token'))
            raise exception.Unauthorized(e)
Ejemplo n.º 8
0
    def _authenticate_token(self, context, auth):
        """Try to authenticate using an already existing token.

        Returns auth_token_data, (user_ref, tenant_ref, metadata_ref)
        """
        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']

        try:
            old_token_ref = self.token_api.get_token(context=context,
                                                     token_id=old_token)
        except exception.NotFound as e:
            raise exception.Unauthorized(e)

        user_ref = old_token_ref['user']
        user_id = user_ref['id']

        current_user_ref = self.identity_api.get_user(context=context,
                                                      user_id=user_id)

        tenant_id = self._get_tenant_id_from_auth(context, auth)

        tenant_ref = self._get_tenant_ref(context, user_id, tenant_id)
        metadata_ref = self._get_metadata_ref(context, user_id, tenant_id)

        expiry = old_token_ref['expires']
        auth_token_data = self._get_auth_token_data(current_user_ref,
                                                    tenant_ref, metadata_ref,
                                                    expiry)

        return auth_token_data, (current_user_ref, tenant_ref, metadata_ref)
Ejemplo n.º 9
0
 def authenticate(self, context, auth_payload, user_context):
     try:
         if 'id' not in auth_payload:
             raise exception.ValidationError(attribute='id',
                                             target=METHOD_NAME)
         token_id = auth_payload['id']
         token_ref = self.token_api.get_token(token_id)
         wsgi.validate_token_bind(context, token_ref)
         user_context.setdefault(
             'user_id', token_ref['token_data']['token']['user']['id'])
         # to support Grizzly-3 to Grizzly-RC1 transition
         expires_at = token_ref['token_data']['token'].get(
             'expires_at', token_ref['token_data']['token'].get('expires'))
         user_context.setdefault('expires_at', expires_at)
         user_context['extras'].update(
             token_ref['token_data']['token']['extras'])
         user_context['method_names'].extend(
             token_ref['token_data']['token']['methods'])
         if ('OS-TRUST:trust' in token_ref['token_data']['token']
                 or 'trust' in token_ref['token_data']['token']):
             raise exception.Forbidden()
     except AssertionError as e:
         LOG.error(e)
         raise exception.Unauthorized(e)
Ejemplo n.º 10
0
    def _get_domain_id_for_request(self, context):
        """Get the domain_id for a v3 call."""

        if context['is_admin']:
            return DEFAULT_DOMAIN_ID

        # Fish the domain_id out of the token
        #
        # We could make this more efficient by loading the domain_id
        # into the context in the wrapper function above (since
        # this version of normalize_domain will only be called inside
        # a v3 protected call).  However, this optimization is probably not
        # worth the duplication of state
        try:
            token_ref = self.token_api.get_token(
                token_id=context['token_id'])
        except exception.TokenNotFound:
            LOG.warning(_('Invalid token in _get_domain_id_for_request'))
            raise exception.Unauthorized()

        if 'domain' in token_ref:
            return token_ref['domain']['id']
        else:
            return DEFAULT_DOMAIN_ID
Ejemplo n.º 11
0
    def authenticate(self, context, auth_info, auth_context):
        """Authenticate user."""

        # The 'external' method allows any 'REMOTE_USER' based authentication
        if 'REMOTE_USER' in context['environment']:
            try:
                external = get_auth_method('external')
                external.authenticate(context, auth_info, auth_context)
            except exception.AuthMethodNotSupported:
                # This will happen there is no 'external' plugin registered
                # and the container is performing authentication.
                # The 'kerberos'  and 'saml' methods will be used this way.
                # In those cases, it is correct to not register an
                # 'external' plugin;  if there is both an 'external' and a
                # 'kerberos' plugin, it would run the check on identity twice.
                pass

        # need to aggregate the results in case two or more methods
        # are specified
        auth_response = {'methods': []}
        for method_name in auth_info.get_method_names():
            method = get_auth_method(method_name)
            resp = method.authenticate(context,
                                       auth_info.get_method_data(method_name),
                                       auth_context)
            if resp:
                auth_response['methods'].append(method_name)
                auth_response[method_name] = resp

        if auth_response["methods"]:
            # authentication continuation required
            raise exception.AdditionalAuthRequired(auth_response)

        if 'user_id' not in auth_context:
            msg = _('User not found')
            raise exception.Unauthorized(msg)
Ejemplo n.º 12
0
    def _build_auth_context(self, request):
        token_id = request.headers.get(AUTH_TOKEN_HEADER)

        if token_id == CONF.admin_token:
            # NOTE(gyee): no need to proceed any further as the special admin
            # token is being handled by AdminTokenAuthMiddleware. This code
            # will not be impacted even if AdminTokenAuthMiddleware is removed
            # from the pipeline as "is_admin" is default to "False". This code
            # is independent of AdminTokenAuthMiddleware.
            return {}

        context = {'token_id': token_id}
        context['environment'] = request.environ

        try:
            token_ref = self.token_api.get_token(token_id)
            # TODO(gyee): validate_token_bind should really be its own
            # middleware
            wsgi.validate_token_bind(context, token_ref)
            return authorization.token_to_auth_context(
                token_ref['token_data'])
        except exception.TokenNotFound:
            LOG.warning(_('RBAC: Invalid token'))
            raise exception.Unauthorized()
Ejemplo n.º 13
0
    def authenticate_for_token(self, context, auth=None):
        """Authenticate user and issue a token."""
        include_catalog = 'nocatalog' not in context['query_string']

        try:
            auth_info = AuthInfo.create(context, auth=auth)
            auth_context = AuthContext(extras={}, method_names=[], bind={})
            self.authenticate(context, auth_info, auth_context)
            if auth_context.get('access_token_id'):
                auth_info.set_scope(None, auth_context['project_id'], None)
            self._check_and_set_default_scoping(auth_info, auth_context)
            (domain_id, project_id, trust) = auth_info.get_scope()

            method_names = auth_info.get_method_names()
            method_names += auth_context.get('method_names', [])
            # make sure the list is unique
            method_names = list(set(method_names))
            expires_at = auth_context.get('expires_at')
            # NOTE(morganfainberg): define this here so it is clear what the
            # argument is during the issue_v3_token provider call.
            metadata_ref = None

            (token_id, token_data) = self.token_provider_api.issue_v3_token(
                auth_context['user_id'], method_names, expires_at, project_id,
                domain_id, auth_context, trust, metadata_ref, include_catalog)

            # NOTE(wanghong): We consume a trust use only when we are using
            # trusts and have successfully issued a token.
            if trust:
                self.trust_api.consume_use(trust['id'])

            return render_token_data_response(token_id,
                                              token_data,
                                              created=True)
        except exception.TrustNotFound as e:
            raise exception.Unauthorized(e)