def _check_keystone_extensions(self, url):
        """Calls Keystone URL and detects the available extensions."""
        try:
            if not url.endswith("/"):
                url += '/'
            resp, body = self.request("%sextensions" % url,
                                      "GET",
                                      headers={'Accept': 'application/json'})
            if resp.status_code in (200, 204):  # some cases we get No Content
                if 'extensions' in body and 'values' in body['extensions']:
                    # Parse correct format (per contract)
                    extensions = body['extensions']['values']
                elif 'extensions' in body:
                    # Support incorrect, but prevalent format
                    extensions = body['extensions']
                else:
                    return dict(
                        message=('Unrecognized extensions response from %s' %
                                 url))

                return dict(self._get_extension_info(e) for e in extensions)
            elif resp.status_code == 305:
                return self._check_keystone_extensions(resp['location'])
            else:
                raise exceptions.from_response(resp, "GET",
                                               "%sextensions" % url)
        except Exception as e:
            _logger.exception(e)
示例#2
0
    def _check_keystone_extensions(self, url):
        """Call Keystone URL and detects the available extensions."""
        try:
            if not url.endswith("/"):
                url += '/'
            resp, body = self._request("%sextensions" % url, "GET",
                                       headers={'Accept':
                                                'application/json'})
            if resp.status_code in (200, 204):  # some cases we get No Content
                if 'extensions' in body and 'values' in body['extensions']:
                    # Parse correct format (per contract)
                    extensions = body['extensions']['values']
                elif 'extensions' in body:
                    # Support incorrect, but prevalent format
                    extensions = body['extensions']
                else:
                    return dict(message=(
                        _('Unrecognized extensions response from %s') % url))

                return dict(self._get_extension_info(e) for e in extensions)
            elif resp.status_code == 305:
                return self._check_keystone_extensions(resp['location'])
            else:
                raise exceptions.from_response(
                    resp, "GET", "%sextensions" % url)
        except Exception:
            _logger.exception('Failed to check keystone extensions.')
示例#3
0
    def request(self, url, method, **kwargs):
        """Send an http request with the specified characteristics.

        Wrapper around requests.request to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.
        """
        # Copy the kwargs so we can reuse the original in case of redirects
        request_kwargs = copy.copy(kwargs)
        request_kwargs.setdefault('headers', kwargs.get('headers', {}))
        request_kwargs['headers']['User-Agent'] = self.USER_AGENT
        if self.original_ip:
            request_kwargs['headers']['Forwarded'] = "for=%s;by=%s" % (
                self.original_ip, self.USER_AGENT)
        if 'body' in kwargs:
            request_kwargs['headers']['Content-Type'] = 'application/json'
            request_kwargs['data'] = self.serialize(kwargs['body'])
            del request_kwargs['body']
        if self.cert:
            request_kwargs['cert'] = self.cert
        if self.timeout is not None:
            request_kwargs.setdefault('timeout', self.timeout)

        self.http_log_req((
            url,
            method,
        ), request_kwargs)

        try:
            resp = requests.request(method,
                                    url,
                                    verify=self.verify_cert,
                                    **request_kwargs)
        except requests.ConnectionError:
            msg = 'Unable to establish connection to %s' % url
            raise exceptions.ClientException(msg)

        self.http_log_resp(resp)

        if resp.text:
            try:
                body = json.loads(resp.text)
            except (ValueError, TypeError):
                body = None
                _logger.debug("Could not decode JSON from body: %s" %
                              resp.text)
        else:
            _logger.debug("No body was returned.")
            body = None

        if resp.status_code >= 400:
            _logger.debug("Request returned failure status: %s",
                          resp.status_code)
            raise exceptions.from_response(resp, body or resp.text)
        elif resp.status_code in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp.headers['location'], method, **kwargs)

        return resp, body
    def request(self, url, method, **kwargs):
        """Send an http request with the specified characteristics.

        Wrapper around requests.request to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.
        """
        # Copy the kwargs so we can reuse the original in case of redirects
        request_kwargs = copy.copy(kwargs)
        request_kwargs.setdefault('headers', kwargs.get('headers', {}))
        request_kwargs['headers']['User-Agent'] = self.USER_AGENT
        if self.original_ip:
            request_kwargs['headers']['Forwarded'] = "for=%s;by=%s" % (
                self.original_ip, self.USER_AGENT)
        if 'body' in kwargs:
            request_kwargs['headers']['Content-Type'] = 'application/json'
            request_kwargs['data'] = self.serialize(kwargs['body'])
            del request_kwargs['body']
        if self.cert:
            request_kwargs['cert'] = self.cert
        if self.timeout is not None:
            request_kwargs.setdefault('timeout', self.timeout)

        self.http_log_req((url, method,), request_kwargs)

        try:
            resp = requests.request(
                method,
                url,
                verify=self.verify_cert,
                **request_kwargs)
        except requests.ConnectionError:
            msg = 'Unable to establish connection to %s' % url
            raise exceptions.ClientException(msg)

        self.http_log_resp(resp)

        if resp.text:
            try:
                body = json.loads(resp.text)
            except (ValueError, TypeError):
                body = None
                _logger.debug("Could not decode JSON from body: %s"
                              % resp.text)
        else:
            _logger.debug("No body was returned.")
            body = None

        if resp.status_code >= 400:
            _logger.debug(
                "Request returned failure status: %s",
                resp.status_code)
            raise exceptions.from_response(resp, body or resp.text)
        elif resp.status_code in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp.headers['location'], method, **kwargs)

        return resp, body
示例#5
0
 def assert_exception(self, ex_cls, method, url, status_code, json_data):
     ex = exceptions.from_response(
         FakeResponse(status_code=status_code,
                      headers={"Content-Type": "application/json"},
                      json_data=json_data), method, url)
     self.assertIsInstance(ex, ex_cls)
     self.assertEqual(ex.message, json_data["error"]["message"])
     self.assertEqual(ex.details, json_data["error"]["details"])
     self.assertEqual(ex.method, method)
     self.assertEqual(ex.url, url)
     self.assertEqual(ex.http_status, status_code)
 def assert_exception(self, ex_cls, method, url, status_code, json_data):
     ex = exceptions.from_response(
         FakeResponse(status_code=status_code,
                      headers={"Content-Type": "application/json"},
                      json_data=json_data),
         method,
         url)
     self.assertIsInstance(ex, ex_cls)
     self.assertEqual(ex.message, json_data["error"]["message"])
     self.assertEqual(ex.details, json_data["error"]["details"])
     self.assertEqual(ex.method, method)
     self.assertEqual(ex.url, url)
     self.assertEqual(ex.http_status, status_code)
 def _check_keystone_versions(self, url):
     """Calls Keystone URL and detects the available API versions."""
     try:
         client = httpclient.HTTPClient()
         resp, body = client.request(url,
                                     "GET",
                                     headers={'Accept': 'application/json'})
         # Multiple Choices status code is returned by the root
         # identity endpoint, with references to one or more
         # Identity API versions -- v3 spec
         # some cases we get No Content
         if resp.status_code in (200, 204, 300):
             try:
                 results = {}
                 if 'version' in body:
                     results['message'] = "Keystone found at %s" % url
                     version = body['version']
                     # Stable/diablo incorrect format
                     id, status, version_url = \
                         self._get_version_info(version, url)
                     results[str(id)] = {
                         "id": id,
                         "status": status,
                         "url": version_url
                     }
                     return results
                 elif 'versions' in body:
                     # Correct format
                     results['message'] = "Keystone found at %s" % url
                     for version in body['versions']['values']:
                         id, status, version_url = \
                             self._get_version_info(version, url)
                         results[str(id)] = {
                             "id": id,
                             "status": status,
                             "url": version_url
                         }
                     return results
                 else:
                     results['message'] = ("Unrecognized response from %s" %
                                           url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status_code == 305:
             return self._check_keystone_versions(resp['location'])
         else:
             raise exceptions.from_response(resp, "GET", url)
     except Exception as e:
         _logger.exception(e)
 def _check_keystone_versions(self, url):
     """Calls Keystone URL and detects the available API versions."""
     try:
         httpclient = client.HTTPClient()
         resp, body = httpclient.request(url, "GET",
                                         headers={'Accept':
                                                  'application/json'})
         # Multiple Choices status code is returned by the root
         # identity endpoint, with references to one or more
         # Identity API versions -- v3 spec
         # some cases we get No Content
         if resp.status_code in (200, 204, 300):
             try:
                 results = {}
                 if 'version' in body:
                     results['message'] = "Keystone found at %s" % url
                     version = body['version']
                     # Stable/diablo incorrect format
                     id, status, version_url = \
                         self._get_version_info(version, url)
                     results[str(id)] = {"id": id,
                                         "status": status,
                                         "url": version_url}
                     return results
                 elif 'versions' in body:
                     # Correct format
                     results['message'] = "Keystone found at %s" % url
                     for version in body['versions']['values']:
                         id, status, version_url = \
                             self._get_version_info(version, url)
                         results[str(id)] = {"id": id,
                                             "status": status,
                                             "url": version_url}
                     return results
                 else:
                     results['message'] = ("Unrecognized response from %s"
                                           % url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status_code == 305:
             return self._check_keystone_versions(resp['location'])
         else:
             raise exceptions.from_response(resp, resp.text)
     except Exception as e:
         _logger.exception(e)
示例#9
0
 def _check_keystone_versions(self, url):
     """ Calls Keystone URL and detects the available API versions """
     try:
         httpclient = client.HTTPClient()
         resp, body = httpclient.request(
             url, "GET", headers={'Accept': 'application/json'})
         if resp.status in (200, 204):  # in some cases we get No Content
             try:
                 results = {}
                 if 'version' in body:
                     results['message'] = "Keystone found at %s" % url
                     version = body['version']
                     # Stable/diablo incorrect format
                     id, status, version_url = self._get_version_info(
                         version, url)
                     results[str(id)] = {
                         "id": id,
                         "status": status,
                         "url": version_url
                     }
                     return results
                 elif 'versions' in body:
                     # Correct format
                     results['message'] = "Keystone found at %s" % url
                     for version in body['versions']['values']:
                         id, status, version_url = self._get_version_info(
                             version, url)
                         results[str(id)] = {
                             "id": id,
                             "status": status,
                             "url": version_url
                         }
                     return results
                 else:
                     results['message'] = ("Unrecognized response from %s" %
                                           url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status == 305:
             return self._check_keystone_versions(resp['location'])
         else:
             raise exceptions.from_response(resp, body)
     except Exception as e:
         _logger.exception(e)
示例#10
0
    def request(self, url, method, **kwargs):
        """ Send an http request with the specified characteristics.

        Wrapper around requests.request to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.
        """
        # Copy the kwargs so we can reuse the original in case of redirects
        request_kwargs = copy.copy(kwargs)
        request_kwargs.setdefault("headers", kwargs.get("headers", {}))
        request_kwargs["headers"]["User-Agent"] = self.USER_AGENT
        if self.original_ip:
            request_kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (self.original_ip, self.USER_AGENT)
        if "body" in kwargs:
            request_kwargs["headers"]["Content-Type"] = "application/json"
            request_kwargs["data"] = self.serialize(kwargs["body"])
            del request_kwargs["body"]
        if self.cert:
            request_kwargs["cert"] = self.cert
        if self.timeout is not None:
            request_kwargs.setdefault("timeout", self.timeout)

        self.http_log_req((url, method), request_kwargs)
        resp = requests.request(method, url, verify=self.verify_cert, **request_kwargs)

        self.http_log_resp(resp)

        if resp.status_code >= 400:
            _logger.debug("Request returned failure status: %s", resp.status_code)
            raise exceptions.from_response(resp, resp.text)
        elif resp.status_code in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp.headers["location"], method, **kwargs)

        if resp.text:
            try:
                body = json.loads(resp.text)
            except ValueError:
                body = None
                _logger.debug("Could not decode JSON from body: %s" % resp.text)
        else:
            _logger.debug("No body was returned.")
            body = None

        return resp, body
示例#11
0
 def _check_keystone_extensions(self, url):
     """Calls Keystone URL and detects the available extensions."""
     try:
         client = httpclient.HTTPClient()
         if not url.endswith("/"):
             url += '/'
         resp, body = client.request("%sextensions" % url, "GET",
                                     headers={'Accept':
                                              'application/json'})
         if resp.status_code in (200, 204):  # some cases we get No Content
             try:
                 results = {}
                 if 'extensions' in body:
                     if 'values' in body['extensions']:
                         # Parse correct format (per contract)
                         for extension in body['extensions']['values']:
                             alias, name = \
                                 self._get_extension_info(
                                     extension['extension']
                                 )
                             results[alias] = name
                         return results
                     else:
                         # Support incorrect, but prevalent format
                         for extension in body['extensions']:
                             alias, name = \
                                 self._get_extension_info(extension)
                             results[alias] = name
                         return results
                 else:
                     results['message'] = ("Unrecognized extensions "
                                           "response from %s" % url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status_code == 305:
             return self._check_keystone_extensions(resp['location'])
         else:
             raise exceptions.from_response(
                 resp, "GET", "%sextensions" % url)
     except Exception as e:
         _logger.exception(e)
示例#12
0
 def _check_keystone_extensions(self, url):
     """Calls Keystone URL and detects the available extensions."""
     try:
         client = httpclient.HTTPClient()
         if not url.endswith("/"):
             url += '/'
         resp, body = client.request("%sextensions" % url,
                                     "GET",
                                     headers={'Accept': 'application/json'})
         if resp.status_code in (200, 204):  # some cases we get No Content
             try:
                 results = {}
                 if 'extensions' in body:
                     if 'values' in body['extensions']:
                         # Parse correct format (per contract)
                         for extension in body['extensions']['values']:
                             alias, name = \
                                 self._get_extension_info(
                                     extension['extension']
                                 )
                             results[alias] = name
                         return results
                     else:
                         # Support incorrect, but prevalent format
                         for extension in body['extensions']:
                             alias, name = \
                                 self._get_extension_info(extension)
                             results[alias] = name
                         return results
                 else:
                     results['message'] = ("Unrecognized extensions "
                                           "response from %s" % url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status_code == 305:
             return self._check_keystone_extensions(resp['location'])
         else:
             raise exceptions.from_response(resp, "GET",
                                            "%sextensions" % url)
     except Exception as e:
         _logger.exception(e)
示例#13
0
 def _check_keystone_versions(self, url):
     """ Calls Keystone URL and detects the available API versions """
     try:
         httpclient = client.HTTPClient()
         resp, body = httpclient.request(url, "GET",
                                         headers={'Accept':
                                                  'application/json'})
         if resp.status in (200, 204):  # in some cases we get No Content
             try:
                 results = {}
                 if 'version' in body:
                     results['message'] = "Keystone found at %s" % url
                     version = body['version']
                     # Stable/diablo incorrect format
                     id, status, version_url = \
                         self._get_version_info(version, url)
                     results[str(id)] = {"id": id,
                                         "status": status,
                                         "url": version_url}
                     return results
                 elif 'versions' in body:
                     # Correct format
                     results['message'] = "Keystone found at %s" % url
                     for version in body['versions']['values']:
                         id, status, version_url = \
                             self._get_version_info(version, url)
                         results[str(id)] = {"id": id,
                                             "status": status,
                                             "url": version_url}
                     return results
                 else:
                     results['message'] = ("Unrecognized response from %s"
                                           % url)
                 return results
             except KeyError:
                 raise exceptions.AuthorizationFailure()
         elif resp.status == 305:
             return self._check_keystone_versions(resp['location'])
         else:
             raise exceptions.from_response(resp, body)
     except Exception as e:
         _logger.exception(e)
示例#14
0
    def request(self, url, method, **kwargs):
        """ Send an http request with the specified characteristics.

        Wrapper around httplib2.Http.request to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.
        """
        # Copy the kwargs so we can reuse the original in case of redirects
        request_kwargs = copy.copy(kwargs)
        request_kwargs.setdefault('headers', kwargs.get('headers', {}))
        request_kwargs['headers']['User-Agent'] = self.USER_AGENT
        if self.original_ip:
            request_kwargs['headers']['Forwarded'] = "for=%s;by=%s" % (
                self.original_ip, self.USER_AGENT)
        if 'body' in kwargs:
            request_kwargs['headers']['Content-Type'] = 'application/json'
            request_kwargs['body'] = self.serialize(kwargs['body'])

        self.http_log_req((
            url,
            method,
        ), request_kwargs)
        resp, body = super(HTTPClient, self).request(url, method,
                                                     **request_kwargs)
        self.http_log_resp(resp, body)

        if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
            _logger.debug("Request returned failure status.")
            raise exceptions.from_response(resp, body)
        elif resp.status in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp['location'], method, **kwargs)

        if body:
            try:
                body = json.loads(body)
            except ValueError:
                _logger.debug("Could not decode JSON from body: %s" % body)
        else:
            _logger.debug("No body was returned.")
            body = None

        return resp, body
示例#15
0
    def request(self, url, method, **kwargs):
        """ Send an http request with the specified characteristics.

        Wrapper around httplib2.Http.request to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.
        """
        # Copy the kwargs so we can reuse the original in case of redirects
        request_kwargs = copy.copy(kwargs)
        request_kwargs.setdefault('headers', kwargs.get('headers', {}))
        request_kwargs['headers']['User-Agent'] = self.USER_AGENT
        if self.original_ip:
            request_kwargs['headers']['Forwarded'] = "for=%s;by=%s" % (
                self.original_ip, self.USER_AGENT)
        if 'body' in kwargs:
            request_kwargs['headers']['Content-Type'] = 'application/json'
            request_kwargs['body'] = json.dumps(kwargs['body'])

        self.http_log_req((url, method,), request_kwargs)
        resp, body = super(HTTPClient, self).request(url,
                                                     method,
                                                     **request_kwargs)
        self.http_log_resp(resp, body)

        if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
            _logger.debug("Request returned failure status.")
            raise exceptions.from_response(resp, body)
        elif resp.status in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp['location'], method, **kwargs)

        if body:
            try:
                body = json.loads(body)
            except ValueError:
                _logger.debug("Could not decode JSON from body: %s" % body)
        else:
            _logger.debug("No body was returned.")
            body = None

        return resp, body
示例#16
0
def _retrieve_data_from_keystone(redis_client, url, tenant, token,
                                 blacklist_ttl, max_cache_life):
    """Retrieve the authentication data from OpenStack Keystone

    :param redis_client: redis.Redis object connected to the redis cache
    :param url: Keystone Identity URL to authenticate against
    :param tenant: tenant id of user data to retrieve
    :param token: auth_token for the tenant_id
    :param blacklist_ttl: time in milliseconds for blacklisting failed tokens
    :param max_cache_life: time in seconds for the maximum time a cache entry
                           should remain in the cache of valid data

    :returns: a keystoneclient.access.AccessInfo on success or None on error
    """
    try:
        # Try to authenticate the user and get the user information using
        # only the data provided, no special administrative tokens required.
        # When using the alternative validation method, the service catalog
        # identity does not return a service catalog for valid tokens.

        if get_conf().alternate_validation is True:
            _url = url.rstrip('/') + '/tokens'
            validation_url = _url + '/{0}'.format(token)
            headers = {
                'Accept': 'application/json',
                'X-Auth-Token': token
            }
            resp = requests.get(validation_url, headers=headers)
            if resp.status_code >= 400:
                LOG.debug('Request returned failure status: {0}'.format(
                    resp.status_code))
                raise exceptions.from_response(resp, 'GET', _url)

            try:
                resp_data = resp.json()['access']
            except (KeyError, ValueError):
                raise exceptions.InvalidResponse(response=resp)

            access_info = access.AccessInfoV2(**resp_data)
        else:
            keystone = keystonev2_client.Client(tenant_id=tenant,
                                                token=token,
                                                auth_url=url)
            access_info = keystone.get_raw_token_from_identity_service(
                auth_url=url, tenant_id=tenant, token=token)

        # cache the data so it is easier to access next time
        _send_data_to_cache(redis_client, url, access_info, max_cache_life)

        return access_info

    except (exceptions.AuthorizationFailure, exceptions.Unauthorized) as ex:
        # re-raise 413 here and later on respond with 503
        if 'HTTP 413' in str(ex):
            raise exceptions.RequestEntityTooLarge(
                method='POST',
                url=url,
                http_status=413
            )
        # Provided data was invalid and authorization failed
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        # Blacklist the token
        _blacklist_token(redis_client, token, blacklist_ttl)
        return None
    except exceptions.RequestEntityTooLarge:
        LOG.debug('Request entity too large error from authentication server.')
        raise
    except Exception as ex:
        # Provided data was invalid or something else went wrong
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        return None
    def request(self, url, method, json=None, original_ip=None,
                user_agent=None, redirect=None, **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Fully qualified URL of HTTP request
        :param string method: The http method to use. (eg. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        string_parts = ['curl -i']

        if method:
            string_parts.extend(['-X', method])

        string_parts.append(url)

        if headers:
            for header in six.iteritems(headers):
                string_parts.append('-H "%s: %s"' % header)

        _logger.debug('REQ: %s', ' '.join(string_parts))

        data = kwargs.get('data')
        if data:
            _logger.debug('REQ BODY: %s', data)

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        resp = self._send_request(url, method, redirect, **kwargs)

        # NOTE(jamielennox): we create a tuple here to be the same as what is
        # returned by the requests library.
        resp.history = tuple(resp.history)

        if resp.status_code >= 400:
            _logger.debug('Request returned failure status: %s',
                          resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
示例#18
0
    def request(self,
                url,
                method,
                json=None,
                original_ip=None,
                user_agent=None,
                redirect=None,
                authenticated=None,
                endpoint_filter=None,
                auth=None,
                requests_auth=None,
                raise_exc=True,
                allow_reauth=True,
                log=True,
                endpoint_override=None,
                connect_retries=0,
                logger=_logger,
                **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Path or fully qualified URL of HTTP request. If only
                           a path is provided then endpoint_filter must also be
                           provided such that the base URL can be determined.
                           If a fully qualified URL is provided then
                           endpoint_filter will be ignored.
        :param string method: The http method to use. (e.g. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param int connect_retries: the maximum number of retries that should
                                    be attempted for connection errors.
                                    (optional, defaults to 0 - never retry).
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param dict endpoint_filter: Data to be provided to an auth plugin with
                                     which it should be able to determine an
                                     endpoint to use for this request. If not
                                     provided then URL is expected to be a
                                     fully qualified URL. (optional)
        :param str endpoint_override: The URL to use instead of looking up the
                                      endpoint in the auth plugin. This will be
                                      ignored if a fully qualified URL is
                                      provided but take priority over an
                                      endpoint_filter. (optional)
        :param auth: The auth plugin to use when authenticating this request.
                     This will override the plugin that is attached to the
                     session (if any). (optional)
        :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin`
        :param requests_auth: A requests library auth plugin that cannot be
                              passed via kwarg because the `auth` kwarg
                              collides with our own auth plugins. (optional)
        :type requests_auth: :py:class:`requests.auth.AuthBase`
        :param bool raise_exc: If True then raise an appropriate exception for
                               failed HTTP requests. If False then return the
                               request object. (optional, default True)
        :param bool allow_reauth: Allow fetching a new token and retrying the
                                  request on receiving a 401 Unauthorized
                                  response. (optional, default True)
        :param bool log: If True then log the request and response data to the
                         debug log. (optional, default True)
        :param logger: The logger object to use to log request and responses.
                       If not provided the keystoneclient.session default
                       logger will be used.
        :type logger: logging.Logger
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises keystoneclient.exceptions.ClientException: For connection
            failure, or to indicate an error response code.

        :returns: The response to the request.
        """
        headers = kwargs.setdefault('headers', dict())

        if authenticated is None:
            authenticated = bool(auth or self.auth)

        if authenticated:
            auth_headers = self.get_auth_headers(auth)

            if auth_headers is None:
                msg = _('No valid authentication is available')
                raise exceptions.AuthorizationFailure(msg)

            headers.update(auth_headers)

        if osprofiler_web:
            headers.update(osprofiler_web.get_trace_id_headers())

        # if we are passed a fully qualified URL and an endpoint_filter we
        # should ignore the filter. This will make it easier for clients who
        # want to overrule the default endpoint_filter data added to all client
        # requests. We check fully qualified here by the presence of a host.
        if not urllib.parse.urlparse(url).netloc:
            base_url = None

            if endpoint_override:
                base_url = endpoint_override
            elif endpoint_filter:
                base_url = self.get_endpoint(auth, **endpoint_filter)

            if not base_url:
                service_type = (endpoint_filter
                                or {}).get('service_type', 'unknown')
                msg = _('Endpoint for %s service') % service_type
                raise exceptions.EndpointNotFound(msg)

            url = '%s/%s' % (base_url.rstrip('/'), url.lstrip('/'))

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        if requests_auth:
            kwargs['auth'] = requests_auth

        if log:
            self._http_log_request(url,
                                   method=method,
                                   data=kwargs.get('data'),
                                   headers=headers,
                                   logger=logger)

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        send = functools.partial(self._send_request, url, method, redirect,
                                 log, logger, connect_retries)

        try:
            connection_params = self.get_auth_connection_params(auth=auth)
        except exceptions.MissingAuthPlugin:  # nosec(cjschaef)
            # NOTE(jamielennox): If we've gotten this far without an auth
            # plugin then we should be happy with allowing no additional
            # connection params. This will be the typical case for plugins
            # anyway.
            pass
        else:
            if connection_params:
                kwargs.update(connection_params)

        resp = send(**kwargs)

        # handle getting a 401 Unauthorized response by invalidating the plugin
        # and then retrying the request. This is only tried once.
        if resp.status_code == 401 and authenticated and allow_reauth:
            if self.invalidate(auth):
                auth_headers = self.get_auth_headers(auth)

                if auth_headers is not None:
                    headers.update(auth_headers)
                    resp = send(**kwargs)

        if raise_exc and resp.status_code >= 400:
            logger.debug('Request returned failure status: %s',
                         resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
    def request(self, url, method, json=None, original_ip=None,
                user_agent=None, redirect=None, authenticated=None,
                endpoint_filter=None, **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Path or fully qualified URL of HTTP request. If only
                           a path is provided then endpoint_filter must also be
                           provided such that the base URL can be determined.
                           If a fully qualified URL is provided then
                           endpoint_filter will be ignored.
        :param string method: The http method to use. (eg. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param dict endpoint_filter: Data to be provided to an auth plugin with
                                     which it should be able to determine an
                                     endpoint to use for this request. If not
                                     provided then URL is expected to be a
                                     fully qualified URL. (optional)
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if authenticated is None:
            authenticated = self.auth is not None

        if authenticated:
            token = self.get_token()

            if not token:
                raise exceptions.AuthorizationFailure("No token Available")

            headers['X-Auth-Token'] = token

        # if we are passed a fully qualified URL and a endpoint_filter we
        # should ignore the filter. This will make it easier for clients who
        # want to overrule the default endpoint_filter data added to all client
        # requests. We check fully qualified here by the presence of a host.
        url_data = urllib.parse.urlparse(url)
        if endpoint_filter and not url_data.netloc:
            base_url = self.get_endpoint(**endpoint_filter)

            if not base_url:
                raise exceptions.EndpointNotFound()

            url = '%s/%s' % (base_url.rstrip('/'), url.lstrip('/'))

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        string_parts = ['curl -i']

        # NOTE(jamielennox): None means let requests do its default validation
        # so we need to actually check that this is False.
        if self.verify is False:
            string_parts.append('--insecure')

        if method:
            string_parts.extend(['-X', method])

        string_parts.append(url)

        if headers:
            for header in six.iteritems(headers):
                string_parts.append('-H "%s: %s"' % header)

        try:
            string_parts.append("-d '%s'" % kwargs['data'])
        except KeyError:
            pass

        _logger.debug('REQ: %s', ' '.join(string_parts))

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        resp = self._send_request(url, method, redirect, **kwargs)

        # NOTE(jamielennox): we create a tuple here to be the same as what is
        # returned by the requests library.
        resp.history = tuple(resp.history)

        if resp.status_code >= 400:
            _logger.debug('Request returned failure status: %s',
                          resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
示例#20
0
                                                     **request_kwargs)

        self.http_log((url, method,), request_kwargs, resp, body)

        if body:
            try:
                body = json.loads(body)
            except ValueError, e:
                _logger.debug("Could not decode JSON from body: %s" % body)
        else:
            _logger.debug("No body was returned.")
            body = None

        if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
            _logger.exception("Request returned failure status.")
            raise exceptions.from_response(resp, body)
        elif resp.status in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp['location'], method, **kwargs)

        return resp, body

    def _cs_request(self, url, method, **kwargs):
        if not self.management_url:
            self.authenticate()

        kwargs.setdefault('headers', {})
        if self.auth_token:
            kwargs['headers']['X-Auth-Token'] = self.auth_token

        # Perform the request once. If we get a 401 back then it
示例#21
0
            url,
            method,
        ), request_kwargs, resp, body)

        if body:
            try:
                body = json.loads(body)
            except ValueError, e:
                _logger.debug("Could not decode JSON from body: %s" % body)
        else:
            _logger.debug("No body was returned.")
            body = None

        if resp.status in (400, 401, 403, 404, 408, 409, 413, 500, 501):
            _logger.exception("Request returned failure status.")
            raise exceptions.from_response(resp, body)
        elif resp.status in (301, 302, 305):
            # Redirected. Reissue the request to the new location.
            return self.request(resp['location'], method, **kwargs)

        return resp, body

    def _cs_request(self, url, method, **kwargs):
        if not self.management_url:
            self.authenticate()

        kwargs.setdefault('headers', {})
        if self.auth_token and self.auth_token != self.password:
            kwargs['headers']['X-Auth-Token'] = self.auth_token

        # Perform the request once. If we get a 401 back then it
示例#22
0
    def request(self,
                url,
                method,
                json=None,
                original_ip=None,
                user_agent=None,
                redirect=None,
                authenticated=None,
                **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Fully qualified URL of HTTP request
        :param string method: The http method to use. (eg. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if authenticated is None:
            authenticated = self.auth is not None

        if authenticated:
            token = self.get_token()

            if not token:
                raise exceptions.AuthorizationFailure("No token Available")

            headers['X-Auth-Token'] = token

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        string_parts = ['curl -i']

        # NOTE(jamielennox): None means let requests do its default validation
        # so we need to actually check that this is False.
        if self.verify is False:
            string_parts.append('--insecure')

        if method:
            string_parts.extend(['-X', method])

        string_parts.append(url)

        if headers:
            for header in six.iteritems(headers):
                string_parts.append('-H "%s: %s"' % header)

        try:
            string_parts.append("-d '%s'" % kwargs['data'])
        except KeyError:
            pass

        _logger.debug('REQ: %s', ' '.join(string_parts))

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        resp = self._send_request(url, method, redirect, **kwargs)

        # NOTE(jamielennox): we create a tuple here to be the same as what is
        # returned by the requests library.
        resp.history = tuple(resp.history)

        if resp.status_code >= 400:
            _logger.debug('Request returned failure status: %s',
                          resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
    def request(self, url, method, json=None, original_ip=None, debug=None,
                logger=None, user_agent=None, **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Fully qualified URL of HTTP request
        :param string method: The http method to use. (eg. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param bool debug: Enable debug logging. (Defaults to False)
        :param kwargs: any other parameter that can be passed to
             requests.Session.request (such as `headers`) or `json`
             that will be encoded as JSON and used as `data` argument
        :param logging.Logger logger: A logger to output to. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        if not logger:
            logger = _logger

        if debug is None:
            debug = self.debug

        kwargs.setdefault('verify', self.verify)

        if debug:
            string_parts = ['curl -i']

            if method:
                string_parts.extend([' -X ', method])

            string_parts.extend([' ', url])

            if headers:
                for header in six.iteritems(headers):
                    string_parts.append(' -H "%s: %s"' % header)

            logger.debug('REQ: %s', ''.join(string_parts))

            data = kwargs.get('data')
            if data:
                logger.debug('REQ BODY: %s', data)

        try:
            resp = self.session.request(method, url, **kwargs)
        except requests.exceptions.SSLError:
            msg = 'SSL exception connecting to %s' % url
            raise exceptions.SSLError(msg)
        except requests.exceptions.Timeout:
            msg = 'Request to %s timed out' % url
            raise exceptions.Timeout(msg)
        except requests.exceptions.ConnectionError:
            msg = 'Unable to establish connection to %s' % url
            raise exceptions.ConnectionError(msg)

        if debug:
            logger.debug('RESP: [%s] %s\nRESP BODY: %s\n',
                         resp.status_code, resp.headers, resp.text)

        if resp.status_code >= 400:
            logger.debug('Request returned failure status: %s',
                         resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
示例#24
0
def request(url, method='GET', headers=None, original_ip=None, debug=False,
            logger=None, **kwargs):
    """Perform a http request with standard settings.

    A wrapper around requests.request that adds standard headers like
    User-Agent and provides optional debug logging of the request.

    Arguments that are not handled are passed through to the requests library.

    :param string url: The url to make the request of.
    :param string method: The http method to use. (eg. 'GET', 'POST')
    :param dict headers: Headers to be included in the request. (optional)
    :param string original_ip: Mark this request as forwarded for this ip.
                               (optional)
    :param bool debug: Enable debug logging. (Defaults to False)
    :param logging.Logger logger: A logger to output to. (optional)

    :raises exceptions.ClientException: For connection failure, or to indicate
                                        an error response code.

    :returns: The response to the request.
    """

    if not headers:
        headers = dict()

    if not logger:
        logger = _logger

    headers.setdefault('User-Agent', USER_AGENT)

    if original_ip:
        headers['Forwarded'] = "for=%s;by=%s" % (original_ip, USER_AGENT)

    if debug:
        string_parts = ['curl -i']

        if method:
            string_parts.append(' -X %s' % method)

        string_parts.append(' %s' % url)

        if headers:
            for header in headers.iteritems():
                string_parts.append(' -H "%s: %s"' % header)

        logger.debug("REQ: %s" % "".join(string_parts))

        data = kwargs.get('data')
        if data:
            logger.debug("REQ BODY: %s\n" % data)

    try:
        resp = requests.request(
            method,
            url,
            headers=headers,
            **kwargs)
    except requests.ConnectionError:
        msg = 'Unable to establish connection to %s' % url
        raise exceptions.ClientException(msg)

    if debug:
        logger.debug("RESP: [%s] %s\nRESP BODY: %s\n",
                     resp.status_code, resp.headers, resp.text)

    if resp.status_code >= 400:
        logger.debug("Request returned failure status: %s",
                     resp.status_code)
        raise exceptions.from_response(resp, method, url)

    return resp
示例#25
0
    def request(
        self,
        url,
        method,
        json=None,
        original_ip=None,
        user_agent=None,
        redirect=None,
        authenticated=None,
        endpoint_filter=None,
        auth=None,
        requests_auth=None,
        raise_exc=True,
        allow_reauth=True,
        log=True,
        endpoint_override=None,
        connect_retries=0,
        logger=_logger,
        **kwargs
    ):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Path or fully qualified URL of HTTP request. If only
                           a path is provided then endpoint_filter must also be
                           provided such that the base URL can be determined.
                           If a fully qualified URL is provided then
                           endpoint_filter will be ignored.
        :param string method: The http method to use. (e.g. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param int connect_retries: the maximum number of retries that should
                                    be attempted for connection errors.
                                    (optional, defaults to 0 - never retry).
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param dict endpoint_filter: Data to be provided to an auth plugin with
                                     which it should be able to determine an
                                     endpoint to use for this request. If not
                                     provided then URL is expected to be a
                                     fully qualified URL. (optional)
        :param str endpoint_override: The URL to use instead of looking up the
                                      endpoint in the auth plugin. This will be
                                      ignored if a fully qualified URL is
                                      provided but take priority over an
                                      endpoint_filter. (optional)
        :param auth: The auth plugin to use when authenticating this request.
                     This will override the plugin that is attached to the
                     session (if any). (optional)
        :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin`
        :param requests_auth: A requests library auth plugin that cannot be
                              passed via kwarg because the `auth` kwarg
                              collides with our own auth plugins. (optional)
        :type requests_auth: :py:class:`requests.auth.AuthBase`
        :param bool raise_exc: If True then raise an appropriate exception for
                               failed HTTP requests. If False then return the
                               request object. (optional, default True)
        :param bool allow_reauth: Allow fetching a new token and retrying the
                                  request on receiving a 401 Unauthorized
                                  response. (optional, default True)
        :param bool log: If True then log the request and response data to the
                         debug log. (optional, default True)
        :param logger: The logger object to use to log request and responses.
                       If not provided the keystoneclient.session default
                       logger will be used.
        :type logger: logging.Logger
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises keystoneclient.exceptions.ClientException: For connection
            failure, or to indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault("headers", dict())

        if authenticated is None:
            authenticated = bool(auth or self.auth)

        if authenticated:
            auth_headers = self.get_auth_headers(auth)

            if auth_headers is None:
                msg = _("No valid authentication is available")
                raise exceptions.AuthorizationFailure(msg)

            headers.update(auth_headers)

        if osprofiler_web:
            headers.update(osprofiler_web.get_trace_id_headers())

        # if we are passed a fully qualified URL and an endpoint_filter we
        # should ignore the filter. This will make it easier for clients who
        # want to overrule the default endpoint_filter data added to all client
        # requests. We check fully qualified here by the presence of a host.
        if not urllib.parse.urlparse(url).netloc:
            base_url = None

            if endpoint_override:
                base_url = endpoint_override
            elif endpoint_filter:
                base_url = self.get_endpoint(auth, **endpoint_filter)

            if not base_url:
                service_type = (endpoint_filter or {}).get("service_type", "unknown")
                msg = _("Endpoint for %s service") % service_type
                raise exceptions.EndpointNotFound(msg)

            url = "%s/%s" % (base_url.rstrip("/"), url.lstrip("/"))

        if self.cert:
            kwargs.setdefault("cert", self.cert)

        if self.timeout is not None:
            kwargs.setdefault("timeout", self.timeout)

        if user_agent:
            headers["User-Agent"] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault("User-Agent", self.user_agent)
        else:
            user_agent = headers.setdefault("User-Agent", USER_AGENT)

        if self.original_ip:
            headers.setdefault("Forwarded", "for=%s;by=%s" % (self.original_ip, user_agent))

        if json is not None:
            headers["Content-Type"] = "application/json"
            kwargs["data"] = jsonutils.dumps(json)

        kwargs.setdefault("verify", self.verify)

        if requests_auth:
            kwargs["auth"] = requests_auth

        if log:
            self._http_log_request(url, method=method, data=kwargs.get("data"), headers=headers, logger=logger)

        # Force disable requests redirect handling. We will manage this below.
        kwargs["allow_redirects"] = False

        if redirect is None:
            redirect = self.redirect

        send = functools.partial(self._send_request, url, method, redirect, log, logger, connect_retries)

        try:
            connection_params = self.get_auth_connection_params(auth=auth)
        except exceptions.MissingAuthPlugin:
            # NOTE(jamielennox): If we've gotten this far without an auth
            # plugin then we should be happy with allowing no additional
            # connection params. This will be the typical case for plugins
            # anyway.
            pass
        else:
            if connection_params:
                kwargs.update(connection_params)

        resp = send(**kwargs)

        # handle getting a 401 Unauthorized response by invalidating the plugin
        # and then retrying the request. This is only tried once.
        if resp.status_code == 401 and authenticated and allow_reauth:
            if self.invalidate(auth):
                auth_headers = self.get_auth_headers(auth)

                if auth_headers is not None:
                    headers.update(auth_headers)
                    resp = send(**kwargs)

        if raise_exc and resp.status_code >= 400:
            logger.debug("Request returned failure status: %s", resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
示例#26
0
文件: auth.py 项目: isaacm/eom
def _retrieve_data_from_keystone(redis_client, url, tenant, token,
                                 blacklist_ttl, max_cache_life):
    """Retrieve the authentication data from OpenStack Keystone

    :param redis_client: redis.Redis object connected to the redis cache
    :param url: Keystone Identity URL to authenticate against
    :param tenant: tenant id of user data to retrieve
    :param token: auth_token for the tenant_id
    :param blacklist_ttl: time in milliseconds for blacklisting failed tokens
    :param max_cache_life: time in seconds for the maximum time a cache entry
                           should remain in the cache of valid data

    :returns: a keystoneclient.access.AccessInfo on success or None on error
    """
    try:
        # Try to authenticate the user and get the user information using
        # only the data provided, no special administrative tokens required.
        # When using the alternative validation method, the service catalog
        # identity does not return a service catalog for valid tokens.

        if get_conf().alternate_validation is True:
            _url = url.rstrip('/') + '/tokens'
            validation_url = _url + '/{0}'.format(token)
            headers = {
                'Accept': 'application/json',
                'X-Auth-Token': token
            }
            resp = requests.get(validation_url, headers=headers)
            if resp.status_code >= 400:
                LOG.debug('Request returned failure status: {0}'.format(
                    resp.status_code))
                raise exceptions.from_response(resp, 'GET', _url)

            try:
                resp_data = resp.json()['access']
            except (KeyError, ValueError):
                raise exceptions.InvalidResponse(response=resp)

            access_info = access.AccessInfoV2(**resp_data)
        else:
            keystone = keystonev2_client.Client(tenant_id=tenant,
                                                token=token,
                                                auth_url=url)
            access_info = keystone.get_raw_token_from_identity_service(
                auth_url=url, tenant_id=tenant, token=token)

        # cache the data so it is easier to access next time
        _send_data_to_cache(redis_client, url, access_info, max_cache_life)

        return access_info

    except (exceptions.AuthorizationFailure, exceptions.Unauthorized) as ex:
        # re-raise 413 here and later on respond with 503
        if 'HTTP 413' in str(ex):
            raise exceptions.RequestEntityTooLarge(
                method='POST',
                url=url,
                http_status=413
            )
        # Provided data was invalid and authorization failed
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        # Blacklist the token
        _blacklist_token(redis_client, token, blacklist_ttl)
        return None
    except exceptions.RequestEntityTooLarge:
        LOG.debug('Request entity too large error from authentication server.')
        raise
    except Exception as ex:
        # Provided data was invalid or something else went wrong
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        return None
def request(url,
            method='GET',
            headers=None,
            original_ip=None,
            debug=False,
            logger=None,
            **kwargs):
    """Perform a http request with standard settings.

    A wrapper around requests.request that adds standard headers like
    User-Agent and provides optional debug logging of the request.

    Arguments that are not handled are passed through to the requests library.

    :param string url: The url to make the request of.
    :param string method: The http method to use. (eg. 'GET', 'POST')
    :param dict headers: Headers to be included in the request. (optional)
    :param string original_ip: Mark this request as forwarded for this ip.
                               (optional)
    :param bool debug: Enable debug logging. (Defaults to False)
    :param logging.Logger logger: A logger to output to. (optional)

    :raises exceptions.ClientException: For connection failure, or to indicate
                                        an error response code.

    :returns: The response to the request.
    """

    if not headers:
        headers = dict()

    if not logger:
        logger = _logger

    headers.setdefault('User-Agent', USER_AGENT)

    if original_ip:
        headers['Forwarded'] = "for=%s;by=%s" % (original_ip, USER_AGENT)

    if debug:
        string_parts = ['curl -i']

        if method:
            string_parts.append(' -X %s' % method)

        string_parts.append(' %s' % url)

        if headers:
            for header in headers.iteritems():
                string_parts.append(' -H "%s: %s"' % header)

        logger.debug("REQ: %s" % "".join(string_parts))

        data = kwargs.get('data')
        if data:
            logger.debug("REQ BODY: %s\n" % data)

    try:
        resp = requests.request(method, url, headers=headers, **kwargs)
    except requests.ConnectionError:
        msg = 'Unable to establish connection to %s' % url
        raise exceptions.ClientException(msg)

    if debug:
        logger.debug("RESP: [%s] %s\nRESP BODY: %s\n", resp.status_code,
                     resp.headers, resp.text)

    if resp.status_code >= 400:
        logger.debug("Request returned failure status: %s", resp.status_code)
        raise exceptions.from_response(resp, method, url)

    return resp
    def request(self, url, method, json=None, original_ip=None,
                user_agent=None, redirect=None, authenticated=None,
                endpoint_filter=None, auth=None, requests_auth=None,
                raise_exc=True, allow_reauth=True, log=True, **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Path or fully qualified URL of HTTP request. If only
                           a path is provided then endpoint_filter must also be
                           provided such that the base URL can be determined.
                           If a fully qualified URL is provided then
                           endpoint_filter will be ignored.
        :param string method: The http method to use. (e.g. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param dict endpoint_filter: Data to be provided to an auth plugin with
                                     which it should be able to determine an
                                     endpoint to use for this request. If not
                                     provided then URL is expected to be a
                                     fully qualified URL. (optional)
        :param auth: The auth plugin to use when authenticating this request.
                     This will override the plugin that is attached to the
                     session (if any). (optional)
        :type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
        :param requests_auth: A requests library auth plugin that cannot be
                              passed via kwarg because the `auth` kwarg
                              collides with our own auth plugins. (optional)
        :type requests_auth: :class:`requests.auth.AuthBase`
        :param bool raise_exc: If True then raise an appropriate exception for
                               failed HTTP requests. If False then return the
                               request object. (optional, default True)
        :param bool allow_reauth: Allow fetching a new token and retrying the
                                  request on receiving a 401 Unauthorized
                                  response. (optional, default True)
        :param bool log: If True then log the request and response data to the
                         debug log. (optional, default True)
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if authenticated is None:
            authenticated = bool(auth or self.auth)

        if authenticated:
            token = self.get_token(auth)

            if not token:
                raise exceptions.AuthorizationFailure("No token Available")

            headers['X-Auth-Token'] = token

        if osprofiler_web:
            headers.update(osprofiler_web.get_trace_id_headers())

        # if we are passed a fully qualified URL and an endpoint_filter we
        # should ignore the filter. This will make it easier for clients who
        # want to overrule the default endpoint_filter data added to all client
        # requests. We check fully qualified here by the presence of a host.
        url_data = urllib.parse.urlparse(url)
        if endpoint_filter and not url_data.netloc:
            base_url = self.get_endpoint(auth, **endpoint_filter)

            if not base_url:
                raise exceptions.EndpointNotFound()

            url = '%s/%s' % (base_url.rstrip('/'), url.lstrip('/'))

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        if requests_auth:
            kwargs['auth'] = requests_auth

        if log:
            self._http_log_request(url, method=method,
                                   data=kwargs.get('data'),
                                   headers=headers)

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        resp = self._send_request(url, method, redirect, log, **kwargs)

        # handle getting a 401 Unauthorized response by invalidating the plugin
        # and then retrying the request. This is only tried once.
        if resp.status_code == 401 and authenticated and allow_reauth:
            if self.invalidate(auth):
                token = self.get_token(auth)
                if token:
                    headers['X-Auth-Token'] = token
                    resp = self._send_request(url, method, redirect, log,
                                              **kwargs)

        if raise_exc and resp.status_code >= 400:
            _logger.debug('Request returned failure status: %s',
                          resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp
示例#29
0
    def request(self,
                url,
                method,
                json=None,
                original_ip=None,
                user_agent=None,
                redirect=None,
                authenticated=None,
                endpoint_filter=None,
                auth=None,
                requests_auth=None,
                raise_exc=True,
                **kwargs):
        """Send an HTTP request with the specified characteristics.

        Wrapper around `requests.Session.request` to handle tasks such as
        setting headers, JSON encoding/decoding, and error handling.

        Arguments that are not handled are passed through to the requests
        library.

        :param string url: Path or fully qualified URL of HTTP request. If only
                           a path is provided then endpoint_filter must also be
                           provided such that the base URL can be determined.
                           If a fully qualified URL is provided then
                           endpoint_filter will be ignored.
        :param string method: The http method to use. (e.g. 'GET', 'POST')
        :param string original_ip: Mark this request as forwarded for this ip.
                                   (optional)
        :param dict headers: Headers to be included in the request. (optional)
        :param json: Some data to be represented as JSON. (optional)
        :param string user_agent: A user_agent to use for the request. If
                                  present will override one present in headers.
                                  (optional)
        :param int/bool redirect: the maximum number of redirections that
                                  can be followed by a request. Either an
                                  integer for a specific count or True/False
                                  for forever/never. (optional)
        :param bool authenticated: True if a token should be attached to this
                                   request, False if not or None for attach if
                                   an auth_plugin is available.
                                   (optional, defaults to None)
        :param dict endpoint_filter: Data to be provided to an auth plugin with
                                     which it should be able to determine an
                                     endpoint to use for this request. If not
                                     provided then URL is expected to be a
                                     fully qualified URL. (optional)
        :param auth: The auth plugin to use when authenticating this request.
                     This will override the plugin that is attached to the
                     session (if any). (optional)
        :type auth: :class:`keystoneclient.auth.base.BaseAuthPlugin`
        :param requests_auth: A requests library auth plugin that cannot be
                              passed via kwarg because the `auth` kwarg
                              collides with our own auth plugins. (optional)
        :type requests_auth: :class:`requests.auth.AuthBase`
        :param bool raise_exc: If True then raise an appropriate exception for
                               failed HTTP requests. If False then return the
                               request object. (optional, default True)
        :param kwargs: any other parameter that can be passed to
                       requests.Session.request (such as `headers`). Except:
                       'data' will be overwritten by the data in 'json' param.
                       'allow_redirects' is ignored as redirects are handled
                       by the session.

        :raises exceptions.ClientException: For connection failure, or to
                                            indicate an error response code.

        :returns: The response to the request.
        """

        headers = kwargs.setdefault('headers', dict())

        if authenticated is None:
            authenticated = bool(auth or self.auth)

        if authenticated:
            token = self.get_token(auth)

            if not token:
                raise exceptions.AuthorizationFailure("No token Available")

            headers['X-Auth-Token'] = token

        # if we are passed a fully qualified URL and an endpoint_filter we
        # should ignore the filter. This will make it easier for clients who
        # want to overrule the default endpoint_filter data added to all client
        # requests. We check fully qualified here by the presence of a host.
        url_data = urllib.parse.urlparse(url)
        if endpoint_filter and not url_data.netloc:
            base_url = self.get_endpoint(auth, **endpoint_filter)

            if not base_url:
                raise exceptions.EndpointNotFound()

            url = '%s/%s' % (base_url.rstrip('/'), url.lstrip('/'))

        if self.cert:
            kwargs.setdefault('cert', self.cert)

        if self.timeout is not None:
            kwargs.setdefault('timeout', self.timeout)

        if user_agent:
            headers['User-Agent'] = user_agent
        elif self.user_agent:
            user_agent = headers.setdefault('User-Agent', self.user_agent)
        else:
            user_agent = headers.setdefault('User-Agent', USER_AGENT)

        if self.original_ip:
            headers.setdefault('Forwarded',
                               'for=%s;by=%s' % (self.original_ip, user_agent))

        if json is not None:
            headers['Content-Type'] = 'application/json'
            kwargs['data'] = jsonutils.dumps(json)

        kwargs.setdefault('verify', self.verify)

        if requests_auth:
            kwargs['auth'] = requests_auth

        string_parts = ['curl -i']

        # NOTE(jamielennox): None means let requests do its default validation
        # so we need to actually check that this is False.
        if self.verify is False:
            string_parts.append('--insecure')

        if method:
            string_parts.extend(['-X', method])

        string_parts.append(url)

        if headers:
            for header in six.iteritems(headers):
                string_parts.append('-H "%s: %s"' % header)

        try:
            string_parts.append("-d '%s'" % kwargs['data'])
        except KeyError:
            pass

        _logger.debug('REQ: %s', ' '.join(string_parts))

        # Force disable requests redirect handling. We will manage this below.
        kwargs['allow_redirects'] = False

        if redirect is None:
            redirect = self.redirect

        resp = self._send_request(url, method, redirect, **kwargs)

        if raise_exc and resp.status_code >= 400:
            _logger.debug('Request returned failure status: %s',
                          resp.status_code)
            raise exceptions.from_response(resp, method, url)

        return resp