Skip to content

Commit a886932

Browse files
feat: add cred info to ADC creds
1 parent c6d9903 commit a886932

16 files changed

+218
-65
lines changed

google/auth/_default.py

Lines changed: 2 additions & 0 deletions
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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121

2222
import datetime
23+
import json
2324

2425
from google.auth import _helpers
2526
from google.auth import credentials
@@ -157,6 +158,14 @@ def universe_domain(self):
157158
self._universe_domain_cached = True
158159
return self._universe_domain
159160

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

google/auth/credentials.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ 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+
return None
134+
131135
@abc.abstractmethod
132136
def refresh(self, request):
133137
"""Refreshes the access token.

google/auth/external_account.py

Lines changed: 17 additions & 0 deletions
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

@@ -448,6 +462,7 @@ def with_quota_project(self, quota_project_id):
448462
kwargs = self._constructor_args()
449463
kwargs.update(quota_project_id=quota_project_id)
450464
new_cred = self.__class__(**kwargs)
465+
new_cred._cred_file_path = self._cred_file_path
451466
new_cred._metrics_options = self._metrics_options
452467
return new_cred
453468

@@ -456,6 +471,7 @@ def with_token_uri(self, token_uri):
456471
kwargs = self._constructor_args()
457472
kwargs.update(token_url=token_uri)
458473
new_cred = self.__class__(**kwargs)
474+
new_cred._cred_file_path = self._cred_file_path
459475
new_cred._metrics_options = self._metrics_options
460476
return new_cred
461477

@@ -464,6 +480,7 @@ def with_universe_domain(self, universe_domain):
464480
kwargs = self._constructor_args()
465481
kwargs.update(universe_domain=universe_domain)
466482
new_cred = self.__class__(**kwargs)
483+
new_cred._cred_file_path = self._cred_file_path
467484
new_cred._metrics_options = self._metrics_options
468485
return new_cred
469486

google/auth/external_account_authorized_user.py

Lines changed: 19 additions & 3 deletions
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+
293303
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
294304
def with_quota_project(self, quota_project_id):
295305
kwargs = self.constructor_args()
296306
kwargs.update(quota_project_id=quota_project_id)
297-
return self.__class__(**kwargs)
307+
cred = self.__class__(**kwargs)
308+
cred._cred_file_path = self._cred_file_path
309+
return cred
298310

299311
@_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
300312
def with_token_uri(self, token_uri):
301313
kwargs = self.constructor_args()
302314
kwargs.update(token_url=token_uri)
303-
return self.__class__(**kwargs)
315+
cred = self.__class__(**kwargs)
316+
cred._cred_file_path = self._cred_file_path
317+
return cred
304318

305319
@_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
306320
def with_universe_domain(self, universe_domain):
307321
kwargs = self.constructor_args()
308322
kwargs.update(universe_domain=universe_domain)
309-
return self.__class__(**kwargs)
323+
cred = self.__class__(**kwargs)
324+
cred._cred_file_path = self._cred_file_path
325+
return cred
310326

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

google/auth/impersonated_credentials.py

Lines changed: 17 additions & 2 deletions
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,9 +317,19 @@ def signer(self):
316317
def requires_scopes(self):
317318
return not self._target_scopes
318319

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+
319330
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
320331
def with_quota_project(self, quota_project_id):
321-
return self.__class__(
332+
cred = self.__class__(
322333
self._source_credentials,
323334
target_principal=self._target_principal,
324335
target_scopes=self._target_scopes,
@@ -327,10 +338,12 @@ def with_quota_project(self, quota_project_id):
327338
quota_project_id=quota_project_id,
328339
iam_endpoint_override=self._iam_endpoint_override,
329340
)
341+
cred._cred_file_path = self._cred_file_path
342+
return cred
330343

331344
@_helpers.copy_docstring(credentials.Scoped)
332345
def with_scopes(self, scopes, default_scopes=None):
333-
return self.__class__(
346+
cred = self.__class__(
334347
self._source_credentials,
335348
target_principal=self._target_principal,
336349
target_scopes=scopes or default_scopes,
@@ -339,6 +352,8 @@ def with_scopes(self, scopes, default_scopes=None):
339352
quota_project_id=self._quota_project_id,
340353
iam_endpoint_override=self._iam_endpoint_override,
341354
)
355+
cred._cred_file_path = self._cred_file_path
356+
return cred
342357

343358

344359
class IDTokenCredentials(credentials.CredentialsWithQuotaProject):

google/oauth2/credentials.py

Lines changed: 31 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def __init__(
151151
self._trust_boundary = trust_boundary
152152
self._universe_domain = universe_domain or credentials.DEFAULT_UNIVERSE_DOMAIN
153153
self._account = account or ""
154+
self._cred_file_path = None
154155

155156
def __getstate__(self):
156157
"""A __getstate__ method must exist for the __setstate__ to be called
@@ -189,6 +190,7 @@ def __setstate__(self, d):
189190
self._universe_domain = (
190191
d.get("_universe_domain") or credentials.DEFAULT_UNIVERSE_DOMAIN
191192
)
193+
self._cred_file_path = d.get("_cred_file_path")
192194
# The refresh_handler setter should be used to repopulate this.
193195
self._refresh_handler = None
194196
self._refresh_worker = None
@@ -278,10 +280,8 @@ def account(self):
278280
"""str: The user account associated with the credential. If the account is unknown an empty string is returned."""
279281
return self._account
280282

281-
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
282-
def with_quota_project(self, quota_project_id):
283-
284-
return self.__class__(
283+
def _make_copy(self):
284+
cred = self.__class__(
285285
self.token,
286286
refresh_token=self.refresh_token,
287287
id_token=self.id_token,
@@ -291,34 +291,36 @@ def with_quota_project(self, quota_project_id):
291291
scopes=self.scopes,
292292
default_scopes=self.default_scopes,
293293
granted_scopes=self.granted_scopes,
294-
quota_project_id=quota_project_id,
294+
quota_project_id=self.quota_project_id,
295295
rapt_token=self.rapt_token,
296296
enable_reauth_refresh=self._enable_reauth_refresh,
297297
trust_boundary=self._trust_boundary,
298298
universe_domain=self._universe_domain,
299299
account=self._account,
300300
)
301+
cred._cred_file_path = self._cred_file_path
302+
return cred
303+
304+
@_helpers.copy_docstring(credentials.Credentials)
305+
def get_cred_info(self):
306+
if self._cred_file_path:
307+
return {
308+
"credential_source": self._cred_file_path,
309+
"credential_type": "user credentials",
310+
}
311+
return None
312+
313+
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
314+
def with_quota_project(self, quota_project_id):
315+
cred = self._make_copy()
316+
cred._quota_project_id = quota_project_id
317+
return cred
301318

302319
@_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
303320
def with_token_uri(self, token_uri):
304-
305-
return self.__class__(
306-
self.token,
307-
refresh_token=self.refresh_token,
308-
id_token=self.id_token,
309-
token_uri=token_uri,
310-
client_id=self.client_id,
311-
client_secret=self.client_secret,
312-
scopes=self.scopes,
313-
default_scopes=self.default_scopes,
314-
granted_scopes=self.granted_scopes,
315-
quota_project_id=self.quota_project_id,
316-
rapt_token=self.rapt_token,
317-
enable_reauth_refresh=self._enable_reauth_refresh,
318-
trust_boundary=self._trust_boundary,
319-
universe_domain=self._universe_domain,
320-
account=self._account,
321-
)
321+
cred = self._make_copy()
322+
cred._token_uri = token_uri
323+
return cred
322324

323325
def with_account(self, account):
324326
"""Returns a copy of these credentials with a modified account.
@@ -329,45 +331,15 @@ def with_account(self, account):
329331
Returns:
330332
google.oauth2.credentials.Credentials: A new credentials instance.
331333
"""
332-
333-
return self.__class__(
334-
self.token,
335-
refresh_token=self.refresh_token,
336-
id_token=self.id_token,
337-
token_uri=self._token_uri,
338-
client_id=self.client_id,
339-
client_secret=self.client_secret,
340-
scopes=self.scopes,
341-
default_scopes=self.default_scopes,
342-
granted_scopes=self.granted_scopes,
343-
quota_project_id=self.quota_project_id,
344-
rapt_token=self.rapt_token,
345-
enable_reauth_refresh=self._enable_reauth_refresh,
346-
trust_boundary=self._trust_boundary,
347-
universe_domain=self._universe_domain,
348-
account=account,
349-
)
334+
cred = self._make_copy()
335+
cred._account = account
336+
return cred
350337

351338
@_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
352339
def with_universe_domain(self, universe_domain):
353-
354-
return self.__class__(
355-
self.token,
356-
refresh_token=self.refresh_token,
357-
id_token=self.id_token,
358-
token_uri=self._token_uri,
359-
client_id=self.client_id,
360-
client_secret=self.client_secret,
361-
scopes=self.scopes,
362-
default_scopes=self.default_scopes,
363-
granted_scopes=self.granted_scopes,
364-
quota_project_id=self.quota_project_id,
365-
rapt_token=self.rapt_token,
366-
enable_reauth_refresh=self._enable_reauth_refresh,
367-
trust_boundary=self._trust_boundary,
368-
universe_domain=universe_domain,
369-
account=self._account,
370-
)
340+
cred = self._make_copy()
341+
cred._universe_domain = universe_domain
342+
return cred
371343

372344
def _metric_header_for_usage(self):
373345
return metrics.CRED_TYPE_USER

google/oauth2/service_account.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272

7373
import copy
7474
import datetime
75+
import json
7576

7677
from google.auth import _helpers
7778
from google.auth import _service_account_info
@@ -173,6 +174,7 @@ def __init__(
173174
"""
174175
super(Credentials, self).__init__()
175176

177+
self._cred_file_path = None
176178
self._scopes = scopes
177179
self._default_scopes = default_scopes
178180
self._signer = signer
@@ -220,7 +222,7 @@ def _from_signer_and_info(cls, signer, info, **kwargs):
220222
"universe_domain", credentials.DEFAULT_UNIVERSE_DOMAIN
221223
),
222224
trust_boundary=info.get("trust_boundary"),
223-
**kwargs
225+
**kwargs,
224226
)
225227

226228
@classmethod
@@ -294,6 +296,7 @@ def _make_copy(self):
294296
always_use_jwt_access=self._always_use_jwt_access,
295297
universe_domain=self._universe_domain,
296298
)
299+
cred._cred_file_path = self._cred_file_path
297300
return cred
298301

299302
@_helpers.copy_docstring(credentials.Scoped)
@@ -503,6 +506,16 @@ def signer(self):
503506
def signer_email(self):
504507
return self._service_account_email
505508

509+
@_helpers.copy_docstring(credentials.Credentials)
510+
def get_cred_info(self):
511+
if self._cred_file_path:
512+
return {
513+
"credential_source": self._cred_file_path,
514+
"credential_type": "service account credentials",
515+
"principal": self.service_account_email,
516+
}
517+
return None
518+
506519

507520
class IDTokenCredentials(
508521
credentials.Signing,

0 commit comments

Comments
 (0)