Exemple #1
0
    def authorize(self, transport, **kwargs):
        """Obtain access information from an OpenStack Identity Service."""
        headers = {'Accept': 'application/json'}
        body = {'auth': {'identity': {}}}
        ident = body['auth']['identity']

        for method in self.auth_methods:
            name, auth_data = method.get_auth_data(transport, self, headers)
            ident.setdefault('methods', []).append(name)
            ident[name] = auth_data

        if not ident:
            raise exceptions.AuthorizationFailure('Authentication method '
                                                  'required (e.g. password)')

        mutual_exclusion = [
            bool(self.domain_id or self.domain_name),
            bool(self.project_id or self.project_name),
            bool(self.trust_id)
        ]

        if sum(mutual_exclusion) > 1:
            raise exceptions.AuthorizationFailure('Authentication cannot be '
                                                  'scoped to multiple '
                                                  'targets. Pick one of: '
                                                  'project, domain or trust')

        if self.domain_id:
            body['auth']['scope'] = {'domain': {'id': self.domain_id}}
        elif self.domain_name:
            body['auth']['scope'] = {'domain': {'name': self.domain_name}}
        elif self.project_id:
            body['auth']['scope'] = {'project': {'id': self.project_id}}
        elif self.project_name:
            scope = body['auth']['scope'] = {'project': {}}
            scope['project']['name'] = self.project_name

            if self.project_domain_id:
                scope['project']['domain'] = {'id': self.project_domain_id}
            elif self.project_domain_name:
                scope['project']['domain'] = {'name': self.project_domain_name}
        elif self.trust_id:
            body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}}

        # NOTE(jamielennox): we add nocatalog here rather than in token_url
        # directly as some federation plugins require the base token_url
        token_url = self.token_url
        if not self.include_catalog:
            token_url += '?nocatalog'

        _logger.debug('Making authentication request to %s', token_url)
        resp = transport.post(token_url, json=body, headers=headers)

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

        return access.AccessInfoV3(resp.headers['X-Subject-Token'],
                                   **resp_data)
Exemple #2
0
    def download(self, session):
        """Download the data contained in an image"""
        # TODO(briancurtin): This method should probably offload the get
        # operation into another thread or something of that nature.
        url = utils.urljoin(self.base_path, self.id, 'file')
        resp = session.get(url, endpoint_filter=self.service)

        # See the following bug report for details on why the checksum
        # code may sometimes depend on a second GET call.
        # https://bugs.launchpad.net/python-openstacksdk/+bug/1619675
        checksum = resp.headers.get("Content-MD5")

        if checksum is None:
            # If we don't receive the Content-MD5 header with the download,
            # make an additional call to get the image details and look at
            # the checksum attribute.
            details = self.get(session)
            checksum = details.checksum

        if checksum is not None:
            digest = hashlib.md5(resp.content).hexdigest()
            if digest != checksum:
                raise exceptions.InvalidResponse(
                    "checksum mismatch: %s != %s" % (checksum, digest))
        else:
            _logger.warn("Unable to verify the integrity of image %s" %
                         (self.id))

        return resp.content
Exemple #3
0
    def download(self, session, stream=False, output=None, chunk_size=1024):
        """Download the data contained in an image"""
        # TODO(briancurtin): This method should probably offload the get
        # operation into another thread or something of that nature.
        url = utils.urljoin(self.base_path, self.id, 'file')
        resp = session.get(url, stream=stream)

        # See the following bug report for details on why the checksum
        # code may sometimes depend on a second GET call.
        # https://storyboard.openstack.org/#!/story/1619675
        checksum = resp.headers.get("Content-MD5")

        if checksum is None:
            # If we don't receive the Content-MD5 header with the download,
            # make an additional call to get the image details and look at
            # the checksum attribute.
            details = self.fetch(session)
            checksum = details.checksum

        if output:
            try:
                # In python 2 we might get StringIO - delete it as soon as
                # py2 support is dropped
                if isinstance(output, io.IOBase) \
                        or isinstance(output, six.StringIO):
                    for chunk in resp.iter_content(chunk_size=chunk_size):
                        output.write(chunk)
                else:
                    with open(output, 'wb') as fd:
                        for chunk in resp.iter_content(chunk_size=chunk_size):
                            fd.write(chunk)
                return resp
            except Exception as e:
                raise exceptions.SDKException("Unable to download image: %s" %
                                              e)
        # if we are returning the repsonse object, ensure that it
        # has the content-md5 header so that the caller doesn't
        # need to jump through the same hoops through which we
        # just jumped.
        if stream:
            resp.headers['content-md5'] = checksum
            return resp

        if checksum is not None:
            digest = hashlib.md5(resp.content).hexdigest()
            if digest != checksum:
                raise exceptions.InvalidResponse(
                    "checksum mismatch: %s != %s" % (checksum, digest))
        else:
            session.log.warn("Unable to verify the integrity of image %s" %
                             (self.id))

        return resp
    def test_claim_messages_no_invalid_response(self):
        sess = mock.Mock()
        resp = mock.Mock()
        resp.status_code = 204
        sess.post = mock.Mock(
            side_effect=exceptions.InvalidResponse(response=resp))
        sot = claim.Claim()

        messages = list(sot.claim_messages(
            sess, claim.Claim.new(client_id=CLIENT, queue_name=QUEUE, **FAKE)))

        self.assertEqual(0, len(messages))
Exemple #5
0
    def download(self, session):
        """Download the data contained in an image"""
        # TODO(briancurtin): This method should probably offload the get
        # operation into another thread or something of that nature.
        url = utils.urljoin(self.base_path, self.id, 'file')
        resp = session.get(url, endpoint_filter=self.service)

        checksum = resp.headers["Content-MD5"]
        digest = hashlib.md5(resp.content).hexdigest()
        if digest != checksum:
            raise exceptions.InvalidResponse("checksum mismatch")

        return resp.content
Exemple #6
0
    def test_claim_messages_invalid_response(self):
        sess = mock.Mock()
        resp = mock.Mock()
        resp.status_code = 400
        sess.post = mock.Mock(side_effect=exceptions.InvalidResponse(
            response=resp))
        sot = claim.Claim()

        try:
            list(
                sot.claim_messages(
                    sess, claim.Claim.new(client=CLIENT, queue=QUEUE, **FAKE)))
        except exceptions.InvalidResponse as e:
            self.assertEqual(400, e.response.status_code)
Exemple #7
0
    def authorize(self, transport, **kwargs):
        headers = {'Accept': 'application/json'}
        url = self.auth_url.rstrip('/') + '/tokens'
        params = {'auth': self.get_auth_data(headers)}

        if self.tenant_id:
            params['auth']['tenantId'] = self.tenant_id
        elif self.tenant_name:
            params['auth']['tenantName'] = self.tenant_name
        if self.trust_id:
            params['auth']['trust_id'] = self.trust_id

        _logger.debug('Making authentication request to %s', url)
        resp = transport.post(url, json=params, headers=headers)

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

        return access.AccessInfoV2(**resp_data)
    def download(self, session, stream=False):
        """Download the data contained in an image"""
        # TODO(briancurtin): This method should probably offload the get
        # operation into another thread or something of that nature.
        url = utils.urljoin(self.base_path, self.id, 'file')
        resp = session.get(url, stream=stream)

        # See the following bug report for details on why the checksum
        # code may sometimes depend on a second GET call.
        # https://storyboard.openstack.org/#!/story/1619675
        checksum = resp.headers.get("Content-MD5")

        if checksum is None:
            # If we don't receive the Content-MD5 header with the download,
            # make an additional call to get the image details and look at
            # the checksum attribute.
            details = self.fetch(session)
            checksum = details.checksum

        # if we are returning the repsonse object, ensure that it
        # has the content-md5 header so that the caller doesn't
        # need to jump through the same hoops through which we
        # just jumped.
        if stream:
            resp.headers['content-md5'] = checksum
            return resp

        if checksum is not None:
            digest = hashlib.md5(resp.content).hexdigest()
            if digest != checksum:
                raise exceptions.InvalidResponse(
                    "checksum mismatch: %s != %s" % (checksum, digest))
        else:
            _logger.warn("Unable to verify the integrity of image %s" %
                         (self.id))

        return resp.content
    def request(self, method, url, redirect=None, **kwargs):
        """Send a request

        Perform an HTTP request. The following arguments differ from
        ``requests.Session``:

        :param string method: Request HTTP method
        :param string url: Request URL
        :param boolean/integer redirect: (integer) The maximum number of
                                         redirections followed in a request.
                                         (boolean) No redirections if False,
                                         requests.Session handles redirection
                                         if True. (optional)

        The following additional keyword arguments are supported:

        :param object json: Request body to be encoded as JSON
                            Overwrites ``data`` argument if present
        :param string accept: Set the ``Accept`` header; overwrites any value
                              that may be in the headers dict. Header is
                              omitted if ``None``.
        :param string user_agent: Prepend an additional value to the existing
                                  ``User-Agent`` header.

        Remaining keyword arguments from requests.Session.request() supported
        """

        headers = kwargs.setdefault('headers', {})

        # JSON-encode the data in json arg if present
        # Overwrites any existing 'data' value
        json_data = kwargs.pop('json', None)
        if json_data is not None:
            kwargs['data'] = json.dumps(json_data)
            headers['Content-Type'] = JSON

        # Prepend the caller's user_agent to User-Agent header if included,
        # or use the default that this transport was created with.
        # Note: Only attempt to work with strings and avoid needlessly
        # concatenating an empty string.
        user_agent = kwargs.pop('user_agent', None)
        if isinstance(user_agent, six.string_types) and user_agent != '':
            headers['User-Agent'] = '%s %s' % (user_agent, self._user_agent)
        elif 'User-Agent' in headers:
            # If they've specified their own headers with a User-Agent,
            # use that directly.
            pass
        else:
            headers.setdefault('User-Agent', self._user_agent)

        if redirect is None:
            redirect = self._redirect

        if isinstance(redirect, bool) and redirect:
            # Fall back to requests redirect handling
            kwargs['allow_redirects'] = True
        else:
            # Force disable requests redirect handling, we will manage
            # redirections below
            kwargs['allow_redirects'] = False
        if 'accept' in kwargs:
            accept = kwargs.pop('accept')
        else:
            accept = self._accept
        if accept:
            headers.setdefault('Accept', accept)

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

        try:
            resp.raise_for_status()
        except requests.RequestException as e:
            if resp.status_code == 404:
                exc_type = exceptions.NotFoundException
            else:
                exc_type = exceptions.HttpException

            raise exc_type(six.text_type(e),
                           details=self._parse_error_response(resp),
                           status_code=resp.status_code)

        if accept == JSON:
            try:
                resp.body = resp.json()
            except ValueError as e:
                # this may be simplejson.decode.JSONDecodeError
                # Re-raise into our own exception
                raise exceptions.InvalidResponse(response=resp)

        return resp
Exemple #10
0
def _verify_checksum(md5, checksum):
    if checksum:
        digest = md5.hexdigest()
        if digest != checksum:
            raise exceptions.InvalidResponse(
                "checksum mismatch: %s != %s" % (checksum, digest))
Exemple #11
0
    def request(self, method, url, redirect=None, **kwargs):
        """Send a request

        :param string method: Request HTTP method
        :param string url: Request URL
        :param boolean/integer redirect: (integer) The maximum number of
                                         redirections followed in a request.
                                         (boolean) No redirections if False,
                                         requests.Session handles redirection
                                         if True. (optional)

        The following additional kw args are supported:
        :param object json: Request body to be encoded as JSON
                            Overwrites ``data`` argument if present
        :param string accept: Set the ``Accept`` header; overwrites
                                  any value that may be in the headers dict.
                                  Header is omitted if ``None``.
        :param string user_agent: Set the ``User-Agent`` header; overwrites
                                  any value that may be in the headers dict.
                                  Header is omitted if ``None``.

        Remaining kw args from requests.Session.request() supported

        """

        headers = kwargs.setdefault('headers', {})

        # JSON-encode the data in json arg if present
        # Overwrites any existing 'data' value
        json_data = kwargs.pop('json', None)
        if json_data is not None:
            kwargs['data'] = json.dumps(json_data)
            headers['Content-Type'] = JSON

        # Set User-Agent header if user_agent arg included, or
        # fall through the default chain as described above
        if 'user_agent' in kwargs:
            headers['User-Agent'] = kwargs.pop('user_agent')
        elif self._user_agent:
            headers.setdefault('User-Agent', self._user_agent)
        else:
            headers.setdefault('User-Agent', DEFAULT_USER_AGENT)

        if redirect is None:
            redirect = self._redirect

        if isinstance(redirect, bool) and redirect:
            # Fall back to requests redirect handling
            kwargs['allow_redirects'] = True
        else:
            # Force disable requests redirect handling, we will manage
            # redirections below
            kwargs['allow_redirects'] = False
        if 'accept' in kwargs:
            accept = kwargs.pop('accept')
        else:
            accept = self._accept
        if accept:
            headers.setdefault('Accept', accept)

        self._log_request(method, url, **kwargs)

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

        self._log_response(resp)

        try:
            resp.raise_for_status()
        except requests.RequestException as e:
            raise exceptions.HttpException(six.text_type(e), details=resp.text)
        if accept == JSON:
            try:
                resp.body = resp.json()
            except ValueError as e:
                # this may be simplejson.decode.JSONDecodeError
                # Re-raise into our own exception
                raise exceptions.InvalidResponse(response=resp.text)

        return resp