Example #1
0
    def _get_path(self, path, headers=None):
        """
        Retrieve a single path within the upstream registry, and return a 2-tuple of the headers and
        the response body.

        :param path: a full http path to retrieve that will be urljoin'd to the upstream registry
                     url.
        :type  path: basestring
        :param headers: headers sent in the request
        :type headers:  dict

        :return:     (headers, response body)
        :rtype:      tuple
        """
        url = urlparse.urljoin(self.registry_url, path)
        _logger.debug(_('Retrieving {0}'.format(url)))
        request = DownloadRequest(url, StringIO())
        request.headers = headers

        if self.token:
            request.headers = auth_util.update_token_auth_header(
                request.headers, self.token)

        report = self.downloader.download_one(request)

        # If the download was unauthorized, check report header, if basic auth is expected
        # retry with basic auth, otherwise attempt to get a token and try again
        if report.state == report.DOWNLOAD_FAILED:
            if report.error_report.get(
                    'response_code') == httplib.UNAUTHORIZED:
                auth_header = report.headers.get('www-authenticate')
                if auth_header is None:
                    raise IOError("401 responses are expected to "
                                  "contain authentication information")
                elif "Basic" in auth_header:
                    _logger.debug(
                        _('Download unauthorized, retrying with basic authentication'
                          ))
                    report = self.auth_downloader.download_one(request)
                else:
                    _logger.debug(
                        _('Download unauthorized, attempting to retrieve a token.'
                          ))
                    self.token = auth_util.request_token(
                        self.auth_downloader, request, auth_header, self.name)
                    request.headers = auth_util.update_token_auth_header(
                        request.headers, self.token)
                    report = self.downloader.download_one(request)

        if report.state == report.DOWNLOAD_FAILED:
            # this condition was added in case the registry would not allow to access v2 endpoint
            # but still token would be valid for other endpoints.
            # see https://pulp.plan.io/issues/2643
            if path == '/v2/' and report.error_report.get(
                    'response_code') == httplib.UNAUTHORIZED:
                pass
            else:
                self._raise_path_error(report)

        return report.headers, report.destination.getvalue()
Example #2
0
 def test_no_headers(self):
     """
     Test that when there are no existing headers, it is added.
     """
     mock_headers = auth_util.update_token_auth_header(None, "mock token")
     self.assertDictEqual(mock_headers,
                          {"Authorization": "Bearer mock token"})
Example #3
0
 def test_with_headers(self):
     """
     Test that when the headers exists, the auth token is added to it.
     """
     updated = auth_util.update_token_auth_header({"mock": "header"},
                                                  "mock token")
     self.assertDictEqual(updated, {
         "Authorization": "Bearer mock token",
         "mock": "header"
     })
Example #4
0
    def download_failed(self, report):
        """
        If the download is unauthorized, depending on the returned auth scheme, either try with
        basic auth or attempt to retrieve a token and try again.

        :param report: download report
        :type  report: nectar.report.DownloadReport
        """
        if report.error_report.get('response_code') == httplib.UNAUTHORIZED:
            request = self._requests_map[report.url]
            auth_header = report.headers.get('www-authenticate')

            if auth_header is None:
                raise IOError(
                    "401 responses are expected to contain authentication information"
                )
            if "Basic" in auth_header:
                self.downloader.session.headers = auth_util.update_basic_auth_header(
                    self.downloader.session.headers, self.basic_auth_username,
                    self.basic_auth_password)
                _logger.debug(
                    _('Download unauthorized, retrying with basic authentication'
                      ))
            else:
                token = auth_util.request_token(
                    self.parent.index_repository.auth_downloader, request,
                    auth_header, self.parent.index_repository.name)
                self.downloader.session.headers = auth_util.update_token_auth_header(
                    self.downloader.session.headers, token)
                _logger.debug(
                    "Download unauthorized, retrying with new bearer token.")

            # Events must be false or download_failed will recurse
            report = self.downloader.download_one(request, events=False)
        if report.state is report.DOWNLOAD_SUCCEEDED:
            self.download_succeeded(report)
        elif report.state is report.DOWNLOAD_FAILED:
            super(AuthDownloadStep, self).download_failed(report)
            # Docker blobs have ancestry relationships and need all blobs to function. Sync should
            # stop immediately to prevent publishing of an incomplete repository.
            os.kill(os.getpid(), signal.SIGKILL)