示例#1
0
    def __call__(
        self, url, method="GET", body=None, headers=None, timeout=None, **kwargs
    ):
        """Make an HTTP request using http.client.

        Args:
            url (str): The URI to be requested.
            method (str): The HTTP method to use for the request. Defaults
                to 'GET'.
            body (bytes): The payload / body in HTTP request.
            headers (Mapping): Request headers.
            timeout (Optional(int)): The number of seconds to wait for a
                response from the server. If not specified or if None, the
                socket global default timeout will be used.
            kwargs: Additional arguments passed throught to the underlying
                :meth:`~http.client.HTTPConnection.request` method.

        Returns:
            Response: The HTTP response.

        Raises:
            google.auth.exceptions.TransportError: If any exception occurred.
        """
        # socket._GLOBAL_DEFAULT_TIMEOUT is the default in http.client.
        if timeout is None:
            timeout = socket._GLOBAL_DEFAULT_TIMEOUT

        # http.client doesn't allow None as the headers argument.
        if headers is None:
            headers = {}

        # http.client needs the host and path parts specified separately.
        parts = urllib.parse.urlsplit(url)
        path = urllib.parse.urlunsplit(
            ("", "", parts.path, parts.query, parts.fragment)
        )

        if parts.scheme != "http":
            raise exceptions.TransportError(
                "http.client transport only supports the http scheme, {}"
                "was specified".format(parts.scheme)
            )

        connection = http_client.HTTPConnection(parts.netloc, timeout=timeout)

        try:
            _LOGGER.debug("Making request: %s %s", method, url)

            connection.request(method, path, body=body, headers=headers, **kwargs)
            response = connection.getresponse()
            return Response(response)

        except (http_client.HTTPException, socket.error) as caught_exc:
            new_exc = exceptions.TransportError(caught_exc)
            six.raise_from(new_exc, caught_exc)

        finally:
            connection.close()
    async def __call__(
        self,
        url,
        method="GET",
        body=None,
        headers=None,
        timeout=_DEFAULT_TIMEOUT,
        **kwargs,
    ):
        """
        Make an HTTP request using aiohttp.

        Args:
            url (str): The URL to be requested.
            method (Optional[str]):
                The HTTP method to use for the request. Defaults to 'GET'.
            body (Optional[bytes]):
                The payload or body in HTTP request.
            headers (Optional[Mapping[str, str]]):
                Request headers.
            timeout (Optional[int]): The number of seconds to wait for a
                response from the server. If not specified or if None, the
                requests default timeout will be used.
            kwargs: Additional arguments passed through to the underlying
                requests :meth:`requests.Session.request` method.

        Returns:
            google.auth.transport.Response: The HTTP response.

        Raises:
            google.auth.exceptions.TransportError: If any exception occurred.
        """

        try:
            if self.session is None:  # pragma: NO COVER
                self.session = aiohttp.ClientSession(
                    auto_decompress=False)  # pragma: NO COVER
            requests._LOGGER.debug("Making request: %s %s", method, url)
            response = await self.session.request(method,
                                                  url,
                                                  data=body,
                                                  headers=headers,
                                                  timeout=timeout,
                                                  **kwargs)
            return _CombinedResponse(response)

        except aiohttp.ClientError as caught_exc:
            new_exc = exceptions.TransportError(caught_exc)
            raise new_exc from caught_exc

        except asyncio.TimeoutError as caught_exc:
            new_exc = exceptions.TransportError(caught_exc)
            raise new_exc from caught_exc
示例#3
0
def get(request, path, root=_METADATA_ROOT, recursive=False):
    """Fetch a resource from the metadata server.

    Args:
        request (google.auth.transport.Request): A callable used to make
            HTTP requests.
        path (str): The resource to retrieve. For example,
            ``'instance/service-accounts/default'``.
        root (str): The full path to the metadata server root.
        recursive (bool): Whether to do a recursive query of metadata. See
            https://cloud.google.com/compute/docs/metadata#aggcontents for more
            details.

    Returns:
        Union[Mapping, str]: If the metadata server returns JSON, a mapping of
            the decoded JSON is return. Otherwise, the response content is
            returned as a string.

    Raises:
        google.auth.exceptions.TransportError: if an error occurred while
            retrieving metadata.
    """
    base_url = urlparse.urljoin(root, path)
    query_params = {}

    if recursive:
        query_params["recursive"] = "true"

    url = _helpers.update_query(base_url, query_params)

    response = request(url=url, method="GET", headers=_METADATA_HEADERS)

    if response.status == http_client.OK:
        content = _helpers.from_bytes(response.data)
        if response.headers["content-type"] == "application/json":
            try:
                return json.loads(content)
            except ValueError as caught_exc:
                new_exc = exceptions.TransportError(
                    "Received invalid JSON from the Google Compute Engine"
                    "metadata service: {:.20}".format(content))
                six.raise_from(new_exc, caught_exc)
        else:
            return content
    else:
        raise exceptions.TransportError(
            "Failed to retrieve {} from the Google Compute Engine"
            "metadata service. Status: {} Response:\n{}".format(
                url, response.status, response.data),
            response,
        )
def test_if_transient_error():
    assert retry.if_transient_error(exceptions.InternalServerError(""))
    assert retry.if_transient_error(exceptions.TooManyRequests(""))
    assert retry.if_transient_error(exceptions.ServiceUnavailable(""))
    assert retry.if_transient_error(requests.exceptions.ConnectionError(""))
    assert retry.if_transient_error(auth_exceptions.TransportError(""))
    assert not retry.if_transient_error(exceptions.InvalidArgument(""))
示例#5
0
    def test_refresh_error(self, get):
        get.side_effect = exceptions.TransportError("http error")

        with pytest.raises(exceptions.RefreshError) as excinfo:
            self.credentials.refresh(None)

        assert excinfo.match(r"http error")
示例#6
0
    def test_transport_error_from_metadata(self, get,
                                           get_service_account_info):
        get.side_effect = exceptions.TransportError("transport error")
        get_service_account_info.return_value = {"email": "*****@*****.**"}

        cred = credentials.IDTokenCredentials(
            mock.Mock(), "audience", use_metadata_identity_endpoint=True)

        with pytest.raises(exceptions.RefreshError) as excinfo:
            cred.refresh(request=mock.Mock())
        assert excinfo.match(r"transport error")
示例#7
0
    def __call__(self,
                 url,
                 method='GET',
                 body=None,
                 headers=None,
                 timeout=None,
                 **kwargs):
        """Make an HTTP request using httplib2.

        Args:
            url (str): The URI to be requested.
            method (str): The HTTP method to use for the request. Defaults
                to 'GET'.
            body (bytes): The payload / body in HTTP request.
            headers (Mapping[str, str]): Request headers.
            timeout (Optional[int]): The number of seconds to wait for a
                response from the server. This is ignored by httplib2 and will
                issue a warning.
            kwargs: Additional arguments passed throught to the underlying
                :meth:`httplib2.Http.request` method.

        Returns:
            google.auth.transport.Response: The HTTP response.

        Raises:
            google.auth.exceptions.TransportError: If any exception occurred.
        """
        if timeout is not None:
            _LOGGER.warning(
                'httplib2 transport does not support per-request timeout. '
                'Set the timeout when constructing the httplib2.Http instance.'
            )

        if self.user_agent:
            if headers.get('user-agent'):
                headers['user-agent'] = '%s %s' % (self.user_agent,
                                                   headers['user-agent'])
            else:
                headers['user-agent'] = self.user_agent

        try:
            _LOGGER.debug('Making request: %s %s', method, url)
            response, data = self.http.request(url,
                                               method=method,
                                               body=body,
                                               headers=headers,
                                               **kwargs)
            return _Response(response, data)
        # httplib2 should catch the lower http error, this is a bug and
        # needs to be fixed there.  Catch the error for the meanwhile.
        except (httplib2.HttpLib2Error, http_client.HTTPException) as exc:
            raise exceptions.TransportError(exc)
示例#8
0
def make_request(data, status=http_client.OK, headers=None, retry=False):
    response = mock.create_autospec(transport.Response, instance=True)
    response.status = status
    response.data = _helpers.to_bytes(data)
    response.headers = headers or {}

    request = mock.create_autospec(transport.Request)
    if retry:
        request.side_effect = [exceptions.TransportError(), response]
    else:
        request.return_value = response

    return request
示例#9
0
def test_get_failure_connection_failed():
    request = make_request("")
    request.side_effect = exceptions.TransportError()

    with pytest.raises(exceptions.TransportError) as excinfo:
        _metadata.get(request, PATH)

    assert excinfo.match(r"Compute Engine Metadata server unavailable")

    request.assert_called_with(
        method="GET",
        url=_metadata._METADATA_ROOT + PATH,
        headers=_metadata._METADATA_HEADERS,
    )
    assert request.call_count == 5
示例#10
0
    def _make_signing_request(self, message):
        """Makes a request to the API signBlob API."""
        message = _helpers.to_bytes(message)

        method = "POST"
        url = _SIGN_BLOB_URI.format(self._service_account_email)
        headers = {}
        body = json.dumps({"bytesToSign": base64.b64encode(message).decode("utf-8")})

        self._credentials.before_request(self._request, method, url, headers)
        response = self._request(url=url, method=method, body=body, headers=headers)

        if response.status != http_client.OK:
            raise exceptions.TransportError(
                "Error calling the IAM signBytes API: {}".format(response.data)
            )

        return json.loads(response.data.decode("utf-8"))
def test_fetch_id_token_credentials_no_cred_exists(monkeypatch):
    monkeypatch.delenv(environment_vars.CREDENTIALS, raising=False)

    with mock.patch(
        "google.auth.compute_engine._metadata.ping",
        side_effect=exceptions.TransportError(),
    ):
        with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
            id_token.fetch_id_token_credentials(ID_TOKEN_AUDIENCE)
        assert excinfo.match(
            r"Neither metadata server or valid service account credentials are found."
        )

    with mock.patch("google.auth.compute_engine._metadata.ping", return_value=False):
        with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
            id_token.fetch_id_token_credentials(ID_TOKEN_AUDIENCE)
        assert excinfo.match(
            r"Neither metadata server or valid service account credentials are found."
        )
def _sign_message(message, access_token, service_account_email):

    """Signs a message.

    :type message: str
    :param message: The message to be signed.

    :type access_token: str
    :param access_token: Access token for a service account.


    :type service_account_email: str
    :param service_account_email: E-mail address of the service account.

    :raises: :exc:`TransportError` if an `access_token` is unauthorized.

    :rtype: str
    :returns: The signature of the message.

    """
    message = _helpers._to_bytes(message)

    method = "POST"
    url = "https://iam.googleapis.com/v1/projects/-/serviceAccounts/{}:signBlob?alt=json".format(
        service_account_email
    )
    headers = {
        "Authorization": "Bearer " + access_token,
        "Content-type": "application/json",
    }
    body = json.dumps({"bytesToSign": base64.b64encode(message).decode("utf-8")})

    request = requests.Request()
    response = request(url=url, method=method, body=body, headers=headers)

    if response.status != six.moves.http_client.OK:
        raise exceptions.TransportError(
            "Error calling the IAM signBytes API: {}".format(response.data)
        )

    data = json.loads(response.data.decode("utf-8"))
    return data["signature"]
示例#13
0
    def sign_bytes(self, message):

        iam_sign_endpoint = _IAM_SIGN_ENDPOINT.format(self._target_principal)

        body = {
            "payload": base64.b64encode(message).decode("utf-8"),
            "delegates": self._delegates,
        }

        headers = {"Content-Type": "application/json"}

        authed_session = AuthorizedSession(self._source_credentials)

        response = authed_session.post(url=iam_sign_endpoint,
                                       headers=headers,
                                       json=body)

        if response.status_code != http_client.OK:
            raise exceptions.TransportError(
                "Error calling sign_bytes: {}".format(response.json()))

        return base64.b64decode(response.json()["signedBlob"])
示例#14
0
def _fetch_certs(request, certs_url):
    """Fetches certificates.

    Google-style cerificate endpoints return JSON in the format of
    ``{'key id': 'x509 certificate'}``.

    Args:
        request (google.auth.transport.Request): The object used to make
            HTTP requests.
        certs_url (str): The certificate endpoint URL.

    Returns:
        Mapping[str, str]: A mapping of public key ID to x.509 certificate
            data.
    """
    response = request(certs_url, method="GET")

    if response.status != http.client.OK:
        raise exceptions.TransportError(
            "Could not fetch certificates at {}".format(certs_url))

    return json.loads(response.data.decode("utf-8"))
示例#15
0
async def _fetch_certs(session: ClientSession, certs_url):
    """Fetches certificates.
    If non-expired certificates exists in CERT_CACHE these are returned.
    Otherwise new certs are fetched from certs_url and placed into cache.

    Google-style cerificate endpoints return JSON in the format of
    ``{'key id': 'x509 certificate'}``.

    Args:
        session (aiohhtp.Session): The object used to make
            HTTP requests.
        certs_url (str): The certificate endpoint URL.

    Returns:
        Mapping[str, str]: A mapping of public key ID to x.509 certificate
            data.
    """
    try:
        certs, expiry = CERTS_CACHE[certs_url]
    except KeyError:
        pass
    else:
        if datetime.now() > expiry:
            del CERTS_CACHE[certs_url]
        else:
            return certs

    async with session.get(certs_url) as response:

        # data = await resp.json()

        if response.status != HTTPStatus.OK:
            raise exceptions.TransportError(
                'Could not fetch certificates at {}'.format(certs_url))
        certs = await response.json()
        CERTS_CACHE[certs_url] = (certs, datetime.now() + CERTS_CACHE_TTL)
        return certs
async def test_fetch_id_token_no_cred_exists(monkeypatch):
    monkeypatch.delenv(environment_vars.CREDENTIALS, raising=False)

    with mock.patch(
            "google.auth.compute_engine._metadata.ping",
            side_effect=exceptions.TransportError(),
    ):
        with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
            request = mock.AsyncMock()
            await id_token.fetch_id_token(request,
                                          "https://pubsub.googleapis.com")
        assert excinfo.match(
            r"Neither metadata server or valid service account credentials are found."
        )

    with mock.patch("google.auth.compute_engine._metadata.ping",
                    return_value=False):
        with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
            request = mock.AsyncMock()
            await id_token.fetch_id_token(request,
                                          "https://pubsub.googleapis.com")
        assert excinfo.match(
            r"Neither metadata server or valid service account credentials are found."
        )
示例#17
0
def test_ping_failure_connection_failed():
    request = make_request("")
    request.side_effect = exceptions.TransportError()

    assert not _metadata.ping(request)
@mock.patch('google.auth.compute_engine._metadata.ping',
            return_value=False,
            autospec=True)
def test__get_gce_credentials_no_ping(ping_mock):
    credentials, project_id = _default._get_gce_credentials()

    assert credentials is None
    assert project_id is None


@mock.patch('google.auth.compute_engine._metadata.ping',
            return_value=True,
            autospec=True)
@mock.patch('google.auth.compute_engine._metadata.get_project_id',
            side_effect=exceptions.TransportError(),
            autospec=True)
def test__get_gce_credentials_no_project_id(get_mock, ping_mock):
    credentials, project_id = _default._get_gce_credentials()

    assert isinstance(credentials, compute_engine.Credentials)
    assert project_id is None


@mock.patch('google.auth.compute_engine._metadata.ping',
            return_value=False,
            autospec=True)
def test__get_gce_credentials_explicit_request(ping_mock):
    _default._get_gce_credentials(mock.sentinel.request)
    ping_mock.assert_called_with(request=mock.sentinel.request)
示例#19
0
def get(
    request, path, root=_METADATA_ROOT, params=None, recursive=False, retry_count=5
):
    """Fetch a resource from the metadata server.

    Args:
        request (google.auth.transport.Request): A callable used to make
            HTTP requests.
        path (str): The resource to retrieve. For example,
            ``'instance/service-accounts/default'``.
        root (str): The full path to the metadata server root.
        params (Optional[Mapping[str, str]]): A mapping of query parameter
            keys to values.
        recursive (bool): Whether to do a recursive query of metadata. See
            https://cloud.google.com/compute/docs/metadata#aggcontents for more
            details.
        retry_count (int): How many times to attempt connecting to metadata
            server using above timeout.

    Returns:
        Union[Mapping, str]: If the metadata server returns JSON, a mapping of
            the decoded JSON is return. Otherwise, the response content is
            returned as a string.

    Raises:
        google.auth.exceptions.TransportError: if an error occurred while
            retrieving metadata.
    """
    base_url = urlparse.urljoin(root, path)
    query_params = {} if params is None else params

    if recursive:
        query_params["recursive"] = "true"

    url = _helpers.update_query(base_url, query_params)

    retries = 0
    while retries < retry_count:
        try:
            response = request(url=url, method="GET", headers=_METADATA_HEADERS)
            break

        except exceptions.TransportError as e:
            _LOGGER.warning(
                "Compute Engine Metadata server unavailable on "
                "attempt %s of %s. Reason: %s",
                retries + 1,
                retry_count,
                e,
            )
            retries += 1
    else:
        raise exceptions.TransportError(
            "Failed to retrieve {} from the Google Compute Engine"
            "metadata service. Compute Engine Metadata server unavailable".format(url)
        )

    if response.status == http.client.OK:
        content = _helpers.from_bytes(response.data)
        if response.headers["content-type"] == "application/json":
            try:
                return json.loads(content)
            except ValueError as caught_exc:
                new_exc = exceptions.TransportError(
                    "Received invalid JSON from the Google Compute Engine"
                    "metadata service: {:.20}".format(content)
                )
                raise new_exc from caught_exc
        else:
            return content
    else:
        raise exceptions.TransportError(
            "Failed to retrieve {} from the Google Compute Engine"
            "metadata service. Status: {} Response:\n{}".format(
                url, response.status, response.data
            ),
            response,
        )
示例#20
0
def test_ping_failure_connection_failed(mock_request):
    request_mock = mock_request('')
    request_mock.side_effect = exceptions.TransportError()

    assert not _metadata.ping(request_mock)
示例#21
0
@mock.patch(
    'google.auth.compute_engine._metadata.ping', return_value=False,
    autospec=True)
def test__get_gce_credentials_no_ping(unused_ping):
    credentials, project_id = _default._get_gce_credentials()

    assert credentials is None
    assert project_id is None


@mock.patch(
    'google.auth.compute_engine._metadata.ping', return_value=True,
    autospec=True)
@mock.patch(
    'google.auth.compute_engine._metadata.get_project_id',
    side_effect=exceptions.TransportError(), autospec=True)
def test__get_gce_credentials_no_project_id(unused_get, unused_ping):
    credentials, project_id = _default._get_gce_credentials()

    assert isinstance(credentials, compute_engine.Credentials)
    assert project_id is None


def test__get_gce_credentials_no_compute_engine():
    import sys
    with mock.patch.dict('sys.modules'):
        sys.modules['google.auth.compute_engine'] = None
        credentials, project_id = _default._get_gce_credentials()
        assert credentials is None
        assert project_id is None