Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

cleanup: minor code cleanup #1589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions 30 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,36 @@

[1]: https://pypi.org/project/google-auth/#history

## [2.34.0](https://github.com/googleapis/google-auth-library-python/compare/v2.33.0...v2.34.0) (2024-08-13)


### Features

* **auth:** Update get_client_ssl_credentials to support X.509 workload certs ([#1558](https://github.com/googleapis/google-auth-library-python/issues/1558)) ([18c2ec1](https://github.com/googleapis/google-auth-library-python/commit/18c2ec1b571d506c0dbcffc483aa5e7b95e1b246))


### Bug Fixes

* Retry token request on retryable status code ([#1563](https://github.com/googleapis/google-auth-library-python/issues/1563)) ([f858a15](https://github.com/googleapis/google-auth-library-python/commit/f858a151cb7e29d34578e03c9e3fd4110c6bc258))

## [2.33.0](https://github.com/googleapis/google-auth-library-python/compare/v2.32.0...v2.33.0) (2024-08-06)


### Features

* Implement async `StaticCredentials` using access tokens ([#1559](https://github.com/googleapis/google-auth-library-python/issues/1559)) ([dc17dfc](https://github.com/googleapis/google-auth-library-python/commit/dc17dfc3fb65c87f2912300f0d11f79781240e78))
* Implement base classes for credentials and request sessions ([#1551](https://github.com/googleapis/google-auth-library-python/issues/1551)) ([036dac4](https://github.com/googleapis/google-auth-library-python/commit/036dac43018b8cc26b5608e1bb21d6e3ee62a282))


### Bug Fixes

* **metadata:** Enhance retry logic for metadata server access in _metadata.py ([#1545](https://github.com/googleapis/google-auth-library-python/issues/1545)) ([61c2432](https://github.com/googleapis/google-auth-library-python/commit/61c24321e52f6e017eecee211e11260d621c909b))


### Documentation

* Update argument for Credentials initialization ([#1557](https://github.com/googleapis/google-auth-library-python/issues/1557)) ([40b9ed9](https://github.com/googleapis/google-auth-library-python/commit/40b9ed91a6b01948561cfc71edaaabdd7f362f17))

## [2.32.0](https://github.com/googleapis/google-auth-library-python/compare/v2.31.0...v2.32.0) (2024-07-08)


Expand Down
8 changes: 2 additions & 6 deletions 8 google/auth/aio/transport/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,9 @@
"""Sequence[int]: HTTP status codes indicating a request can be retried.
"""

DEFAULT_REFRESH_STATUS_CODES = google.auth.transport.DEFAULT_REFRESH_STATUS_CODES
"""Sequence[int]: Which HTTP status code indicate that credentials should be
refreshed.
"""

DEFAULT_MAX_REFRESH_ATTEMPTS = 3
"""int: How many times to refresh the credentials and retry a request."""
DEFAULT_MAX_RETRY_ATTEMPTS = 3
"""int: How many times to retry a request."""


class Response(metaclass=abc.ABCMeta):
Expand Down
14 changes: 8 additions & 6 deletions 14 google/auth/aio/transport/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Transport adapter for AIOHTTP Requests.
"""Transport adapter for Asynchronous HTTP Requests based on aiohttp.
"""

import asyncio
Expand All @@ -32,7 +32,7 @@

class Response(transport.Response):
"""
Represents an HTTP response and its data. It is returned by ``google.auth.aio.transport.sessions.AuthorizedSession``.
Represents an HTTP response and its data. It is returned by ``google.auth.aio.transport.sessions.AsyncAuthorizedSession``.

Args:
response (aiohttp.ClientResponse): An instance of aiohttp.ClientResponse.
Expand All @@ -58,7 +58,9 @@ def headers(self) -> Mapping[str, str]:
@_helpers.copy_docstring(transport.Response)
async def content(self, chunk_size: int = 1024) -> AsyncGenerator[bytes, None]:
try:
async for chunk in self._response.content.iter_chunked(chunk_size): # pragma: no branch
async for chunk in self._response.content.iter_chunked(
chunk_size
): # pragma: no branch
yield chunk
except aiohttp.ClientPayloadError as exc:
raise exceptions.ResponseError(
Expand All @@ -81,8 +83,8 @@ class Request(transport.Request):
"""Asynchronous Requests request adapter.

This class is used internally for making requests using aiohttp
in a consistent way. If you use :class:`AuthorizedSession` you do not need
to construct or use this class directly.
in a consistent way. If you use :class:`google.auth.aio.transport.sessions.AsyncAuthorizedSession`
you do not need to construct or use this class directly.

This class can be useful if you want to configure a Request callable
with a custom ``aiohttp.ClientSession`` in :class:`AuthorizedSession` or if
Expand All @@ -98,7 +100,7 @@ class Request(transport.Request):
# Custom aiohttp Session Example:
session = session=aiohttp.ClientSession(auto_decompress=False)
request = google.auth.aio.transport.aiohttp.Request(session=session)
auth_sesion = google.auth.aio.transport.sessions.AuthorizedSession(auth_request=request)
auth_sesion = google.auth.aio.transport.sessions.AsyncAuthorizedSession(auth_request=request)

Args:
session (aiohttp.ClientSession): An instance :class:`aiohttp.ClientSession` used
Expand Down
6 changes: 3 additions & 3 deletions 6 google/auth/aio/transport/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async def with_timeout(coro):
_remaining_time()


class AuthorizedSession:
class AsyncAuthorizedSession:
"""This is an asynchronous implementation of :class:`google.auth.requests.AuthorizedSession` class.
We utilize an instance of a class that implements :class:`google.auth.aio.transport.Request` configured
by the caller or otherwise default to `google.auth.aio.transport.aiohttp.Request` if the external aiohttp
Expand All @@ -89,7 +89,7 @@ class AuthorizedSession:
import aiohttp
from google.auth.aio.transport import sessions

async with sessions.AuthorizedSession(credentials) as authed_session:
async with sessions.AsyncAuthorizedSession(credentials) as authed_session:
response = await authed_session.request(
'GET', 'https://www.googleapis.com/storage/v1/b')

Expand Down Expand Up @@ -172,7 +172,7 @@ async def request(
"""

retries = _exponential_backoff.AsyncExponentialBackoff(
total_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS
total_attempts=transport.DEFAULT_MAX_RETRY_ATTEMPTS
)
async with timeout_guard(max_allowed_time) as with_timeout:
await with_timeout(
Expand Down
13 changes: 12 additions & 1 deletion 13 google/auth/compute_engine/_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from google.auth import environment_vars
from google.auth import exceptions
from google.auth import metrics
from google.auth import transport
from google.auth._exponential_backoff import ExponentialBackoff

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -204,7 +205,17 @@ def get(
for attempt in backoff:
try:
response = request(url=url, method="GET", headers=headers_to_use)
break
if response.status in transport.DEFAULT_RETRYABLE_STATUS_CODES:
_LOGGER.warning(
"Compute Engine Metadata server unavailable on "
"attempt %s of %s. Response status: %s",
attempt,
retry_count,
response.status,
)
continue
else:
break

except exceptions.TransportError as e:
_LOGGER.warning(
Expand Down
40 changes: 29 additions & 11 deletions 40 google/auth/transport/_mtls_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from google.auth import exceptions

CONTEXT_AWARE_METADATA_PATH = "~/.secureConnect/context_aware_metadata.json"
_CERTIFICATE_CONFIGURATION_DEFAULT_PATH = "~/.config/gcloud/certificate_config.json"
CERTIFICATE_CONFIGURATION_DEFAULT_PATH = "~/.config/gcloud/certificate_config.json"
_CERTIFICATE_CONFIGURATION_ENV = "GOOGLE_API_CERTIFICATE_CONFIG"
_CERT_PROVIDER_COMMAND = "cert_provider_command"
_CERT_REGEX = re.compile(
Expand All @@ -48,21 +48,21 @@
)


def _check_dca_metadata_path(metadata_path):
"""Checks for context aware metadata. If it exists, returns the absolute path;
def _check_config_path(config_path):
"""Checks for config file path. If it exists, returns the absolute path with user expansion;
otherwise returns None.

Args:
metadata_path (str): context aware metadata path.
config_path (str): The config file path for either context_aware_metadata.json or certificate_config.json for example

Returns:
str: absolute path if exists and None otherwise.
"""
metadata_path = path.expanduser(metadata_path)
if not path.exists(metadata_path):
_LOGGER.debug("%s is not found, skip client SSL authentication.", metadata_path)
config_path = path.expanduser(config_path)
if not path.exists(config_path):
_LOGGER.debug("%s is not found.", config_path)
return None
return metadata_path
return config_path


def _load_json_file(path):
Expand Down Expand Up @@ -136,7 +136,7 @@ def _get_cert_config_path(certificate_config_path=None):
if env_path is not None and env_path != "":
certificate_config_path = env_path
else:
certificate_config_path = _CERTIFICATE_CONFIGURATION_DEFAULT_PATH
certificate_config_path = CERTIFICATE_CONFIGURATION_DEFAULT_PATH

certificate_config_path = path.expanduser(certificate_config_path)
if not path.exists(certificate_config_path):
Expand Down Expand Up @@ -279,14 +279,22 @@ def _run_cert_provider_command(command, expect_encrypted_key=False):
def get_client_ssl_credentials(
generate_encrypted_key=False,
context_aware_metadata_path=CONTEXT_AWARE_METADATA_PATH,
certificate_config_path=CERTIFICATE_CONFIGURATION_DEFAULT_PATH,
):
"""Returns the client side certificate, private key and passphrase.

We look for certificates and keys with the following order of priority:
1. Certificate and key specified by certificate_config.json.
Currently, only X.509 workload certificates are supported.
2. Certificate and key specified by context aware metadata (i.e. SecureConnect).

Args:
generate_encrypted_key (bool): If set to True, encrypted private key
and passphrase will be generated; otherwise, unencrypted private key
will be generated and passphrase will be None.
will be generated and passphrase will be None. This option only
affects keys obtained via context_aware_metadata.json.
context_aware_metadata_path (str): The context_aware_metadata.json file path.
certificate_config_path (str): The certificate_config.json file path.

Returns:
Tuple[bool, bytes, bytes, bytes]:
Expand All @@ -297,7 +305,17 @@ def get_client_ssl_credentials(
google.auth.exceptions.ClientCertError: if problems occurs when getting
the cert, key and passphrase.
"""
metadata_path = _check_dca_metadata_path(context_aware_metadata_path)

# 1. Check for certificate config json.
cert_config_path = _check_config_path(certificate_config_path)
if cert_config_path:
# Attempt to retrieve X.509 Workload cert and key.
cert, key = _get_workload_cert_and_key(cert_config_path)
if cert and key:
return True, cert, key, None

# 2. Check for context aware metadata json
metadata_path = _check_config_path(context_aware_metadata_path)

if metadata_path:
metadata_json = _load_json_file(metadata_path)
Expand Down
2 changes: 1 addition & 1 deletion 2 google/auth/transport/grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def __init__(self):
self._is_mtls = False
else:
# Load client SSL credentials.
metadata_path = _mtls_helper._check_dca_metadata_path(
metadata_path = _mtls_helper._check_config_path(
_mtls_helper.CONTEXT_AWARE_METADATA_PATH
)
self._is_mtls = metadata_path is not None
Expand Down
17 changes: 13 additions & 4 deletions 17 google/auth/transport/mtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ def has_default_client_cert_source():
Returns:
bool: indicating if the default client cert source exists.
"""
metadata_path = _mtls_helper._check_dca_metadata_path(
_mtls_helper.CONTEXT_AWARE_METADATA_PATH
)
return metadata_path is not None
if (
_mtls_helper._check_config_path(_mtls_helper.CONTEXT_AWARE_METADATA_PATH)
is not None
):
return True
if (
_mtls_helper._check_config_path(
_mtls_helper.CERTIFICATE_CONFIGURATION_DEFAULT_PATH
)
is not None
):
return True
return False


def default_client_cert_source():
Expand Down
2 changes: 1 addition & 1 deletion 2 google/auth/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "2.32.0"
__version__ = "2.34.0"
4 changes: 1 addition & 3 deletions 4 setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
"pyopenssl": ["pyopenssl>=20.0.0", "cryptography>=38.0.3"],
"requests": "requests >= 2.20.0, < 3.0.0.dev0",
"reauth": "pyu2f>=0.1.5",
# Enterprise cert only works for OpenSSL 1.1.1. Newer versions of these
# dependencies are built with OpenSSL 3.0 so we need to fix the version.
"enterprise_cert": ["cryptography==36.0.2", "pyopenssl==22.0.0"],
"enterprise_cert": ["cryptography", "pyopenssl"],
}

with io.open("README.rst", "r") as fh:
Expand Down
Binary file modified BIN +0 Bytes (100%) system_tests/secrets.tar.enc
Binary file not shown.
2 changes: 1 addition & 1 deletion 2 testing/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ grpcio
pytest-asyncio; python_version > '3.0'
aioresponses; python_version > '3.0'
asynctest; python_version > '3.0'
aiohttp; python_version > '3.0'
aiohttp < 3.10.0; python_version > '3.0'
68 changes: 68 additions & 0 deletions 68 tests/compute_engine/test__metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,74 @@ def test_get_universe_domain_not_found():
assert universe_domain == "googleapis.com"


def test_get_universe_domain_retryable_error_failure():
# Test that if the universe domain endpoint returns a retryable error
# we should retry.
#
# In this case, the error persists, and we still fail after retrying.
request = make_request("too many requests", status=http_client.TOO_MANY_REQUESTS)

with pytest.raises(exceptions.TransportError) as excinfo:
_metadata.get_universe_domain(request)

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

request.assert_called_with(
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe_domain",
headers=_metadata._METADATA_HEADERS,
)
assert request.call_count == 5


def test_get_universe_domain_retryable_error_success():
# Test that if the universe domain endpoint returns a retryable error
# we should retry.
#
# In this case, the error is temporary, and we succeed after retrying.
request_error = make_request(
"too many requests", status=http_client.TOO_MANY_REQUESTS
)
request_ok = make_request(
"fake_universe_domain", headers={"content-type": "text/plain"}
)

class _RequestErrorOnce:
"""This class forwards the request parameters to `request_error` once.

All subsequent calls are forwarded to `request_ok`.
"""

def __init__(self, request_error, request_ok):
self._request_error = request_error
self._request_ok = request_ok
self._call_index = 0

def request(self, *args, **kwargs):
if self._call_index == 0:
self._call_index += 1
return self._request_error(*args, **kwargs)

return self._request_ok(*args, **kwargs)

request = _RequestErrorOnce(request_error, request_ok).request

universe_domain = _metadata.get_universe_domain(request)

request_error.assert_called_once_with(
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe_domain",
headers=_metadata._METADATA_HEADERS,
)
request_ok.assert_called_once_with(
method="GET",
url=_metadata._METADATA_ROOT + "universe/universe_domain",
headers=_metadata._METADATA_HEADERS,
)

assert universe_domain == "fake_universe_domain"


def test_get_universe_domain_other_error():
# Test that if the universe domain endpoint returns an error other than 404
# we should throw the error
Expand Down
2 changes: 1 addition & 1 deletion 2 tests/transport/aio/test_aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async def test_request_call_success(self, aiohttp_request):
assert response.headers == {"Content-Type": "application/json"}
content = b"".join([chunk async for chunk in response.content()])
assert content == b"Cavefish have no sight."

async def test_request_call_success_with_provided_session(self):
mock_session = aiohttp.ClientSession()
request = auth_aiohttp.Request(mock_session)
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.