feat(auth): make RAB feature production ready (#17390) · googleapis/google-cloud-python@af19393 · GitHub
Skip to content

Commit af19393

Browse files
authored
feat(auth): make RAB feature production ready (#17390)
This PR resolves issues identified during verification of gcloud Regional Access Boundary (RAB) flows and enables RAB verification by default: * Removes the client-side environment variable feature gate (`GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED`) to execute RAB lookups by default across standard credential classes. * Updates the Python auth SDK to recognize mTLS regional endpoints (`.rep.mtls.googleapis.com`), bypassing redundant RAB lookups on secure transport boundaries. * Defers Service Account impersonation setup until HTTP request execution before_request, propagating active cached tokens downward onto the inner credential to guarantee that access tokens restored across external CLI entrypoints correctly delegate regional access boundary (RAB) lookups to target Service Account endpoints without forcing redundant STS network renewal.
1 parent 00ec9bf commit af19393

12 files changed

Lines changed: 318 additions & 201 deletions

packages/google-auth/google/auth/_regional_access_boundary_utils.py

Lines changed: 0 additions & 21 deletions

packages/google-auth/google/auth/aws.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -841,11 +841,9 @@ def from_info(cls, info, **kwargs):
841841
Raises:
842842
ValueError: For invalid parameters.
843843
"""
844-
aws_security_credentials_supplier = info.get(
845-
"aws_security_credentials_supplier"
846-
)
847-
kwargs.update(
848-
{"aws_security_credentials_supplier": aws_security_credentials_supplier}
844+
kwargs.setdefault(
845+
"aws_security_credentials_supplier",
846+
info.get("aws_security_credentials_supplier"),
849847
)
850848
return super(Credentials, cls).from_info(info, **kwargs)
851849

packages/google-auth/google/auth/credentials.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,13 @@ def _is_regional_endpoint(self, url):
446446
try:
447447
# Do not perform a lookup if the request is for a regional endpoint.
448448
hostname = urlparse(url).hostname
449-
if hostname and (
450-
hostname.endswith(".rep.googleapis.com")
451-
or hostname.endswith(".rep.sandbox.googleapis.com")
449+
if hostname and hostname.endswith(
450+
(
451+
".rep.googleapis.com",
452+
".rep.sandbox.googleapis.com",
453+
".rep.mtls.googleapis.com",
454+
".rep.mtls.sandbox.googleapis.com",
455+
)
452456
):
453457
return True
454458
except (ValueError, TypeError, AttributeError):
@@ -484,16 +488,11 @@ def _maybe_start_regional_access_boundary_refresh(self, request, url):
484488
def _is_regional_access_boundary_lookup_required(self):
485489
"""Checks if a Regional Access Boundary lookup is required.
486490
487-
A lookup is required if the feature is enabled via an environment
488-
variable and the universe domain is supported.
491+
A lookup is required if the universe domain is supported.
489492
490493
Returns:
491494
bool: True if a Regional Access Boundary lookup is required, False otherwise.
492495
"""
493-
# Check if the feature is enabled.
494-
if not _regional_access_boundary_utils.is_regional_access_boundary_enabled():
495-
return False
496-
497496
# Skip for non-default universe domains.
498497
if self.universe_domain != DEFAULT_UNIVERSE_DOMAIN:
499498
return False

packages/google-auth/google/auth/environment_vars.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,13 @@
105105
AWS_REGION = "AWS_REGION"
106106
AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
107107

108+
108109
GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED"
109110
"""Environment variable controlling whether to enable trust boundary feature.
110-
The default value is false. Users have to explicitly set this value to true."""
111+
112+
.. deprecated::
113+
This environment variable is deprecated and no longer has any effect.
114+
"""
111115

112116
GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG"
113117
"""Environment variable defining the location of Google API certificate config

packages/google-auth/google/auth/external_account.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import json
3737
import logging
3838
import re
39+
import threading
3940
from typing import Optional, TYPE_CHECKING
4041

4142

@@ -200,6 +201,7 @@ def __init__(
200201
self._metrics_options = self._create_default_metrics_options()
201202

202203
self._impersonated_credentials = None
204+
self._impersonation_lock = threading.Lock()
203205
self._project_id = None
204206
self._supplier_context = SupplierContext(
205207
self._subject_token_type, self._audience
@@ -213,6 +215,15 @@ def __init__(
213215
"credentials"
214216
)
215217

218+
def __getstate__(self):
219+
state = self.__dict__.copy()
220+
state.pop("_impersonation_lock", None)
221+
return state
222+
223+
def __setstate__(self, state):
224+
super().__setstate__(state)
225+
self._impersonation_lock = threading.Lock()
226+
216227
@property
217228
def info(self):
218229
"""Generates the dictionary representation of the current credentials.
@@ -444,6 +455,17 @@ def _maybe_start_regional_access_boundary_refresh(self, request, url):
444455
HTTP requests.
445456
url (str): The URL of the request.
446457
"""
458+
if self._should_initialize_impersonated_credentials():
459+
with self._impersonation_lock:
460+
if self._impersonated_credentials is None:
461+
impersonated = self._initialize_impersonated_credentials()
462+
if getattr(self, "token", None):
463+
impersonated.token = self.token
464+
if getattr(self, "expiry", None):
465+
impersonated.expiry = self.expiry
466+
self._impersonated_credentials = impersonated
467+
self._rab_manager = impersonated._rab_manager
468+
447469
if getattr(self, "_impersonated_credentials", None):
448470
self._impersonated_credentials._maybe_start_regional_access_boundary_refresh(
449471
request, url
@@ -462,7 +484,11 @@ def _perform_refresh_token(self, request, cert_fingerprint=None):
462484
)
463485

464486
if self._should_initialize_impersonated_credentials():
465-
self._impersonated_credentials = self._initialize_impersonated_credentials()
487+
with self._impersonation_lock:
488+
if self._impersonated_credentials is None:
489+
self._impersonated_credentials = (
490+
self._initialize_impersonated_credentials()
491+
)
466492

467493
if self._impersonated_credentials:
468494
self._impersonated_credentials.refresh(request)
@@ -581,9 +607,10 @@ def with_universe_domain(self, universe_domain):
581607
return cred
582608

583609
def _should_initialize_impersonated_credentials(self):
610+
"""Determines if the underlying Service Account credential should be initialized."""
584611
return (
585-
self._service_account_impersonation_url is not None
586-
and self._impersonated_credentials is None
612+
getattr(self, "_service_account_impersonation_url", None) is not None
613+
and getattr(self, "_impersonated_credentials", None) is None
587614
)
588615

589616
def _initialize_impersonated_credentials(self):

packages/google-auth/google/auth/identity_pool.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,7 @@ def from_info(cls, info, **kwargs):
526526
Raises:
527527
ValueError: For invalid parameters.
528528
"""
529-
subject_token_supplier = info.get("subject_token_supplier")
530-
kwargs.update({"subject_token_supplier": subject_token_supplier})
529+
kwargs.setdefault("subject_token_supplier", info.get("subject_token_supplier"))
531530
return super(Credentials, cls).from_info(info, **kwargs)
532531

533532
@classmethod

packages/google-auth/tests/compute_engine/test_credentials.py

Lines changed: 35 additions & 15 deletions

0 commit comments

Comments
 (0)