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

Commit 61da847

Browse files
Merge branch 'main' into remove_rsa_3
2 parents 971cbd7 + 52558ae commit 61da847

9 files changed

Lines changed: 80 additions & 64 deletions

File tree

google/auth/_agent_identity_utils.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
from google.auth import environment_vars
2626
from google.auth import exceptions
27-
from google.auth.transport import _mtls_helper
2827

2928

3029
_LOGGER = logging.getLogger(__name__)
@@ -263,14 +262,6 @@ def should_request_bound_token(cert):
263262
return is_agent_cert and is_opted_in
264263

265264

266-
def call_client_cert_callback():
267-
"""Calls the client cert callback and returns the certificate and key."""
268-
_, cert_bytes, key_bytes, passphrase = _mtls_helper.get_client_ssl_credentials(
269-
generate_encrypted_key=True
270-
)
271-
return cert_bytes, key_bytes
272-
273-
274265
def get_cached_cert_fingerprint(cached_cert):
275266
"""Returns the fingerprint of the cached certificate."""
276267
if cached_cert:

google/auth/credentials.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@
1717

1818
import abc
1919
from enum import Enum
20+
import logging
2021
import os
2122
from typing import List
2223

2324
from google.auth import _helpers, environment_vars
2425
from google.auth import exceptions
2526
from google.auth import metrics
2627
from google.auth._credentials_base import _BaseCredentials
27-
from google.auth._default import _LOGGER
2828
from google.auth._refresh_worker import RefreshThreadManager
2929

3030
DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"
3131
NO_OP_TRUST_BOUNDARY_LOCATIONS: List[str] = []
3232
NO_OP_TRUST_BOUNDARY_ENCODED_LOCATIONS = "0x0"
3333

34+
_LOGGER = logging.getLogger("google.auth._default")
35+
3436

3537
class Credentials(_BaseCredentials):
3638
"""Base class for all credentials.

google/auth/iam.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import base64
2323
import http.client as http_client
2424
import json
25+
import os
2526

2627
from google.auth import _exponential_backoff
2728
from google.auth import _helpers
2829
from google.auth import credentials
2930
from google.auth import crypt
3031
from google.auth import exceptions
32+
from google.auth.transport import mtls
3133

3234
IAM_RETRY_CODES = {
3335
http_client.INTERNAL_SERVER_ERROR,
@@ -38,25 +40,33 @@
3840

3941
_IAM_SCOPE = ["https://www.googleapis.com/auth/iam"]
4042

41-
_IAM_ENDPOINT = (
42-
"https://iamcredentials.googleapis.com/v1/projects/-"
43-
+ "/serviceAccounts/{}:generateAccessToken"
44-
)
45-
46-
_IAM_SIGN_ENDPOINT = (
47-
"https://iamcredentials.googleapis.com/v1/projects/-"
48-
+ "/serviceAccounts/{}:signBlob"
49-
)
50-
51-
_IAM_SIGNJWT_ENDPOINT = (
52-
"https://iamcredentials.googleapis.com/v1/projects/-"
53-
+ "/serviceAccounts/{}:signJwt"
54-
)
55-
56-
_IAM_IDTOKEN_ENDPOINT = (
57-
"https://iamcredentials.googleapis.com/v1/"
58-
+ "projects/-/serviceAccounts/{}:generateIdToken"
59-
)
43+
# 1. Determine if we should use mTLS.
44+
# Note: We only support automatic mTLS on the default googleapis.com universe.
45+
if hasattr(mtls, "should_use_client_cert"):
46+
use_client_cert = mtls.should_use_client_cert()
47+
else: # pragma: NO COVER
48+
# if unsupported, fallback to reading from env var
49+
use_client_cert = (
50+
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower() == "true"
51+
)
52+
53+
# 2. Construct the template domain using the library's DEFAULT_UNIVERSE_DOMAIN constant.
54+
# This ensures that the .replace() calls in the classes will work correctly.
55+
if use_client_cert:
56+
# We use the .mtls. prefix only for the default universe template
57+
_IAM_DOMAIN = f"iamcredentials.mtls.{credentials.DEFAULT_UNIVERSE_DOMAIN}"
58+
else:
59+
_IAM_DOMAIN = f"iamcredentials.{credentials.DEFAULT_UNIVERSE_DOMAIN}"
60+
61+
# 3. Create the common base URL template
62+
# We use double brackets {{}} so .format() can be called later for the email.
63+
_IAM_BASE_URL = f"https://{_IAM_DOMAIN}/v1/projects/-/serviceAccounts/{{}}"
64+
65+
# 4. Define the endpoints as templates
66+
_IAM_ENDPOINT = _IAM_BASE_URL + ":generateAccessToken"
67+
_IAM_SIGN_ENDPOINT = _IAM_BASE_URL + ":signBlob"
68+
_IAM_SIGNJWT_ENDPOINT = _IAM_BASE_URL + ":signJwt"
69+
_IAM_IDTOKEN_ENDPOINT = _IAM_BASE_URL + ":generateIdToken"
6070

6171

6272
class Signer(crypt.Signer):

google/auth/transport/_mtls_helper.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ def check_parameters_for_unauthorized_response(cached_cert):
489489
str: The base64-encoded SHA256 cached fingerprint.
490490
str: The base64-encoded SHA256 current cert fingerprint.
491491
"""
492-
call_cert_bytes, call_key_bytes = _agent_identity_utils.call_client_cert_callback()
492+
call_cert_bytes, call_key_bytes = call_client_cert_callback()
493493
cert_obj = _agent_identity_utils.parse_certificate(call_cert_bytes)
494494
current_cert_fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(
495495
cert_obj
@@ -501,3 +501,11 @@ def check_parameters_for_unauthorized_response(cached_cert):
501501
else:
502502
cached_fingerprint = current_cert_fingerprint
503503
return call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint
504+
505+
506+
def call_client_cert_callback():
507+
"""Calls the client cert callback and returns the certificate and key."""
508+
_, cert_bytes, key_bytes, passphrase = get_client_ssl_credentials(
509+
generate_encrypted_key=True
510+
)
511+
return cert_bytes, key_bytes

tests/compute_engine/test__metadata.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,9 @@ def test_ping_success_custom_root(mock_metrics_header_value):
201201
)
202202

203203

204+
@mock.patch("time.sleep", return_value=None)
204205
@mock.patch("google.auth.metrics.mds_ping", return_value=MDS_PING_METRICS_HEADER_VALUE)
205-
def test_ping_failure_custom_retry(mock_metrics_header_value):
206+
def test_ping_failure_custom_retry(mock_metrics_header_value, _mock_sleep):
206207
request = make_request("")
207208
request.side_effect = exceptions.TransportError()
208209

@@ -450,7 +451,8 @@ def test_get_failure_connection_failed(mock_sleep):
450451
assert request.call_count == 5
451452

452453

453-
def test_get_too_many_requests_retryable_error_failure():
454+
@mock.patch("time.sleep", return_value=None)
455+
def test_get_too_many_requests_retryable_error_failure(_mock_sleep):
454456
request = make_request("too many requests", status=http_client.TOO_MANY_REQUESTS)
455457

456458
with pytest.raises(exceptions.TransportError) as excinfo:
@@ -546,7 +548,8 @@ def test_get_universe_domain_not_found():
546548
assert universe_domain == "googleapis.com"
547549

548550

549-
def test_get_universe_domain_retryable_error_failure():
551+
@mock.patch("time.sleep", return_value=None)
552+
def test_get_universe_domain_retryable_error_failure(_mock_sleep):
550553
# Test that if the universe domain endpoint returns a retryable error
551554
# we should retry.
552555
#

tests/test_agent_identity_utils.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -282,23 +282,6 @@ def test_get_agent_identity_certificate_path_fallback_to_well_known_path(
282282
mock_sleep.assert_called_once()
283283
assert mock_is_ready.call_count == 2
284284

285-
@mock.patch("google.auth.transport._mtls_helper.get_client_ssl_credentials")
286-
def test_call_client_cert_callback(self, mock_get_client_ssl_credentials):
287-
mock_get_client_ssl_credentials.return_value = (
288-
True,
289-
b"cert_bytes",
290-
b"key_bytes",
291-
b"passphrase",
292-
)
293-
294-
cert, key = _agent_identity_utils.call_client_cert_callback()
295-
296-
assert cert == b"cert_bytes"
297-
assert key == b"key_bytes"
298-
mock_get_client_ssl_credentials.assert_called_once_with(
299-
generate_encrypted_key=True
300-
)
301-
302285
def test_get_cached_cert_fingerprint_no_cert(self):
303286
with pytest.raises(ValueError, match="mTLS connection is not configured."):
304287
_agent_identity_utils.get_cached_cert_fingerprint(None)

tests/transport/test__mtls_helper.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -813,11 +813,12 @@ def test_no_env_vars_set(self):
813813

814814

815815
class TestMtlsHelper:
816+
@mock.patch.object(_mtls_helper, "call_client_cert_callback")
816817
@mock.patch("google.auth.transport._mtls_helper._agent_identity_utils")
817818
def test_check_parameters_for_unauthorized_response_with_cached_cert(
818-
self, mock_agent_identity_utils
819+
self, mock_agent_identity_utils, mock_call_client_cert_callback
819820
):
820-
mock_agent_identity_utils.call_client_cert_callback.return_value = (
821+
mock_call_client_cert_callback.return_value = (
821822
CERT_MOCK_VAL,
822823
KEY_MOCK_VAL,
823824
)
@@ -841,16 +842,17 @@ def test_check_parameters_for_unauthorized_response_with_cached_cert(
841842
assert key == KEY_MOCK_VAL
842843
assert cached_fingerprint == "cached_fingerprint"
843844
assert current_fingerprint == "current_fingerprint"
844-
mock_agent_identity_utils.call_client_cert_callback.assert_called_once()
845+
mock_call_client_cert_callback.assert_called_once()
845846
mock_agent_identity_utils.get_cached_cert_fingerprint.assert_called_once_with(
846847
b"cached_cert_bytes"
847848
)
848849

849-
@mock.patch("google.auth.transport._mtls_helper._agent_identity_utils")
850+
@mock.patch.object(_mtls_helper, "call_client_cert_callback")
851+
@mock.patch.object(_mtls_helper, "_agent_identity_utils")
850852
def test_check_parameters_for_unauthorized_response_without_cached_cert(
851-
self, mock_agent_identity_utils
853+
self, mock_agent_identity_utils, mock_call_client_cert_callback
852854
):
853-
mock_agent_identity_utils.call_client_cert_callback.return_value = (
855+
mock_call_client_cert_callback.return_value = (
854856
CERT_MOCK_VAL,
855857
KEY_MOCK_VAL,
856858
)
@@ -869,5 +871,22 @@ def test_check_parameters_for_unauthorized_response_without_cached_cert(
869871
assert key == KEY_MOCK_VAL
870872
assert cached_fingerprint == "current_fingerprint"
871873
assert current_fingerprint == "current_fingerprint"
872-
mock_agent_identity_utils.call_client_cert_callback.assert_called_once()
874+
mock_call_client_cert_callback.assert_called_once()
873875
mock_agent_identity_utils.get_cached_cert_fingerprint.assert_not_called()
876+
877+
@mock.patch("google.auth.transport._mtls_helper.get_client_ssl_credentials")
878+
def test_call_client_cert_callback(self, mock_get_client_ssl_credentials):
879+
mock_get_client_ssl_credentials.return_value = (
880+
True,
881+
b"cert_bytes",
882+
b"key_bytes",
883+
b"passphrase",
884+
)
885+
886+
cert, key = _mtls_helper.call_client_cert_callback()
887+
888+
assert cert == b"cert_bytes"
889+
assert key == b"key_bytes"
890+
mock_get_client_ssl_credentials.assert_called_once_with(
891+
generate_encrypted_key=True
892+
)

tests/transport/test_requests.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def test_cert_rotation_when_cert_mismatch_and_mtls_enabled(self):
566566

567567
# Mock call_client_cert_callback to return the new certificate.
568568
with mock.patch.object(
569-
google.auth.transport._mtls_helper._agent_identity_utils,
569+
google.auth.transport._mtls_helper,
570570
"call_client_cert_callback",
571571
return_value=(new_cert, new_key),
572572
) as mock_callback:
@@ -605,7 +605,7 @@ def test_no_cert_rotation_when_cert_match_and_mTLS_enabled(self):
605605

606606
# Mock call_client_cert_callback to return the new certificate.
607607
with mock.patch.object(
608-
google.auth.transport._mtls_helper._agent_identity_utils,
608+
google.auth.transport._mtls_helper,
609609
"call_client_cert_callback",
610610
return_value=(new_cert, new_key),
611611
):
@@ -638,7 +638,7 @@ def test_no_cert_match_check_when_mtls_disabled(self):
638638

639639
# Mock call_client_cert_callback to return the new certificate.
640640
with mock.patch.object(
641-
google.auth.transport._mtls_helper._agent_identity_utils,
641+
google.auth.transport._mtls_helper,
642642
"call_client_cert_callback",
643643
return_value=(new_cert, new_key),
644644
) as mock_callback:
@@ -688,7 +688,7 @@ def test_cert_rotation_failure_raises_error(self):
688688
authed_session._is_mtls = True
689689

690690
with mock.patch.object(
691-
google.auth.transport._mtls_helper._agent_identity_utils,
691+
google.auth.transport._mtls_helper,
692692
"call_client_cert_callback",
693693
return_value=(new_cert, new_key),
694694
):

tests/transport/test_urllib3.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def test_cert_rotation_when_cert_mismatch_and_mtls_endpoint_used(self):
343343
authed_http._is_mtls = True
344344
# Mock call_client_cert_callback to return the new certificate.
345345
with mock.patch.object(
346-
google.auth._agent_identity_utils,
346+
google.auth.transport._mtls_helper,
347347
"call_client_cert_callback",
348348
return_value=(new_cert, new_key),
349349
) as mock_callback:
@@ -378,7 +378,7 @@ def test_no_cert_rotation_when_cert_match_and_mtls_endpoint_used(self):
378378
authed_http._is_mtls = True
379379
# Mock call_client_cert_callback to return the certificate.
380380
with mock.patch.object(
381-
google.auth._agent_identity_utils,
381+
google.auth.transport._mtls_helper,
382382
"call_client_cert_callback",
383383
return_value=(new_cert, new_key),
384384
):
@@ -408,7 +408,7 @@ def test_no_cert_match_check_when_mtls_endpoint_not_used(self):
408408

409409
# Mock call_client_cert_callback to return the certificate.
410410
with mock.patch.object(
411-
google.auth._agent_identity_utils,
411+
google.auth.transport._mtls_helper,
412412
"call_client_cert_callback",
413413
return_value=(new_cert, new_key),
414414
) as mock_callback:

0 commit comments

Comments
 (0)