Skip to content
This repository was archived by the owner on Mar 6, 2026. It is now read-only.

Commit 4596553

Browse files
authored
Merge branch 'main' into ai-gsutil-migration-b2401cd84236475098a848cc7711313e
2 parents c3e3fd8 + 6aab8bc commit 4596553

15 files changed

Lines changed: 727 additions & 82 deletions

File tree

.github/CODEOWNERS

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,27 @@
44
# For syntax help see:
55
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
66

7-
# The @googleapis/googleapis-auth and @googleapis/python-core-client-libraries is the default owner for changes in this repo
8-
* @googleapis/googleapis-auth @googleapis/python-core-client-libraries
9-
google/auth/_default.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
10-
google/auth/aws.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
11-
google/auth/credentials.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
12-
google/auth/downscoped.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
13-
google/auth/external_account.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
14-
google/auth/external_account_authorized_user.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
15-
google/auth/identity_pool.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
16-
google/auth/pluggable.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
17-
google/auth/sts.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
18-
google/auth/impersonated_credentials.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
19-
tests/test__default.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
20-
tests/test_aws.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
21-
tests/test_credentials.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
22-
tests/test_downscoped.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
23-
tests/test_external_account.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
24-
tests/test_external_account_authorized_user.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
25-
tests/test_identity_pool.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
26-
tests/test_pluggable.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
27-
tests/test_sts.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
28-
tests/test_impersonated_credentials.py @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-core-client-libraries
29-
/samples/ @googleapis/googleapis-auth @googleapis/aion-sdk @googleapis/python-samples-owners @googleapis/python-core-client-libraries
7+
# The @googleapis/googleapis-auth and @googleapis/cloud-sdk-python-team is the default owner for changes in this repo
8+
* @googleapis/googleapis-auth @googleapis/cloud-sdk-python-team
9+
google/auth/_default.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
10+
google/auth/aws.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
11+
google/auth/credentials.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
12+
google/auth/downscoped.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
13+
google/auth/external_account.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
14+
google/auth/external_account_authorized_user.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
15+
google/auth/identity_pool.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
16+
google/auth/pluggable.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
17+
google/auth/sts.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
18+
google/auth/impersonated_credentials.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
19+
tests/test__default.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
20+
tests/test_aws.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
21+
tests/test_credentials.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
22+
tests/test_downscoped.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
23+
tests/test_external_account.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
24+
tests/test_external_account_authorized_user.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
25+
tests/test_identity_pool.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
26+
tests/test_pluggable.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
27+
tests/test_sts.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
28+
tests/test_impersonated_credentials.py @googleapis/googleapis-auth @googleapis/aion-team @googleapis/cloud-sdk-python-team
29+
/samples/ @googleapis/googleapis-auth @googleapis/aion-team @googleapis/python-samples-owners @googleapis/cloud-sdk-python-team
3030
system_tests/secrets.tar.enc # Remove noise from test creds.

google/auth/aio/transport/aiohttp.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Transport adapter for Asynchronous HTTP Requests based on aiohttp.
16-
"""
15+
"""Transport adapter for Asynchronous HTTP Requests based on aiohttp."""
1716

1817
import asyncio
1918
import logging
20-
from typing import AsyncGenerator, Mapping, Optional
19+
from typing import AsyncGenerator, Mapping, Optional, TYPE_CHECKING, Union
2120

2221
try:
2322
import aiohttp # type: ignore
@@ -31,6 +30,15 @@
3130
from google.auth.aio import _helpers as _helpers_async
3231
from google.auth.aio import transport
3332

33+
if TYPE_CHECKING: # pragma: NO COVER
34+
from aiohttp import ClientTimeout # type: ignore
35+
36+
else:
37+
try:
38+
from aiohttp import ClientTimeout
39+
except (ImportError, AttributeError):
40+
ClientTimeout = None
41+
3442
_LOGGER = logging.getLogger(__name__)
3543

3644

@@ -123,7 +131,7 @@ async def __call__(
123131
method: str = "GET",
124132
body: Optional[bytes] = None,
125133
headers: Optional[Mapping[str, str]] = None,
126-
timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
134+
timeout: Union[float, ClientTimeout] = transport._DEFAULT_TIMEOUT_SECONDS,
127135
**kwargs,
128136
) -> transport.Response:
129137
"""
@@ -158,7 +166,10 @@ async def __call__(
158166
if not self._session:
159167
self._session = aiohttp.ClientSession()
160168

161-
client_timeout = aiohttp.ClientTimeout(total=timeout)
169+
if isinstance(timeout, aiohttp.ClientTimeout):
170+
client_timeout = timeout
171+
else:
172+
client_timeout = aiohttp.ClientTimeout(total=timeout)
162173
_helpers.request_log(_LOGGER, method, url, body, headers)
163174
response = await self._session.request(
164175
method,
@@ -176,8 +187,12 @@ async def __call__(
176187
raise client_exc from caught_exc
177188

178189
except asyncio.TimeoutError as caught_exc:
190+
if isinstance(timeout, aiohttp.ClientTimeout):
191+
timeout_seconds = timeout.total
192+
else:
193+
timeout_seconds = timeout
179194
timeout_exc = exceptions.TimeoutError(
180-
f"Request timed out after {timeout} seconds."
195+
f"Request timed out after {timeout_seconds} seconds."
181196
)
182197
raise timeout_exc from caught_exc
183198

google/auth/aio/transport/mtls.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Helper functions for mTLS in async for discovery of certs.
17+
"""
18+
19+
import asyncio
20+
import logging
21+
22+
from google.auth import exceptions
23+
import google.auth.transport._mtls_helper
24+
import google.auth.transport.mtls
25+
26+
_LOGGER = logging.getLogger(__name__)
27+
28+
29+
async def _run_in_executor(func, *args):
30+
"""Run a blocking function in an executor to avoid blocking the event loop.
31+
32+
This implements the non-blocking execution strategy for disk I/O operations.
33+
"""
34+
try:
35+
# For python versions 3.9 and newer versions
36+
return await asyncio.to_thread(func, *args)
37+
except AttributeError:
38+
# Fallback for older Python versions
39+
loop = asyncio.get_running_loop()
40+
return await loop.run_in_executor(None, func, *args)
41+
42+
43+
def default_client_cert_source():
44+
"""Get a callback which returns the default client SSL credentials.
45+
46+
Returns:
47+
Awaitable[Callable[[], [bytes, bytes]]]: A callback which returns the default
48+
client certificate bytes and private key bytes, both in PEM format.
49+
50+
Raises:
51+
google.auth.exceptions.DefaultClientCertSourceError: If the default
52+
client SSL credentials don't exist or are malformed.
53+
"""
54+
if not google.auth.transport.mtls.has_default_client_cert_source(
55+
include_context_aware=False
56+
):
57+
raise exceptions.MutualTLSChannelError(
58+
"Default client cert source doesn't exist"
59+
)
60+
61+
async def callback():
62+
try:
63+
_, cert_bytes, key_bytes = await get_client_cert_and_key()
64+
except (OSError, RuntimeError, ValueError) as caught_exc:
65+
new_exc = exceptions.MutualTLSChannelError(caught_exc)
66+
raise new_exc from caught_exc
67+
68+
return cert_bytes, key_bytes
69+
70+
return callback
71+
72+
73+
async def get_client_ssl_credentials(
74+
certificate_config_path=None,
75+
):
76+
"""Returns the client side certificate, private key and passphrase.
77+
78+
We look for certificates and keys with the following order of priority:
79+
1. Certificate and key specified by certificate_config.json.
80+
Currently, only X.509 workload certificates are supported.
81+
82+
Args:
83+
certificate_config_path (str): The certificate_config.json file path.
84+
85+
Returns:
86+
Tuple[bool, bytes, bytes, bytes]:
87+
A boolean indicating if cert, key and passphrase are obtained, the
88+
cert bytes and key bytes both in PEM format, and passphrase bytes.
89+
90+
Raises:
91+
google.auth.exceptions.ClientCertError: if problems occurs when getting
92+
the cert, key and passphrase.
93+
"""
94+
95+
# Attempt to retrieve X.509 Workload cert and key.
96+
cert, key = await _run_in_executor(
97+
google.auth.transport._mtls_helper._get_workload_cert_and_key,
98+
certificate_config_path,
99+
False,
100+
)
101+
102+
if cert and key:
103+
return True, cert, key, None
104+
105+
return False, None, None, None
106+
107+
108+
async def get_client_cert_and_key(client_cert_callback=None):
109+
"""Returns the client side certificate and private key. The function first
110+
tries to get certificate and key from client_cert_callback; if the callback
111+
is None or doesn't provide certificate and key, the function tries application
112+
default SSL credentials.
113+
114+
Args:
115+
client_cert_callback (Optional[Callable[[], (bytes, bytes)]]): An
116+
optional callback which returns client certificate bytes and private
117+
key bytes both in PEM format.
118+
119+
Returns:
120+
Tuple[bool, bytes, bytes]:
121+
A boolean indicating if cert and key are obtained, the cert bytes
122+
and key bytes both in PEM format.
123+
124+
Raises:
125+
google.auth.exceptions.ClientCertError: if problems occurs when getting
126+
the cert and key.
127+
"""
128+
if client_cert_callback:
129+
result = client_cert_callback()
130+
try:
131+
cert, key = await result
132+
except TypeError:
133+
cert, key = result
134+
return True, cert, key
135+
136+
has_cert, cert, key, _ = await get_client_ssl_credentials()
137+
return has_cert, cert, key

0 commit comments

Comments
 (0)