Esempio n. 1
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: 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)
        except ksa_exceptions.NotFound as e:
            self._LOG.warning(_LW("Authorization failed for token"))
            self._LOG.warning(_LW("Identity response: %s"), e.response.text)
            raise ksm_exceptions.InvalidToken(_("Token authorization failed"))
        except ksa_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 ksm_exceptions.ServiceError(msg)
        except ksa_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 ksm_exceptions.ServiceError(msg)
        else:
            return auth_ref
def _get_token_expiration(data):
    if not data:
        raise exc.InvalidToken(_('Token authorization failed'))
    if _token_is_v2(data):
        return data['access']['token']['expires']
    elif _token_is_v3(data):
        return data['token']['expires_at']
    else:
        raise exc.InvalidToken(_('Token authorization failed'))
Esempio n. 3
0
def _get_token_expiration(data):
    if not data:
        raise exc.InvalidToken(_('Token authorization failed'))
    if _token_is_v2(data):
        return data['access']['token']['expires']
    elif _token_is_v3(data):
        return data['token']['expires_at']
    else:
        raise exc.InvalidToken(_('Token authorization failed'))
Esempio n. 4
0
 def fetch_revocation_list(self):
     try:
         data = self._request_strategy.fetch_revocation_list()
     except ksa_exceptions.HttpError as e:
         msg = _("Failed to fetch token revocation list: %d")
         raise ksm_exceptions.RevocationListError(msg % e.http_status)
     if "signed" not in data:
         msg = _("Revocation list improperly formatted.")
         raise ksm_exceptions.RevocationListError(msg)
     return data["signed"]
Esempio n. 5
0
 def fetch_revocation_list(self):
     try:
         data = self._request_strategy.fetch_revocation_list()
     except ksc_exceptions.HTTPError as e:
         msg = _('Failed to fetch token revocation list: %d')
         raise ksm_exceptions.RevocationListError(msg % e.http_status)
     if 'signed' not in data:
         msg = _('Revocation list improperly formatted.')
         raise ksm_exceptions.RevocationListError(msg)
     return data['signed']
Esempio n. 6
0
 def fetch_revocation_list(self):
     try:
         data = self._request_strategy.fetch_revocation_list()
     except ksa_exceptions.HttpError as e:
         msg = _('Failed to fetch token revocation list: %d')
         raise ksm_exceptions.RevocationListError(msg % e.http_status)
     if 'signed' not in data:
         msg = _('Revocation list improperly formatted.')
         raise ksm_exceptions.RevocationListError(msg)
     return data['signed']
Esempio n. 7
0
    def _cache_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'))

        data, expires = cached

        try:
            expires = timeutils.parse_isotime(expires)
        except ValueError:
            # Gracefully handle upgrade of expiration times from *nix
            # timestamps to ISO 8601 formatted dates by ignoring old cached
            # values.
            return

        expires = timeutils.normalize_time(expires)
        utcnow = timeutils.utcnow()
        if utcnow < expires:
            self._LOG.debug('Returning cached token')
            return data
        else:
            self._LOG.debug('Cached Token seems expired')
            raise exc.InvalidToken(_('Token authorization failed'))
Esempio n. 8
0
    def _cache_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'))

        data, expires = cached

        try:
            expires = timeutils.parse_isotime(expires)
        except ValueError:
            # Gracefully handle upgrade of expiration times from *nix
            # timestamps to ISO 8601 formatted dates by ignoring old cached
            # values.
            return

        expires = timeutils.normalize_time(expires)
        utcnow = timeutils.utcnow()
        if utcnow < expires:
            self._LOG.debug('Returning cached token')
            return data
        else:
            self._LOG.debug('Cached Token seems expired')
            raise exc.InvalidToken(_('Token authorization failed'))
Esempio n. 9
0
def _split_path(path, minsegs=1, maxsegs=None, rest_with_last=False):
    """Validate and split the given HTTP request path.

    **Examples**::

        ['a'] = _split_path('/a')
        ['a', None] = _split_path('/a', 1, 2)
        ['a', 'c'] = _split_path('/a/c', 1, 2)
        ['a', 'c', 'o/r'] = _split_path('/a/c/o/r', 1, 3, True)

    :param path: HTTP Request path to be split
    :param minsegs: Minimum number of segments to be extracted
    :param maxsegs: Maximum number of segments to be extracted
    :param rest_with_last: If True, trailing data will be returned as part
                           of last segment.  If False, and there is
                           trailing data, raises ValueError.
    :returns: list of segments with a length of maxsegs (non-existent
              segments will return as None)
    :raises: ValueError if given an invalid path
    """
    if not maxsegs:
        maxsegs = minsegs
    if minsegs > maxsegs:
        raise ValueError(
            _('minsegs > maxsegs: %(min)d > %(max)d)') % {
                'min': minsegs,
                'max': maxsegs
            })
    if rest_with_last:
        segs = path.split('/', maxsegs)
        minsegs += 1
        maxsegs += 1
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs
                or '' in segs[1:minsegs]):
            raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path))
    else:
        minsegs += 1
        maxsegs += 1
        segs = path.split('/', maxsegs)
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs + 1
                or '' in segs[1:minsegs]
                or (count == maxsegs + 1 and segs[maxsegs])):
            raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path))
    segs = segs[1:maxsegs]
    segs.extend([None] * (maxsegs - 1 - len(segs)))
    return segs
Esempio n. 10
0
    def _build_service_headers(self, token_info):
        """Convert token object into service 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

        """
        auth_ref = access.AccessInfo.factory(body=token_info)

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

        roles = ','.join(auth_ref.role_names)
        rval = {
            'X-Service-Identity-Status': 'Confirmed',
            'X-Service-Roles': roles,
        }

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

        return rval
Esempio n. 11
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'))
Esempio n. 12
0
def _conf_values_type_convert(conf):
    """Convert conf values into correct type."""
    if not conf:
        return {}

    opt_types = {}
    for o in (_OPTS + _auth.AuthTokenPlugin.get_options()):
        type_dest = (getattr(o, 'type', str), o.dest)
        opt_types[o.dest] = type_dest
        # Also add the deprecated name with the same type and dest.
        for d_o in o.deprecated_opts:
            opt_types[d_o.name] = type_dest

    opts = {}
    for k, v in six.iteritems(conf):
        dest = k
        try:
            if v is not None:
                type_, dest = opt_types[k]
                v = type_(v)
        except KeyError:
            # This option is not known to auth_token.
            pass
        except ValueError as e:
            raise exc.ConfigurationError(
                _('Unable to convert the value of %(key)s option into correct '
                  'type: %(ex)s') % {
                      'key': k,
                      'ex': e
                  })
        opts[dest] = v
    return opts
Esempio n. 13
0
def _conf_values_type_convert(conf):
    """Convert conf values into correct type."""
    if not conf:
        return {}

    opt_types = {}
    for o in (_OPTS + _auth.AuthTokenPlugin.get_options()):
        type_dest = (getattr(o, 'type', str), o.dest)
        opt_types[o.dest] = type_dest
        # Also add the deprecated name with the same type and dest.
        for d_o in o.deprecated_opts:
            opt_types[d_o.name] = type_dest

    opts = {}
    for k, v in six.iteritems(conf):
        dest = k
        try:
            if v is not None:
                type_, dest = opt_types[k]
                v = type_(v)
        except KeyError:
            # This option is not known to auth_token.
            pass
        except ValueError as e:
            raise exc.ConfigurationError(
                _('Unable to convert the value of %(key)s option into correct '
                  'type: %(ex)s') % {'key': k, 'ex': e})
        opts[dest] = v
    return opts
Esempio n. 14
0
    def _build_service_headers(self, token_info):
        """Convert token object into service 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

        """
        auth_ref = access.AccessInfo.factory(body=token_info)

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

        roles = ','.join(auth_ref.role_names)
        rval = {
            'X-Service-Identity-Status': 'Confirmed',
            'X-Service-Roles': roles,
        }

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

        return rval
def unprotect_data(keys, signed_data):
    """Given keys and cached string data, verifies the signature,
    decrypts if necessary, and returns the original serialized data.

    """
    # cache backends return None when no data is found. We don't mind
    # that this particular special value is unsigned.
    if signed_data is None:
        return None

    # First we calculate the signature
    provided_mac = signed_data[:DIGEST_LENGTH_B64]
    calculated_mac = sign_data(
        keys['MAC'],
        signed_data[DIGEST_LENGTH_B64:])

    # Then verify that it matches the provided value
    if not constant_time_compare(provided_mac, calculated_mac):
        raise InvalidMacError(_('Invalid MAC; data appears to be corrupted.'))

    data = base64.b64decode(signed_data[DIGEST_LENGTH_B64:])

    # then if necessary decrypt the data
    if keys['strategy'] == b'ENCRYPT':
        data = decrypt_data(keys['ENCRYPTION'], data)

    return data
Esempio n. 16
0
def unprotect_data(keys, signed_data):
    """De-serialize data given a dict of keys.

    Given keys and cached string data, verifies the signature, decrypts if
    necessary, and returns the original serialized data.

    """
    # cache backends return None when no data is found. We don't mind
    # that this particular special value is unsigned.
    if signed_data is None:
        return None

    # First we calculate the signature
    provided_mac = signed_data[:DIGEST_LENGTH_B64]
    calculated_mac = sign_data(keys['MAC'], signed_data[DIGEST_LENGTH_B64:])

    # Then verify that it matches the provided value
    if not constant_time_compare(provided_mac, calculated_mac):
        raise InvalidMacError(_('Invalid MAC; data appears to be corrupted.'))

    data = base64.b64decode(signed_data[DIGEST_LENGTH_B64:])

    # then if necessary decrypt the data
    if keys['strategy'] == b'ENCRYPT':
        data = decrypt_data(keys['ENCRYPTION'], data)

    return data
Esempio n. 17
0
 def fetch_revocation_list(self):
     try:
         response, data = self._json_request(
             'GET', '/tokens/revoked',
             authenticated=True,
             endpoint_filter={'version': (2, 0)})
     except exceptions.HTTPError as e:
         msg = _('Failed to fetch token revocation list: %d')
         raise exc.RevocationListError(msg % e.http_status)
     if response.status_code != 200:
         msg = _('Unable to fetch token revocation list.')
         raise exc.RevocationListError(msg)
     if 'signed' not in data:
         msg = _('Revocation list improperly formatted.')
         raise exc.RevocationListError(msg)
     return data['signed']
Esempio n. 18
0
    def verify_token(self, token):
        auth_ref = self._client.tokens.validate_access_info(token)

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

        return {'access': auth_ref}
Esempio n. 19
0
    def verify_token(self, token):
        auth_ref = self._client.tokens.validate_access_info(token)

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

        return {'access': auth_ref}
 def validate_allowed_request(self, request, token):
     self.log.debug("Validating token access rules against request")
     app_cred = token.get('application_credential')
     if not app_cred:
         return
     access_rules = app_cred.get('access_rules')
     if access_rules is None:
         return
     if hasattr(self, '_conf'):
         my_service_type = self._conf.get('service_type')
     else:
         my_service_type = self._service_type
     if not my_service_type:
         self.log.warning('Cannot validate request with restricted'
                          ' access rules. Set service_type in'
                          ' [keystone_authtoken] to allow access rule'
                          ' validation.')
         raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
     # token can always be validated regardless of access rules
     if (my_service_type == 'identity' and
             request.method == 'GET' and
             request.path.endswith('/v3/auth/tokens')):
         return
     catalog = token['catalog']
     # validate service type is in catalog
     catalog_svcs = [s for s in catalog if s['type'] == my_service_type]
     if len(catalog_svcs) == 0:
         self.log.warning('Cannot validate request with restricted'
                          ' access rules. service_type in'
                          ' [keystone_authtoken] is not a valid service'
                          ' type in the catalog.')
         raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
     if request.service_token:
         # The request may not match an allowed request, but the presence
         # of the service token indicates this is a chain of requests and
         # hence this request was not user-facing
         return
     for access_rule in access_rules:
         method = access_rule['method']
         path = access_rule['path']
         service = access_rule['service']
         if request.method == method and \
                 service == my_service_type and \
                 _path_matches(request.path, path):
             return
     raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
 def fetch_revocation_list(self):
     try:
         response, data = self._json_request(
             'GET',
             '/tokens/revoked',
             authenticated=True,
             endpoint_filter={'version': (2, 0)})
     except exceptions.HTTPError as e:
         msg = _('Failed to fetch token revocation list: %d')
         raise exc.RevocationListError(msg % e.http_status)
     if response.status_code != 200:
         msg = _('Unable to fetch token revocation list.')
         raise exc.RevocationListError(msg)
     if 'signed' not in data:
         msg = _('Revocation list improperly formatted.')
         raise exc.RevocationListError(msg)
     return data['signed']
Esempio n. 22
0
    def verify_token(self, token):
        auth_ref = self._client.tokens.validate(token, include_catalog=self._include_service_catalog)

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

        return {"token": auth_ref}
Esempio n. 23
0
def _split_path(path, minsegs=1, maxsegs=None, rest_with_last=False):
    """Validate and split the given HTTP request path.

    **Examples**::

        ['a'] = _split_path('/a')
        ['a', None] = _split_path('/a', 1, 2)
        ['a', 'c'] = _split_path('/a/c', 1, 2)
        ['a', 'c', 'o/r'] = _split_path('/a/c/o/r', 1, 3, True)

    :param path: HTTP Request path to be split
    :param minsegs: Minimum number of segments to be extracted
    :param maxsegs: Maximum number of segments to be extracted
    :param rest_with_last: If True, trailing data will be returned as part
                           of last segment.  If False, and there is
                           trailing data, raises ValueError.
    :returns: list of segments with a length of maxsegs (non-existent
              segments will return as None)
    :raises: ValueError if given an invalid path
    """
    if not maxsegs:
        maxsegs = minsegs
    if minsegs > maxsegs:
        raise ValueError(_('minsegs > maxsegs: %(min)d > %(max)d)') %
                         {'min': minsegs, 'max': maxsegs})
    if rest_with_last:
        segs = path.split('/', maxsegs)
        minsegs += 1
        maxsegs += 1
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs or
                '' in segs[1:minsegs]):
            raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path))
    else:
        minsegs += 1
        maxsegs += 1
        segs = path.split('/', maxsegs)
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs + 1 or
                '' in segs[1:minsegs] or
                (count == maxsegs + 1 and segs[maxsegs])):
            raise ValueError(_('Invalid path: %s') % urllib.parse.quote(path))
    segs = segs[1:maxsegs]
    segs.extend([None] * (maxsegs - 1 - len(segs)))
    return segs
Esempio n. 24
0
    def verify_token(self, token, allow_expired=False):
        # NOTE(jamielennox): allow_expired is ignored on V2
        auth_ref = self._client.tokens.validate_access_info(token)

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

        return {'access': auth_ref}
Esempio n. 25
0
    def _do_fetch_token(self, token):
        """Helper method to fetch a token and convert it into an AccessInfo"""
        data = self._fetch_token(token)

        try:
            return data, access.AccessInfo.factory(body=data, auth_token=token)
        except Exception:
            self.log.warning(_LW("Invalid token contents."), exc_info=True)
            raise exc.InvalidToken(_("Token authorization failed"))
Esempio n. 26
0
    def verify_token(self, token, allow_expired=False):
        # NOTE(jamielennox): allow_expired is ignored on V2
        auth_ref = self._client.tokens.validate_access_info(token)

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

        return {'access': auth_ref}
Esempio n. 27
0
    def verify_token(self, token):
        auth_ref = self._client.tokens.validate(
            token, include_catalog=self._include_service_catalog)

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

        return {'token': auth_ref}
Esempio n. 28
0
    def _do_fetch_token(self, token):
        """Helper method to fetch a token and convert it into an AccessInfo"""
        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'))
Esempio n. 29
0
    def _do_fetch_token(self, token):
        """Helper method to fetch a token and convert it into an AccessInfo"""
        data = self._fetch_token(token)

        try:
            return data, access.AccessInfo.factory(body=data, auth_token=token)
        except Exception:
            self.log.warning(_LW('Invalid token contents.'), exc_info=True)
            raise exc.InvalidToken(_('Token authorization failed'))
Esempio n. 30
0
    def _fetch_token(self, token):
        """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:
                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_hashes)
            else:
                data = self._validate_offline(token, token_hashes)
                if not data:
                    data = self._identity_server.verify_token(token)

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

        except (exceptions.ConnectionRefused, exceptions.RequestTimeout):
            self.log.debug("Token validation failure.", exc_info=True)
            self.log.warning(_LW("Authorization failed for token"))
            raise exc.InvalidToken(_("Token authorization failed"))
        except exc.ServiceError as e:
            self.log.critical(_LC("Unable to obtain admin token: %s"), e)
            raise webob.exc.HTTPServiceUnavailable()
        except Exception:
            self.log.debug("Token validation failure.", exc_info=True)
            if token_hashes:
                self._token_cache.store_invalid(token_hashes[0])
            self.log.warning(_LW("Authorization failed for token"))
            raise exc.InvalidToken(_("Token authorization failed"))

        return data
Esempio n. 31
0
    def _fetch_token(self, token):
        """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:
                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_hashes)
            else:
                data = self._validate_offline(token, token_hashes)
                if not data:
                    data = self._identity_server.verify_token(token)

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

        except (exceptions.ConnectionRefused, exceptions.RequestTimeout):
            self.log.debug('Token validation failure.', exc_info=True)
            self.log.warning(_LW('Authorization failed for token'))
            raise exc.InvalidToken(_('Token authorization failed'))
        except exc.ServiceError as e:
            self.log.critical(_LC('Unable to obtain admin token: %s'), e)
            raise webob.exc.HTTPServiceUnavailable()
        except Exception:
            self.log.debug('Token validation failure.', exc_info=True)
            if token_hashes:
                self._token_cache.store_invalid(token_hashes[0])
            self.log.warning(_LW('Authorization failed for token'))
            raise exc.InvalidToken(_('Token authorization failed'))

        return data
Esempio n. 32
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'))
    def verify_token(self, token):
        auth_ref = self._client.tokens.validate(
            token,
            include_catalog=self._include_service_catalog)

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

        return {'token': auth_ref}
Esempio n. 34
0
    def __init__(self, log, security_strategy, secret_key, **kwargs):
        super(SecureTokenCache, self).__init__(log, **kwargs)

        security_strategy = security_strategy.upper()

        if security_strategy not in ('MAC', 'ENCRYPT'):
            msg = _('memcache_security_strategy must be ENCRYPT or MAC')
            raise exc.ConfigurationError(msg)
        if not secret_key:
            msg = _('memcache_secret_key must be defined when a '
                    'memcache_security_strategy is defined')
            raise exc.ConfigurationError(msg)

        if isinstance(security_strategy, six.string_types):
            security_strategy = security_strategy.encode('utf-8')
        if isinstance(secret_key, six.string_types):
            secret_key = secret_key.encode('utf-8')

        self._security_strategy = security_strategy
        self._secret_key = secret_key
Esempio n. 35
0
    def _validate_token(self, auth_ref):
        """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 auth_ref.will_expire_soon(stale_duration=0):
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
Esempio n. 36
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'))
Esempio n. 37
0
    def __init__(self, log, security_strategy, secret_key, **kwargs):
        super(SecureTokenCache, self).__init__(log, **kwargs)

        security_strategy = security_strategy.upper()

        if security_strategy not in ('MAC', 'ENCRYPT'):
            msg = _('memcache_security_strategy must be ENCRYPT or MAC')
            raise exc.ConfigurationError(msg)
        if not secret_key:
            msg = _('memcache_secret_key must be defined when a '
                    'memcache_security_strategy is defined')
            raise exc.ConfigurationError(msg)

        if isinstance(security_strategy, six.string_types):
            security_strategy = security_strategy.encode('utf-8')
        if isinstance(secret_key, six.string_types):
            secret_key = secret_key.encode('utf-8')

        self._security_strategy = security_strategy
        self._secret_key = secret_key
Esempio n. 38
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}
Esempio n. 39
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}
Esempio n. 40
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'))
Esempio n. 41
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()
Esempio n. 42
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()
Esempio n. 43
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
Esempio n. 44
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
Esempio n. 45
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'))
def decrypt_data(key, data):
    """Decrypt the data with the given secret key."""
    iv = data[:16]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    try:
        result = cipher.decrypt(data[16:])
    except Exception:
        raise DecryptError(_('Encrypted data appears to be corrupted.'))

    # Strip the last n padding bytes where n is the last value in
    # the plaintext
    return result[:-1 * six.byte2int([result[-1]])]
Esempio n. 47
0
def decrypt_data(key, data):
    """Decrypt the data with the given secret key."""
    iv = data[:16]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    try:
        result = cipher.decrypt(data[16:])
    except Exception:
        raise DecryptError(_('Encrypted data appears to be corrupted.'))

    # Strip the last n padding bytes where n is the last value in
    # the plaintext
    return result[:-1 * six.byte2int([result[-1]])]
Esempio n. 48
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'))
Esempio n. 49
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'))
Esempio n. 50
0
    def get_endpoint(self, session, interface=None, version=None, **kwargs):
        """Return an endpoint for the client.

        There are no required keyword arguments to ``get_endpoint`` as a plugin
        implementation should use best effort with the information available to
        determine the endpoint.

        :param session: The session object that the auth_plugin belongs to.
        :type session: keystoneauth1.session.Session
        :param version: The version number required for this endpoint.
        :type version: tuple or str
        :param str interface: what visibility the endpoint should have.

        :returns: The base URL that will be used to talk to the required
                  service or None if not available.
        :rtype: string
        """
        if interface == plugin.AUTH_INTERFACE:
            return self._identity_uri

        if not version:
            # NOTE(jamielennox): This plugin can only be used within auth_token
            # and auth_token will always provide version= with requests.
            return None

        if not self._discover:
            self._discover = discover.Discover(session,
                                               url=self._identity_uri,
                                               authenticated=False)

        if not self._discover.url_for(version):
            # NOTE(jamielennox): The requested version is not supported by the
            # identity server.
            return None

        # NOTE(jamielennox): for backwards compatibility here we don't
        # actually use the URL from discovery we hack it up instead. :(
        # NOTE(blk-u): Normalizing the version is a workaround for bug 1450272.
        # This can be removed once that's fixed. Also fix the docstring for the
        # version parameter to be just "tuple".
        version = discover.normalize_version_number(version)
        if discover.version_match((2, 0), version):
            return '%s/v2.0' % self._identity_uri
        elif discover.version_match((3, 0), version):
            return '%s/v3' % self._identity_uri

        # NOTE(jamielennox): This plugin will only get called from auth_token
        # middleware. The middleware should never request a version that the
        # plugin doesn't know how to handle.
        msg = _('Invalid version asked for in auth_token plugin')
        raise NotImplementedError(msg)
Esempio n. 51
0
    def get_endpoint(self, session, interface=None, version=None, **kwargs):
        """Return an endpoint for the client.

        There are no required keyword arguments to ``get_endpoint`` as a plugin
        implementation should use best effort with the information available to
        determine the endpoint.

        :param session: The session object that the auth_plugin belongs to.
        :type session: keystoneauth1.session.Session
        :param version: The version number required for this endpoint.
        :type version: tuple or str
        :param str interface: what visibility the endpoint should have.

        :returns: The base URL that will be used to talk to the required
                  service or None if not available.
        :rtype: string
        """
        if interface == plugin.AUTH_INTERFACE:
            return self._identity_uri

        if not version:
            # NOTE(jamielennox): This plugin can only be used within auth_token
            # and auth_token will always provide version= with requests.
            return None

        if not self._discover:
            self._discover = discover.Discover(session,
                                               url=self._identity_uri,
                                               authenticated=False)

        if not self._discover.url_for(version):
            # NOTE(jamielennox): The requested version is not supported by the
            # identity server.
            return None

        # NOTE(jamielennox): for backwards compatibility here we don't
        # actually use the URL from discovery we hack it up instead. :(
        # NOTE(blk-u): Normalizing the version is a workaround for bug 1450272.
        # This can be removed once that's fixed. Also fix the docstring for the
        # version parameter to be just "tuple".
        version = discover.normalize_version_number(version)
        if discover.version_match((2, 0), version):
            return '%s/v2.0' % self._identity_uri
        elif discover.version_match((3, 0), version):
            return '%s/v3' % self._identity_uri

        # NOTE(jamielennox): This plugin will only get called from auth_token
        # middleware. The middleware should never request a version that the
        # plugin doesn't know how to handle.
        msg = _('Invalid version asked for in auth_token plugin')
        raise NotImplementedError(msg)
Esempio n. 52
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'))
Esempio n. 53
0
 def _verify_signing_dir(self):
     if os.path.isdir(self._directory_name):
         if not os.access(self._directory_name, os.W_OK):
             raise exc.ConfigurationError(
                 _('unable to access signing_dir %s') %
                 self._directory_name)
         uid = os.getuid()
         if os.stat(self._directory_name).st_uid != uid:
             self._log.warning(_LW('signing_dir is not owned by %s'), uid)
         current_mode = stat.S_IMODE(os.stat(self._directory_name).st_mode)
         if current_mode != stat.S_IRWXU:
             self._log.warning(
                 _LW('signing_dir mode is %(mode)s instead of %(need)s'),
                 {'mode': oct(current_mode), 'need': oct(stat.S_IRWXU)})
     else:
         os.makedirs(self._directory_name, stat.S_IRWXU)
Esempio n. 54
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'))
Esempio n. 55
0
def decrypt_data(key, data):
    """Decrypt the data with the given secret key."""
    iv = data[:16]
    cipher = ciphers.Cipher(algorithms.AES(key),
                            modes.CBC(iv),
                            backend=crypto_backends.default_backend())
    try:
        decryptor = cipher.decryptor()
        result = decryptor.update(data[16:]) + decryptor.finalize()
    except Exception:
        raise DecryptError(_('Encrypted data appears to be corrupted.'))

    # Strip the last n padding bytes where n is the last value in
    # the plaintext
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    return unpadder.update(result) + unpadder.finalize()
Esempio n. 56
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'))
Esempio n. 57
0
    def _get_user_token_from_request(self, request):
        """Get token id from request.

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

        """
        token = request.user_token

        if token:
            return token
        else:
            if not self._delay_auth_decision:
                self.log.debug('Headers: %s', dict(request.headers))
            raise exc.InvalidToken(_('Unable to find token in headers'))
 def _verify_signing_dir(self):
     if os.path.isdir(self._directory_name):
         if not os.access(self._directory_name, os.W_OK):
             raise exc.ConfigurationError(
                 _('unable to access signing_dir %s') %
                 self._directory_name)
         uid = os.getuid()
         if os.stat(self._directory_name).st_uid != uid:
             self._log.warning(_LW('signing_dir is not owned by %s'), uid)
         current_mode = stat.S_IMODE(os.stat(self._directory_name).st_mode)
         if current_mode != stat.S_IRWXU:
             self._log.warning(
                 _LW('signing_dir mode is %(mode)s instead of %(need)s'),
                 {'mode': oct(current_mode), 'need': oct(stat.S_IRWXU)})
     else:
         os.makedirs(self._directory_name, stat.S_IRWXU)
def decrypt_data(key, data):
    """Decrypt the data with the given secret key."""
    iv = data[:16]
    cipher = ciphers.Cipher(
        algorithms.AES(key),
        modes.CBC(iv),
        backend=crypto_backends.default_backend())
    try:
        decryptor = cipher.decryptor()
        result = decryptor.update(data[16:]) + decryptor.finalize()
    except Exception:
        raise DecryptError(_('Encrypted data appears to be corrupted.'))

    # Strip the last n padding bytes where n is the last value in
    # the plaintext
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    return unpadder.update(result) + unpadder.finalize()
Esempio n. 60
0
def _conf_values_type_convert(group_name, all_options, conf):
    """Convert conf values into correct type."""
    if not conf:
        return {}

    opts = {}
    opt_types = {}

    for group, options in all_options:
        # only accept paste overrides for the primary group
        if group != group_name:
            continue

        for o in options:
            type_dest = (getattr(o, 'type', str), o.dest)
            opt_types[o.dest] = type_dest
            # Also add the deprecated name with the same type and dest.
            for d_o in o.deprecated_opts:
                opt_types[d_o.name] = type_dest

        break

    for k, v in conf.items():
        dest = k
        try:
            # 'here' and '__file__' come from paste.deploy
            # 'configkey' is added by panko and gnocchi
            if v is not None and k not in ['here', '__file__', 'configkey']:
                type_, dest = opt_types[k]
                v = type_(v)
        except KeyError:  # nosec
            _LOG.warning('The option "%s" is not known to keystonemiddleware',
                         k)
        except ValueError as e:
            raise exceptions.ConfigurationError(
                _('Unable to convert the value of option "%(key)s" into '
                  'correct type: %(ex)s') % {
                      'key': k,
                      'ex': e
                  })
        opts[dest] = v

    return opts