Skip to content

Commit 6f75dd5

Browse files
authoredSep 12, 2024··
feat: add cred info to ADC creds (#1587)
* feat: add cred info to ADC credentials * address comment * chore: add token info support * chore: update sys test cred
1 parent c6d9903 commit 6f75dd5

17 files changed

+442
-123
lines changed
 

‎google/auth/_default.py

+2
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ def _get_gcloud_sdk_credentials(quota_project_id=None):
237237
credentials, project_id = load_credentials_from_file(
238238
credentials_filename, quota_project_id=quota_project_id
239239
)
240+
credentials._cred_file_path = credentials_filename
240241

241242
if not project_id:
242243
project_id = _cloud_sdk.get_project_id()
@@ -270,6 +271,7 @@ def _get_explicit_environ_credentials(quota_project_id=None):
270271
credentials, project_id = load_credentials_from_file(
271272
os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
272273
)
274+
credentials._cred_file_path = f"{explicit_file} file via the GOOGLE_APPLICATION_CREDENTIALS environment variable"
273275

274276
return credentials, project_id
275277

‎google/auth/compute_engine/credentials.py

+8
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ def universe_domain(self):
157157
self._universe_domain_cached = True
158158
return self._universe_domain
159159

160+
@_helpers.copy_docstring(credentials.Credentials)
161+
def get_cred_info(self):
162+
return {
163+
"credential_source": "metadata server",
164+
"credential_type": "VM credentials",
165+
"principal": self.service_account_email,
166+
}
167+
160168
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
161169
def with_quota_project(self, quota_project_id):
162170
creds = self.__class__(

‎google/auth/credentials.py

+11
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ def universe_domain(self):
128128
"""The universe domain value."""
129129
return self._universe_domain
130130

131+
def get_cred_info(self):
132+
"""The credential information JSON.
133+
134+
The credential information will be added to auth related error messages
135+
by client library.
136+
137+
Returns:
138+
Mapping[str, str]: The credential information JSON.
139+
"""
140+
return None
141+
131142
@abc.abstractmethod
132143
def refresh(self, request):
133144
"""Refreshes the access token.

‎google/auth/external_account.py

+29-14
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def __init__(
186186
self._supplier_context = SupplierContext(
187187
self._subject_token_type, self._audience
188188
)
189+
self._cred_file_path = None
189190

190191
if not self.is_workforce_pool and self._workforce_pool_user_project:
191192
# Workload identity pools do not support workforce pool user projects.
@@ -321,11 +322,24 @@ def token_info_url(self):
321322

322323
return self._token_info_url
323324

325+
@_helpers.copy_docstring(credentials.Credentials)
326+
def get_cred_info(self):
327+
if self._cred_file_path:
328+
cred_info_json = {
329+
"credential_source": self._cred_file_path,
330+
"credential_type": "external account credentials",
331+
}
332+
if self.service_account_email:
333+
cred_info_json["principal"] = self.service_account_email
334+
return cred_info_json
335+
return None
336+
324337
@_helpers.copy_docstring(credentials.Scoped)
325338
def with_scopes(self, scopes, default_scopes=None):
326339
kwargs = self._constructor_args()
327340
kwargs.update(scopes=scopes, default_scopes=default_scopes)
328341
scoped = self.__class__(**kwargs)
342+
scoped._cred_file_path = self._cred_file_path
329343
scoped._metrics_options = self._metrics_options
330344
return scoped
331345

@@ -442,30 +456,31 @@ def refresh(self, request):
442456

443457
self.expiry = now + lifetime
444458

445-
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
446-
def with_quota_project(self, quota_project_id):
447-
# Return copy of instance with the provided quota project ID.
459+
def _make_copy(self):
448460
kwargs = self._constructor_args()
449-
kwargs.update(quota_project_id=quota_project_id)
450461
new_cred = self.__class__(**kwargs)
462+
new_cred._cred_file_path = self._cred_file_path
451463
new_cred._metrics_options = self._metrics_options
452464
return new_cred
453465

466+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
467+
def with_quota_project(self, quota_project_id):
468+
# Return copy of instance with the provided quota project ID.
469+
cred = self._make_copy()
470+
cred._quota_project_id = quota_project_id
471+
return cred
472+
454473
@_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
455474
def with_token_uri(self, token_uri):
456-
kwargs = self._constructor_args()
457-
kwargs.update(token_url=token_uri)
458-
new_cred = self.__class__(**kwargs)
459-
new_cred._metrics_options = self._metrics_options
460-
return new_cred
475+
cred = self._make_copy()
476+
cred._token_url = token_uri
477+
return cred
461478

462479
@_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
463480
def with_universe_domain(self, universe_domain):
464-
kwargs = self._constructor_args()
465-
kwargs.update(universe_domain=universe_domain)
466-
new_cred = self.__class__(**kwargs)
467-
new_cred._metrics_options = self._metrics_options
468-
return new_cred
481+
cred = self._make_copy()
482+
cred._universe_domain = universe_domain
483+
return cred
469484

470485
def _should_initialize_impersonated_credentials(self):
471486
return (

‎google/auth/external_account_authorized_user.py

+25-9
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def __init__(
120120
self._quota_project_id = quota_project_id
121121
self._scopes = scopes
122122
self._universe_domain = universe_domain or credentials.DEFAULT_UNIVERSE_DOMAIN
123+
self._cred_file_path = None
123124

124125
if not self.valid and not self.can_refresh:
125126
raise exceptions.InvalidOperation(
@@ -290,23 +291,38 @@ def refresh(self, request):
290291
def _make_sts_request(self, request):
291292
return self._sts_client.refresh_token(request, self._refresh_token)
292293

294+
@_helpers.copy_docstring(credentials.Credentials)
295+
def get_cred_info(self):
296+
if self._cred_file_path:
297+
return {
298+
"credential_source": self._cred_file_path,
299+
"credential_type": "external account authorized user credentials",
300+
}
301+
return None
302+
303+
def _make_copy(self):
304+
kwargs = self.constructor_args()
305+
cred = self.__class__(**kwargs)
306+
cred._cred_file_path = self._cred_file_path
307+
return cred
308+
293309
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
294310
def with_quota_project(self, quota_project_id):
295-
kwargs = self.constructor_args()
296-
kwargs.update(quota_project_id=quota_project_id)
297-
return self.__class__(**kwargs)
311+
cred = self._make_copy()
312+
cred._quota_project_id = quota_project_id
313+
return cred
298314

299315
@_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
300316
def with_token_uri(self, token_uri):
301-
kwargs = self.constructor_args()
302-
kwargs.update(token_url=token_uri)
303-
return self.__class__(**kwargs)
317+
cred = self._make_copy()
318+
cred._token_url = token_uri
319+
return cred
304320

305321
@_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
306322
def with_universe_domain(self, universe_domain):
307-
kwargs = self.constructor_args()
308-
kwargs.update(universe_domain=universe_domain)
309-
return self.__class__(**kwargs)
323+
cred = self._make_copy()
324+
cred._universe_domain = universe_domain
325+
return cred
310326

311327
@classmethod
312328
def from_info(cls, info, **kwargs):

‎google/auth/impersonated_credentials.py

+25-13
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ def __init__(
226226
self.expiry = _helpers.utcnow()
227227
self._quota_project_id = quota_project_id
228228
self._iam_endpoint_override = iam_endpoint_override
229+
self._cred_file_path = None
229230

230231
def _metric_header_for_usage(self):
231232
return metrics.CRED_TYPE_SA_IMPERSONATE
@@ -316,29 +317,40 @@ def signer(self):
316317
def requires_scopes(self):
317318
return not self._target_scopes
318319

319-
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
320-
def with_quota_project(self, quota_project_id):
321-
return self.__class__(
320+
@_helpers.copy_docstring(credentials.Credentials)
321+
def get_cred_info(self):
322+
if self._cred_file_path:
323+
return {
324+
"credential_source": self._cred_file_path,
325+
"credential_type": "impersonated credentials",
326+
"principal": self._target_principal,
327+
}
328+
return None
329+
330+
def _make_copy(self):
331+
cred = self.__class__(
322332
self._source_credentials,
323333
target_principal=self._target_principal,
324334
target_scopes=self._target_scopes,
325335
delegates=self._delegates,
326336
lifetime=self._lifetime,
327-
quota_project_id=quota_project_id,
337+
quota_project_id=self._quota_project_id,
328338
iam_endpoint_override=self._iam_endpoint_override,
329339
)
340+
cred._cred_file_path = self._cred_file_path
341+
return cred
342+
343+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
344+
def with_quota_project(self, quota_project_id):
345+
cred = self._make_copy()
346+
cred._quota_project_id = quota_project_id
347+
return cred
330348

331349
@_helpers.copy_docstring(credentials.Scoped)
332350
def with_scopes(self, scopes, default_scopes=None):
333-
return self.__class__(
334-
self._source_credentials,
335-
target_principal=self._target_principal,
336-
target_scopes=scopes or default_scopes,
337-
delegates=self._delegates,
338-
lifetime=self._lifetime,
339-
quota_project_id=self._quota_project_id,
340-
iam_endpoint_override=self._iam_endpoint_override,
341-
)
351+
cred = self._make_copy()
352+
cred._target_scopes = scopes or default_scopes
353+
return cred
342354

343355

344356
class IDTokenCredentials(credentials.CredentialsWithQuotaProject):

0 commit comments

Comments
 (0)
Please sign in to comment.