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

Commit 6890572

Browse files
Merge branch 'main' into warn_rsa
2 parents 07a4f22 + bfc07e1 commit 6890572

13 files changed

Lines changed: 135 additions & 33 deletions

google/auth/_default.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,31 +330,31 @@ def _get_explicit_environ_credentials(quota_project_id=None):
330330
from google.auth import _cloud_sdk
331331

332332
cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()
333-
explicit_file = os.environ.get(environment_vars.CREDENTIALS)
333+
explicit_file = os.environ.get(environment_vars.CREDENTIALS, "")
334334

335335
_LOGGER.debug(
336-
"Checking %s for explicit credentials as part of auth process...", explicit_file
336+
"Checking '%s' for explicit credentials as part of auth process...",
337+
explicit_file,
337338
)
338339

339-
if explicit_file is not None and explicit_file == cloud_sdk_adc_path:
340+
if explicit_file != "" and explicit_file == cloud_sdk_adc_path:
340341
# Cloud sdk flow calls gcloud to fetch project id, so if the explicit
341342
# file path is cloud sdk credentials path, then we should fall back
342343
# to cloud sdk flow, otherwise project id cannot be obtained.
343344
_LOGGER.debug(
344-
"Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
345+
"Explicit credentials path '%s' is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
345346
explicit_file,
346347
)
347348
return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)
348349

349-
if explicit_file is not None:
350+
if explicit_file != "":
350351
with warnings.catch_warnings():
351352
warnings.simplefilter("ignore", DeprecationWarning)
352353
credentials, project_id = load_credentials_from_file(
353354
os.environ[environment_vars.CREDENTIALS],
354355
quota_project_id=quota_project_id,
355356
)
356357
credentials._cred_file_path = f"{explicit_file} file via the GOOGLE_APPLICATION_CREDENTIALS environment variable"
357-
358358
return credentials, project_id
359359

360360
else:

google/auth/_helpers.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,26 @@ def utcnow():
124124
return now
125125

126126

127+
def utcfromtimestamp(timestamp):
128+
"""Returns the UTC datetime from a timestamp.
129+
130+
Args:
131+
timestamp (float): The timestamp to convert.
132+
133+
Returns:
134+
datetime: The time in UTC.
135+
"""
136+
# We used datetime.utcfromtimestamp() before, since it's deprecated from
137+
# python 3.12, we are using datetime.fromtimestamp(timestamp, timezone.utc)
138+
# now. "utcfromtimestamp()" is offset-native (no timezone info), but
139+
# "fromtimestamp(timestamp, timezone.utc)" is offset-aware (with timezone
140+
# info). This will cause datetime comparison problem. For backward
141+
# compatibility, we need to remove the timezone info.
142+
dt = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
143+
dt = dt.replace(tzinfo=None)
144+
return dt
145+
146+
127147
def datetime_to_secs(value):
128148
"""Convert a datetime object to the number of seconds since the UNIX epoch.
129149

google/auth/app_engine.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
https://cloud.google.com/appengine/docs/python/appidentity/
2323
"""
2424

25-
import datetime
2625

2726
from google.auth import _helpers
2827
from google.auth import credentials
@@ -128,7 +127,7 @@ def refresh(self, request):
128127
scopes = self._scopes if self._scopes is not None else self._default_scopes
129128
# pylint: disable=unused-argument
130129
token, ttl = app_identity.get_access_token(scopes, self._service_account_id)
131-
expiry = datetime.datetime.utcfromtimestamp(ttl)
130+
expiry = _helpers.utcfromtimestamp(ttl)
132131

133132
self.token, self.expiry = token, expiry
134133

google/auth/compute_engine/_metadata.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ def _get_metadata_ip_root(use_mtls: bool):
101101
except ValueError: # pragma: NO COVER
102102
_METADATA_DEFAULT_TIMEOUT = 3
103103

104+
# This is used to disable checking for the GCE metadata server and directly
105+
# assuming it's not available.
106+
_NO_GCE_CHECK = os.getenv(environment_vars.NO_GCE_CHECK) == "true"
107+
104108
# Detect GCE Residency
105109
_GOOGLE = "Google"
106110
_GCE_PRODUCT_NAME_FILE = "/sys/class/dmi/id/product_name"
@@ -116,6 +120,9 @@ def is_on_gce(request):
116120
Returns:
117121
bool: True if the code runs on Google Compute Engine, False otherwise.
118122
"""
123+
if _NO_GCE_CHECK:
124+
return False
125+
119126
if ping(request):
120127
return True
121128

google/auth/compute_engine/credentials.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def _call_metadata_identity_endpoint(self, request):
498498
raise new_exc from caught_exc
499499

500500
_, payload, _, _ = jwt._unverified_decode(id_token)
501-
return id_token, datetime.datetime.utcfromtimestamp(payload["exp"])
501+
return id_token, _helpers.utcfromtimestamp(payload["exp"])
502502

503503
def refresh(self, request):
504504
"""Refreshes the ID token.

google/auth/environment_vars.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
"""Environment variable providing an alternate ip:port to be used for ip-only
6161
GCE metadata requests."""
6262

63+
NO_GCE_CHECK = "NO_GCE_CHECK"
64+
"""Environment variable controlling whether to check if running on GCE or not.
65+
66+
The default value is false. Users have to explicitly set this value to true
67+
in order to disable the GCE check."""
68+
6369
GCE_METADATA_MTLS_MODE = "GCE_METADATA_MTLS_MODE"
6470
"""Environment variable controlling the mTLS behavior for GCE metadata requests.
6571

google/auth/impersonated_credentials.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ def refresh(self, request):
649649
raise new_exc from caught_exc
650650

651651
self.token = id_token
652-
self.expiry = datetime.utcfromtimestamp(
652+
self.expiry = _helpers.utcfromtimestamp(
653653
jwt.decode(id_token, verify=False)["exp"]
654654
)
655655

google/oauth2/_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def call_iam_generate_id_token_endpoint(
368368
raise new_exc from caught_exc
369369

370370
payload = jwt.decode(id_token, verify=False)
371-
expiry = datetime.datetime.utcfromtimestamp(payload["exp"])
371+
expiry = _helpers.utcfromtimestamp(payload["exp"])
372372

373373
return id_token, expiry
374374

@@ -420,7 +420,7 @@ def id_token_jwt_grant(request, token_uri, assertion, can_retry=True):
420420
raise new_exc from caught_exc
421421

422422
payload = jwt.decode(id_token, verify=False)
423-
expiry = datetime.datetime.utcfromtimestamp(payload["exp"])
423+
expiry = _helpers.utcfromtimestamp(payload["exp"])
424424

425425
return id_token, expiry, response_data
426426

google/oauth2/_client_async.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
.. _Section 3.1 of rfc6749: https://tools.ietf.org/html/rfc6749#section-3.2
2424
"""
2525

26-
import datetime
2726
import http.client as http_client
2827
import json
2928
import urllib
3029

3130
from google.auth import _exponential_backoff
31+
from google.auth import _helpers
3232
from google.auth import exceptions
3333
from google.auth import jwt
3434
from google.oauth2 import _client as client
@@ -227,7 +227,7 @@ async def id_token_jwt_grant(request, token_uri, assertion, can_retry=True):
227227
raise new_exc from caught_exc
228228

229229
payload = jwt.decode(id_token, verify=False)
230-
expiry = datetime.datetime.utcfromtimestamp(payload["exp"])
230+
expiry = _helpers.utcfromtimestamp(payload["exp"])
231231

232232
return id_token, expiry, response_data
233233

tests/compute_engine/test__metadata.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ def test_is_on_gce_ping_success():
108108
assert _metadata.is_on_gce(request)
109109

110110

111+
def test_is_on_gce_no_gce_check():
112+
request = make_request("", headers=_metadata._METADATA_HEADERS)
113+
114+
os.environ[environment_vars.NO_GCE_CHECK] = "true"
115+
importlib.reload(_metadata)
116+
117+
try:
118+
assert not _metadata.is_on_gce(request)
119+
assert request.call_count == 0
120+
finally:
121+
del os.environ[environment_vars.NO_GCE_CHECK]
122+
importlib.reload(_metadata)
123+
124+
111125
@mock.patch("os.name", new="nt")
112126
def test_is_on_gce_windows_success():
113127
request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"})

0 commit comments

Comments
 (0)