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'))
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
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'))
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}
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()
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 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'))
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'))
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'))
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 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
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
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'))
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
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)
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)
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
def _assert_token_not_expired(self, token_expires): if timeutils.utcnow() > timeutils.normalize_time(token_expires): raise _exceptions.InvalidToken()
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'))
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'))
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'))