Ejemplo n.º 1
0
    def _validate_token(self, auth_ref, allow_expired=False):
        """Perform the validation steps on the token.

        :param auth_ref: The token data
        :type auth_ref: keystoneauth1.access.AccessInfo

        :raises exc.InvalidToken: if token is rejected
        """
        # 0 seconds of validity means it is invalid right now
        if (not allow_expired) and auth_ref.will_expire_soon(stale_duration=0):
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 2
0
    def fetch_token(self, token, allow_expired=False):
        """Retrieve a token from either a PKI bundle or the identity server.

        :param str token: token id

        :raises exc.InvalidToken: if token is rejected
        """
        data = None
        token_hashes = None

        try:
            token_hashes = self._token_hashes(token)
            cached = self._cache_get_hashes(token_hashes)

            if cached:
                if cached == _CACHE_INVALID_INDICATOR:
                    self.log.debug('Cached token is marked unauthorized')
                    raise ksm_exceptions.InvalidToken()

                if self._check_revocations_for_cached:
                    # A token might have been revoked, regardless of initial
                    # mechanism used to validate it, and needs to be checked.
                    self._revocations.check(token_hashes)

                # NOTE(jamielennox): Cached values used to be stored as a tuple
                # of data and expiry time. They no longer are but we have to
                # allow some time to transition the old format so if it's a
                # tuple just use the data.
                if len(cached) == 2:
                    cached = cached[0]

                data = cached
            else:
                data = self._validate_offline(token, token_hashes)
                if not data:
                    data = self._identity_server.verify_token(
                        token, allow_expired=allow_expired)

                self._token_cache.set(token_hashes[0], data)

        except (ksa_exceptions.ConnectFailure, ksa_exceptions.RequestTimeout,
                ksm_exceptions.RevocationListError,
                ksm_exceptions.ServiceError) as e:
            self.log.critical('Unable to validate token: %s', e)
            raise webob.exc.HTTPServiceUnavailable()
        except ksm_exceptions.InvalidToken:
            self.log.debug('Token validation failure.', exc_info=True)
            if token_hashes:
                self._token_cache.set(token_hashes[0],
                                      _CACHE_INVALID_INDICATOR)
            self.log.warning('Authorization failed for token')
            raise

        return data
Ejemplo n.º 3
0
    def _validate_token(self, auth_ref):
        """Perform the validation steps on the token.

        :param auth_ref: The token data
        :type auth_ref: keystoneclient.access.AccessInfo

        :raises exc.InvalidToken: if token is rejected
        """
        # 0 seconds of validity means is it valid right now.
        if auth_ref.will_expire_soon(stale_duration=0):
            raise exc.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 4
0
    def verify_token(self, token, allow_expired=False):
        auth_ref = self._client.tokens.validate(
            token,
            include_catalog=self._include_service_catalog,
            allow_expired=allow_expired)

        if not auth_ref:
            msg = _('Failed to fetch token data from identity server')
            raise ksm_exceptions.InvalidToken(msg)

        return {'token': auth_ref}
Ejemplo n.º 5
0
    def verify_token(self, user_token, retry=True):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :returns: token object received from identity server on success
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        user_token = _utils.safe_quote(user_token)

        try:
            response, data = self._request_strategy.verify_token(user_token)
        except exceptions.NotFound as e:
            self._LOG.warning(_LW('Authorization failed for token'))
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            raise exc.InvalidToken(_('Token authorization failed'))
        except exceptions.Unauthorized as e:
            self._LOG.info(_LI('Identity server rejected authorization'))
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            if retry:
                self._LOG.info(_LI('Retrying validation'))
                return self.verify_token(user_token, False)
            msg = _('Identity server rejected authorization necessary to '
                    'fetch token data')
            raise exc.ServiceError(msg)
        except exceptions.HttpError as e:
            self._LOG.error(
                _LE('Bad response code while validating token: %s'),
                e.http_status)
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            msg = _('Failed to fetch token data from identity server')
            raise exc.ServiceError(msg)
        else:
            if response.status_code == 200:
                return data

            raise exc.InvalidToken()
Ejemplo n.º 6
0
    def _do_fetch_token(self, token, **kwargs):
        """Helper method to fetch a token and convert it into an AccessInfo."""
        # NOTE(edmondsw): strip the token to remove any whitespace that may
        # have been passed along in the header per bug 1689468
        token = token.strip()
        data = self.fetch_token(token, **kwargs)

        try:
            return data, access.create(body=data, auth_token=token)
        except Exception:
            self.log.warning('Invalid token contents.', exc_info=True)
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 7
0
 def verify():
     try:
         signing_cert_path = self._signing_directory.calc_path(
             self._SIGNING_CERT_FILE_NAME)
         signing_ca_path = self._signing_directory.calc_path(
             self._SIGNING_CA_FILE_NAME)
         return cms.cms_verify(data,
                               signing_cert_path,
                               signing_ca_path,
                               inform=inform).decode('utf-8')
     except (exceptions.CMSError,
             cms.subprocess.CalledProcessError) as err:
         self.log.warning(_LW('Verify error: %s'), err)
         raise exc.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 8
0
    def _get_user_token_from_header(self, env):
        """Get token id from request.

        :param env: wsgi request environment
        :returns: token id
        :raises exc.InvalidToken: if no token is provided in request

        """
        token = self._get_header(env, 'X-Auth-Token',
                                 self._get_header(env, 'X-Storage-Token'))
        if token:
            return token
        else:
            if not self._delay_auth_decision:
                self._LOG.debug('Headers: %s', env)
            raise exc.InvalidToken(_('Unable to find token in headers'))
Ejemplo n.º 9
0
    def _do_fetch_token(self, token, **kwargs):
        """Helper method to fetch a token and convert it into an AccessInfo."""
        if self.kwargs_to_fetch_token:
            data = self.fetch_token(token, **kwargs)
        else:
            m = _('Implementations of auth_token must set '
                  'kwargs_to_fetch_token this will be the required and '
                  'assumed in Pike.')
            warnings.warn(m)
            data = self.fetch_token(token)

        try:
            return data, access.create(body=data, auth_token=token)
        except Exception:
            self.log.warning(_LW('Invalid token contents.'), exc_info=True)
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 10
0
    def _do_fetch_token(self, token, **kwargs):
        """Helper method to fetch a token and convert it into an AccessInfo."""
        # NOTE(edmondsw): strip the token to remove any whitespace that may
        # have been passed along in the header per bug 1689468
        token = token.strip()
        if self.kwargs_to_fetch_token:
            data = self.fetch_token(token, **kwargs)
        else:
            m = _('Implementations of auth_token must set '
                  'kwargs_to_fetch_token this will be the required and '
                  'assumed in Queens.')
            warnings.warn(m)
            data = self.fetch_token(token)

        try:
            return data, access.create(body=data, auth_token=token)
        except Exception:
            self.log.warning('Invalid token contents.', exc_info=True)
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 11
0
    def get(self, token_id):
        """Return token information from cache.

        If token is invalid raise exc.InvalidToken
        return token only if fresh (not expired).
        """

        if not token_id:
            # Nothing to do
            return

        key, context = self._get_cache_key(token_id)

        with self._cache_pool.reserve() as cache:
            serialized = cache.get(key)

        if serialized is None:
            return None

        if isinstance(serialized, six.text_type):
            serialized = serialized.encode('utf8')
        data = self._deserialize(serialized, context)

        # Note that _INVALID_INDICATOR and (data, expires) are the only
        # valid types of serialized cache entries, so there is not
        # a collision with jsonutils.loads(serialized) == None.
        if not isinstance(data, six.string_types):
            data = data.decode('utf-8')
        cached = jsonutils.loads(data)
        if cached == self._INVALID_INDICATOR:
            self._LOG.debug('Cached Token is marked unauthorized')
            raise exc.InvalidToken(_('Token authorization failed'))

        # NOTE(jamielennox): Cached values used to be stored as a tuple of data
        # and expiry time. They no longer are but we have to allow some time to
        # transition the old format so if it's a tuple just return the data.
        try:
            data, expires = cached
        except ValueError:
            data = cached

        return data
Ejemplo n.º 12
0
    def verify_token(self, user_token, retry=True, allow_expired=False):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :param allow_expired: Allow retrieving an expired token.
        :returns: access info received from identity server on success
        :rtype: :py:class:`keystoneauth1.access.AccessInfo`
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        try:
            auth_ref = self._request_strategy.verify_token(
                user_token,
                allow_expired=allow_expired)
        except ksa_exceptions.NotFound as e:
            self._LOG.info('Authorization failed for token')
            self._LOG.info('Identity response: %s', e.response.text)
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
        except ksa_exceptions.Unauthorized as e:
            self._LOG.info('Identity server rejected authorization')
            self._LOG.warning('Identity response: %s', e.response.text)
            if retry:
                self._LOG.info('Retrying validation')
                return self.verify_token(user_token, False)
            msg = _('Identity server rejected authorization necessary to '
                    'fetch token data')
            raise ksm_exceptions.ServiceError(msg)
        except ksa_exceptions.HttpError as e:
            self._LOG.error(
                'Bad response code while validating token: %s %s',
                e.http_status, e.message)
            if hasattr(e.response, 'text'):
                self._LOG.warning('Identity response: %s', e.response.text)
            msg = _('Failed to fetch token data from identity server')
            raise ksm_exceptions.ServiceError(msg)
        else:
            return auth_ref
Ejemplo n.º 13
0
    def check_by_audit_id(self, audit_ids):
        """Check whether the audit_id appears in the revocation list.

        :raises keystonemiddleware.auth_token._exceptions.InvalidToken:
            if the audit ID(s) appear in the revocation list.

        """
        revoked_tokens = self._list.get('revoked', None)
        if not revoked_tokens:
            # There's no revoked tokens, so nothing to do.
            return

        # The audit_id may not be present in the revocation events because
        # earlier versions of the identity server didn't provide them.
        revoked_ids = set(
            x['audit_id'] for x in revoked_tokens if 'audit_id' in x)
        for audit_id in audit_ids:
            if audit_id in revoked_ids:
                self._log.debug(
                    'Token is marked as having been revoked by audit id')
                raise exc.InvalidToken(_('Token has been revoked'))
Ejemplo n.º 14
0
    def _build_user_headers(self, auth_ref, token_info):
        """Convert token object into headers.

        Build headers that represent authenticated user - see main
        doc info at start of file for details of headers to be defined.

        :param token_info: token object returned by identity
                           server on authentication
        :raises exc.InvalidToken: when unable to parse token object

        """
        roles = ','.join(auth_ref.role_names)

        if _token_is_v2(token_info) and not auth_ref.project_id:
            raise exc.InvalidToken(_('Unable to determine tenancy.'))

        rval = {
            'X-Identity-Status': 'Confirmed',
            'X-Roles': roles,
        }

        for header_tmplt, attr in six.iteritems(_HEADER_TEMPLATE):
            rval[header_tmplt % ''] = getattr(auth_ref, attr)

        # Deprecated headers
        rval['X-Role'] = roles
        for header_tmplt, attr in six.iteritems(_DEPRECATED_HEADER_TEMPLATE):
            rval[header_tmplt] = getattr(auth_ref, attr)

        if self._include_service_catalog and auth_ref.has_service_catalog():
            catalog = auth_ref.service_catalog.get_data()
            if _token_is_v3(token_info):
                catalog = _v3_to_v2_catalog(catalog)
            rval['X-Service-Catalog'] = jsonutils.dumps(catalog)

        return rval
Ejemplo n.º 15
0
    def _validate_token(self, auth_ref):
        super(AuthProtocol, self)._validate_token(auth_ref)

        if auth_ref.version == 'v2.0' and not auth_ref.project_id:
            msg = _('Unable to determine service tenancy.')
            raise exc.InvalidToken(msg)
Ejemplo n.º 16
0
    def _invalid_user_token(self, msg=False):
        # NOTE(jamielennox): use False as the default so that None is valid
        if msg is False:
            msg = _('Token authorization failed')

        raise exc.InvalidToken(msg)
Ejemplo n.º 17
0
    def fetch_token(self, token, allow_expired=False):
        """Retrieve a token from either a PKI bundle or the identity server.

        :param str token: token id

        :raises exc.InvalidToken: if token is rejected
        """
        data = None
        token_hashes = None

        try:
            token_hashes = self._token_hashes(token)
            cached = self._cache_get_hashes(token_hashes)

            if cached:
                if cached == _CACHE_INVALID_INDICATOR:
                    self.log.debug('Cached token is marked unauthorized')
                    raise ksm_exceptions.InvalidToken()

                # NOTE(jamielennox): Cached values used to be stored as a tuple
                # of data and expiry time. They no longer are but we have to
                # allow some time to transition the old format so if it's a
                # tuple just use the data.
                if len(cached) == 2:
                    cached = cached[0]

                data = cached
            else:
                data = self._validate_offline(token, token_hashes)
                if not data:
                    data = self._identity_server.verify_token(
                        token,
                        allow_expired=allow_expired)

                self._token_cache.set(token_hashes[0], data)

        except (ksa_exceptions.ConnectFailure,
                ksa_exceptions.DiscoveryFailure,
                ksa_exceptions.RequestTimeout,
                ksm_exceptions.ServiceError) as e:
            self.log.critical('Unable to validate token: %s', e)
            if self._delay_auth_decision:
                self.log.debug('Keystone unavailable; marking token as '
                               'invalid and deferring auth decision.')
                raise ksm_exceptions.InvalidToken(
                    'Keystone unavailable: %s' % e)
            raise webob.exc.HTTPServiceUnavailable(
                'The Keystone service is temporarily unavailable.')
        except ksm_exceptions.InvalidToken:
            self.log.debug('Token validation failure.', exc_info=True)
            if token_hashes:
                self._token_cache.set(token_hashes[0],
                                      _CACHE_INVALID_INDICATOR)
            self.log.warning('Authorization failed for token')
            raise
        except ksa_exceptions.EndpointNotFound:
            # Invalidate auth in adapter for identity endpoint update
            self._identity_server.invalidate()
            raise

        return data
Ejemplo n.º 18
0
 def _assert_token_not_expired(self, token_expires):
     if timeutils.utcnow() > timeutils.normalize_time(token_expires):
         raise _exceptions.InvalidToken()
Ejemplo n.º 19
0
    def _validate_token(self, token, env):
        """Authenticate user token

        :param token: token id
        :param env: wsgi environment
        :returns: uncrypted body of the token if the token is valid
        :raises exc.InvalidToken: if token is rejected

        """
        token_id = None

        try:
            token_ids, cached = self._token_cache.get(token)
            token_id = token_ids[0]
            if cached:
                # Token was retrieved from the cache. In this case, there's no
                # need to check that the token is expired because the cache
                # fetch fails for an expired token. Also, there's no need to
                # put the token in the cache because it's already in the cache.

                data = cached

                if self._check_revocations_for_cached:
                    # A token stored in Memcached might have been revoked
                    # regardless of initial mechanism used to validate it,
                    # and needs to be checked.
                    self._revocations.check(token_ids)
                self._confirm_token_bind(data, env)
            else:
                verified = None
                # Token wasn't cached. In this case, the token needs to be
                # checked that it's not expired, and also put in the cache.
                try:
                    if cms.is_pkiz(token):
                        verified = self._verify_pkiz_token(token, token_ids)
                    elif cms.is_asn1_token(token):
                        verified = self._verify_signed_token(token, token_ids)
                except exceptions.CertificateConfigError:
                    self._LOG.warn(_LW('Fetch certificate config failed, '
                                       'fallback to online validation.'))
                except exc.RevocationListError:
                    self._LOG.warn(_LW('Fetch revocation list failed, '
                                       'fallback to online validation.'))

                if verified is not None:
                    data = jsonutils.loads(verified)
                    expires = _get_token_expiration(data)
                    _confirm_token_not_expired(expires)
                else:
                    data = self._identity_server.verify_token(token)
                    # No need to confirm token expiration here since
                    # verify_token fails for expired tokens.
                    expires = _get_token_expiration(data)
                self._confirm_token_bind(data, env)
                self._token_cache.store(token_id, data, expires)
            return data
        except (exceptions.ConnectionRefused, exceptions.RequestTimeout):
            self._LOG.debug('Token validation failure.', exc_info=True)
            self._LOG.warn(_LW('Authorization failed for token'))
            raise exc.InvalidToken(_('Token authorization failed'))
        except exc.ServiceError:
            raise
        except Exception:
            self._LOG.debug('Token validation failure.', exc_info=True)
            if token_id:
                self._token_cache.store_invalid(token_id)
            self._LOG.warn(_LW('Authorization failed for token'))
            raise exc.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 20
0
def _confirm_token_not_expired(expires):
    expires = timeutils.parse_isotime(expires)
    expires = timeutils.normalize_time(expires)
    utcnow = timeutils.utcnow()
    if utcnow >= expires:
        raise exc.InvalidToken(_('Token authorization failed'))
Ejemplo n.º 21
0
 def check(self, token_ids):
     if self._any_revoked(token_ids):
         self._log.debug('Token is marked as having been revoked')
         raise exc.InvalidToken(_('Token has been revoked'))