3030from google .auth .compute_engine import _metadata
3131from google .oauth2 import _client
3232
33+ _TRUST_BOUNDARY_LOOKUP_ENDPOINT = (
34+ "https://iamcredentials.{}/v1/projects/-/serviceAccounts/{}/allowedLocations"
35+ )
36+
3337
3438class Credentials (
3539 credentials .Scoped ,
3640 credentials .CredentialsWithQuotaProject ,
3741 credentials .CredentialsWithUniverseDomain ,
42+ credentials .CredentialsWithTrustBoundary ,
3843):
3944 """Compute Engine Credentials.
4045
@@ -61,6 +66,7 @@ def __init__(
6166 scopes = None ,
6267 default_scopes = None ,
6368 universe_domain = None ,
69+ trust_boundary = None ,
6470 ):
6571 """
6672 Args:
@@ -76,6 +82,7 @@ def __init__(
7682 provided or None, credential will attempt to fetch the value
7783 from metadata server. If metadata server doesn't have universe
7884 domain endpoint, then the default googleapis.com will be used.
85+ trust_boundary (Mapping[str,str]): A credential trust boundary.
7986 """
8087 super (Credentials , self ).__init__ ()
8188 self ._service_account_email = service_account_email
@@ -86,6 +93,7 @@ def __init__(
8693 if universe_domain :
8794 self ._universe_domain = universe_domain
8895 self ._universe_domain_cached = True
96+ self ._trust_boundary = trust_boundary
8997
9098 def _retrieve_info (self , request ):
9199 """Retrieve information about the service account.
@@ -100,16 +108,22 @@ def _retrieve_info(self, request):
100108 request , service_account = self ._service_account_email
101109 )
102110
111+ if not info or "email" not in info :
112+ raise exceptions .RefreshError (
113+ "Unexpected response from metadata server: "
114+ "service account info is missing 'email' field."
115+ )
116+
103117 self ._service_account_email = info ["email" ]
104118
105119 # Don't override scopes requested by the user.
106120 if self ._scopes is None :
107- self ._scopes = info [ "scopes" ]
121+ self ._scopes = info . get ( "scopes" )
108122
109123 def _metric_header_for_usage (self ):
110124 return metrics .CRED_TYPE_SA_MDS
111125
112- def refresh (self , request ):
126+ def _refresh_token (self , request ):
113127 """Refresh the access token and scopes.
114128
115129 Args:
@@ -132,6 +146,37 @@ def refresh(self, request):
132146 new_exc = exceptions .RefreshError (caught_exc )
133147 raise new_exc from caught_exc
134148
149+ def _build_trust_boundary_lookup_url (self ):
150+ """Builds and returns the URL for the trust boundary lookup API for GCE."""
151+ # If the service account email is 'default', we need to get the
152+ # actual email address from the metadata server.
153+ if self ._service_account_email == "default" :
154+ from google .auth .transport import requests as google_auth_requests
155+
156+ request = google_auth_requests .Request ()
157+ try :
158+ info = _metadata .get_service_account_info (request , "default" )
159+ if not info or "email" not in info :
160+ raise exceptions .RefreshError (
161+ "Unexpected response from metadata server: "
162+ "service account info is missing 'email' field."
163+ )
164+ self ._service_account_email = info ["email" ]
165+
166+ except exceptions .TransportError as e :
167+ # If fetching the service account email fails due to a transport error,
168+ # it means we cannot build the trust boundary lookup URL.
169+ # Wrap this in a RefreshError so it's caught by _refresh_trust_boundary.
170+ raise exceptions .RefreshError (
171+ "Failed to get service account email for trust boundary lookup: {}" .format (
172+ e
173+ )
174+ ) from e
175+
176+ return _TRUST_BOUNDARY_LOOKUP_ENDPOINT .format (
177+ self .universe_domain , self .service_account_email
178+ )
179+
135180 @property
136181 def service_account_email (self ):
137182 """The service account email.
@@ -173,8 +218,9 @@ def with_quota_project(self, quota_project_id):
173218 quota_project_id = quota_project_id ,
174219 scopes = self ._scopes ,
175220 default_scopes = self ._default_scopes ,
221+ universe_domain = self ._universe_domain ,
222+ trust_boundary = self ._trust_boundary ,
176223 )
177- creds ._universe_domain = self ._universe_domain
178224 creds ._universe_domain_cached = self ._universe_domain_cached
179225 return creds
180226
@@ -188,8 +234,9 @@ def with_scopes(self, scopes, default_scopes=None):
188234 default_scopes = default_scopes ,
189235 service_account_email = self ._service_account_email ,
190236 quota_project_id = self ._quota_project_id ,
237+ universe_domain = self ._universe_domain ,
238+ trust_boundary = self ._trust_boundary ,
191239 )
192- creds ._universe_domain = self ._universe_domain
193240 creds ._universe_domain_cached = self ._universe_domain_cached
194241 return creds
195242
@@ -200,9 +247,23 @@ def with_universe_domain(self, universe_domain):
200247 default_scopes = self ._default_scopes ,
201248 service_account_email = self ._service_account_email ,
202249 quota_project_id = self ._quota_project_id ,
250+ trust_boundary = self ._trust_boundary ,
203251 universe_domain = universe_domain ,
204252 )
205253
254+ @_helpers .copy_docstring (credentials .CredentialsWithTrustBoundary )
255+ def with_trust_boundary (self , trust_boundary ):
256+ creds = self .__class__ (
257+ service_account_email = self ._service_account_email ,
258+ quota_project_id = self ._quota_project_id ,
259+ scopes = self ._scopes ,
260+ default_scopes = self ._default_scopes ,
261+ universe_domain = self ._universe_domain ,
262+ trust_boundary = trust_boundary ,
263+ )
264+ creds ._universe_domain_cached = self ._universe_domain_cached
265+ return creds
266+
206267
207268_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
208269_DEFAULT_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token"
@@ -275,7 +336,7 @@ def __init__(
275336
276337 if use_metadata_identity_endpoint :
277338 if token_uri or additional_claims or service_account_email or signer :
278- raise exceptions . MalformedError (
339+ raise ValueError (
279340 "If use_metadata_identity_endpoint is set, token_uri, "
280341 "additional_claims, service_account_email, signer arguments"
281342 " must not be set"
@@ -366,7 +427,7 @@ def with_token_uri(self, token_uri):
366427 # since the signer is already instantiated,
367428 # the request is not needed
368429 if self ._use_metadata_identity_endpoint :
369- raise exceptions . MalformedError (
430+ raise ValueError (
370431 "If use_metadata_identity_endpoint is set, token_uri" " must not be set"
371432 )
372433 else :
0 commit comments