4040import com .google .common .annotations .VisibleForTesting ;
4141import com .google .common .base .MoreObjects ;
4242import com .google .common .base .MoreObjects .ToStringHelper ;
43+ import com .google .common .base .Strings ;
4344import com .google .common .collect .ImmutableList ;
45+ import com .google .common .collect .ImmutableMap ;
4446import com .google .errorprone .annotations .CanIgnoreReturnValue ;
4547import java .io .IOException ;
4648import java .io .InputStream ;
@@ -60,9 +62,46 @@ public class GoogleCredentials extends OAuth2Credentials implements QuotaProject
6062 private static final long serialVersionUID = -1522852442442473691L ;
6163
6264 static final String QUOTA_PROJECT_ID_HEADER_KEY = "x-goog-user-project" ;
63- static final String USER_FILE_TYPE = "authorized_user" ;
64- static final String SERVICE_ACCOUNT_FILE_TYPE = "service_account" ;
65- static final String GDCH_SERVICE_ACCOUNT_FILE_TYPE = "gdch_service_account" ;
65+
66+ /** Internal Enum info mapping for GoogleCredential subclasses */
67+ enum GoogleCredentialsInfo {
68+ USER_CREDENTIALS ("User Credentials" , "authorized_user" ),
69+ SERVICE_ACCOUNT_CREDENTIALS ("Service Account Credentials" , "service_account" ),
70+ GDCH_CREDENTIALS ("GDCH Credentials" , "gdch_service_account" ),
71+ EXTERNAL_ACCOUNT_CREDENTIALS ("External Account Credentials" , "external_account" ),
72+ EXTERNAL_ACCOUNT_AUTHORIZED_USER_CREDENTIALS (
73+ "External Account Authorized User Credentials" , "external_account_authorized_user" ),
74+ IMPERSONATED_CREDENTIALS ("Impersonated Credentials" , "impersonated_service_account" ),
75+ APP_ENGINE_CREDENTIALS ("App Engine Credentials" , null ),
76+ CLOUD_SHELL_CREDENTIALS ("Cloud Shell Credentials" , null ),
77+ COMPUTE_ENGINE_CREDENTIALS ("Compute Engine Credentials" , null );
78+
79+ private final String credentialName ;
80+ @ Nullable private final String fileType ;
81+
82+ GoogleCredentialsInfo (String credentialName , String fileType ) {
83+ this .credentialName = credentialName ;
84+ this .fileType = fileType ;
85+ }
86+
87+ String getCredentialName () {
88+ return credentialName ;
89+ }
90+
91+ @ Nullable
92+ String getFileType () {
93+ return fileType ;
94+ }
95+ }
96+
97+ // The following package-private fields to provide additional info for errors message
98+ // Source of the credential (e.g. env var value or well know file location)
99+ String source ;
100+ // User-friendly name of the Credential class
101+ String name ;
102+ // Identity of the credential
103+ // Note: This field may contain data such as serviceAccountEmail which should not be serialized
104+ transient String principal ;
66105
67106 private final String universeDomain ;
68107 private final boolean isExplicitUniverseDomain ;
@@ -207,36 +246,36 @@ public static GoogleCredentials fromStream(
207246 throw new IOException ("Error reading credentials from stream, 'type' field not specified." );
208247 }
209248
210- if (USER_FILE_TYPE .equals (fileType )) {
249+ if (fileType .equals (GoogleCredentialsInfo . USER_CREDENTIALS . getFileType () )) {
211250 return UserCredentials .fromJson (fileContents , transportFactory );
212251 }
213- if (SERVICE_ACCOUNT_FILE_TYPE .equals (fileType )) {
252+ if (fileType .equals (GoogleCredentialsInfo . SERVICE_ACCOUNT_CREDENTIALS . getFileType () )) {
214253 return ServiceAccountCredentials .fromJson (fileContents , transportFactory );
215254 }
216- if (GDCH_SERVICE_ACCOUNT_FILE_TYPE .equals (fileType )) {
255+ if (fileType .equals (GoogleCredentialsInfo . GDCH_CREDENTIALS . getFileType () )) {
217256 return GdchCredentials .fromJson (fileContents );
218257 }
219- if (ExternalAccountCredentials . EXTERNAL_ACCOUNT_FILE_TYPE . equals (fileType )) {
258+ if (fileType . equals (GoogleCredentialsInfo . EXTERNAL_ACCOUNT_CREDENTIALS . getFileType () )) {
220259 return ExternalAccountCredentials .fromJson (fileContents , transportFactory );
221260 }
222- if (ExternalAccountAuthorizedUserCredentials . EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE_TYPE .equals (
223- fileType )) {
261+ if (fileType .equals (
262+ GoogleCredentialsInfo . EXTERNAL_ACCOUNT_AUTHORIZED_USER_CREDENTIALS . getFileType () )) {
224263 return ExternalAccountAuthorizedUserCredentials .fromJson (fileContents , transportFactory );
225264 }
226- if (ImpersonatedCredentials . IMPERSONATED_CREDENTIALS_FILE_TYPE . equals (fileType )) {
265+ if (fileType . equals (GoogleCredentialsInfo . IMPERSONATED_CREDENTIALS . getFileType () )) {
227266 return ImpersonatedCredentials .fromJson (fileContents , transportFactory );
228267 }
229268 throw new IOException (
230269 String .format (
231270 "Error reading credentials from stream, 'type' value '%s' not recognized."
232271 + " Valid values are '%s', '%s', '%s', '%s', '%s', '%s'." ,
233272 fileType ,
234- USER_FILE_TYPE ,
235- SERVICE_ACCOUNT_FILE_TYPE ,
236- GDCH_SERVICE_ACCOUNT_FILE_TYPE ,
237- ExternalAccountCredentials . EXTERNAL_ACCOUNT_FILE_TYPE ,
238- ExternalAccountAuthorizedUserCredentials . EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE_TYPE ,
239- ImpersonatedCredentials . IMPERSONATED_CREDENTIALS_FILE_TYPE ));
273+ GoogleCredentialsInfo . USER_CREDENTIALS . getFileType () ,
274+ GoogleCredentialsInfo . SERVICE_ACCOUNT_CREDENTIALS . getFileType () ,
275+ GoogleCredentialsInfo . GDCH_CREDENTIALS . getFileType () ,
276+ GoogleCredentialsInfo . EXTERNAL_ACCOUNT_CREDENTIALS . getFileType () ,
277+ GoogleCredentialsInfo . EXTERNAL_ACCOUNT_AUTHORIZED_USER_CREDENTIALS . getFileType () ,
278+ GoogleCredentialsInfo . IMPERSONATED_CREDENTIALS . getFileType () ));
240279 }
241280
242281 /**
@@ -359,6 +398,8 @@ protected GoogleCredentials(Builder builder) {
359398 this .universeDomain = builder .getUniverseDomain ();
360399 this .isExplicitUniverseDomain = true ;
361400 }
401+
402+ this .source = builder .source ;
362403 }
363404
364405 /**
@@ -497,9 +538,53 @@ public GoogleCredentials createDelegated(String user) {
497538 return this ;
498539 }
499540
541+ /**
542+ * Internal method meant to help provide information for how certain Credential objects were
543+ * initialized by ADC (e.g. The well-known file location or env var)
544+ */
545+ GoogleCredentials withSource (String source ) {
546+ return toBuilder ().setSource (source ).build ();
547+ }
548+
549+ /**
550+ * Provides additional information regarding credential initialization source
551+ *
552+ * <ul>
553+ * <li>credential source - Initialized via the GOOGLE_APPLICATION_CREDENTIALS env var or well
554+ * known file type
555+ * <li>credential name - The user-friendly name of the credential created
556+ * <li>principal - Identity used for the credential
557+ * </ul>
558+ *
559+ * Unknown field values (i.e. null) are not included in the mapping (e.g. ComputeCredentials may
560+ * not know the principal value until after a call to MDS is made and the field will be excluded
561+ * if `getCredentialInfo` is called prior to retrieving that value). A new map of the fields is
562+ * created on every time this method is called as fields may be updated throughout the Credential
563+ * lifecycle. This mapping is intended to provide information about the Credential that is created
564+ * via ADC. Some fields may not be known if a Credential is directly created (e.g.
565+ * `ServiceAccountCredential.fromStream(InputStream)` may not know the source of the file stream).
566+ * These fields are populated on a best effort basis.
567+ *
568+ * @return ImmutableMap of information regarding how the Credential was initialized
569+ */
570+ public Map <String , String > getCredentialInfo () {
571+ Map <String , String > infoMap = new HashMap <>();
572+ if (!Strings .isNullOrEmpty (source )) {
573+ infoMap .put ("Credential Source" , source );
574+ }
575+ if (!Strings .isNullOrEmpty (name )) {
576+ infoMap .put ("Credential Name" , name );
577+ }
578+ if (!Strings .isNullOrEmpty (principal )) {
579+ infoMap .put ("Principal" , principal );
580+ }
581+ return ImmutableMap .copyOf (infoMap );
582+ }
583+
500584 public static class Builder extends OAuth2Credentials .Builder {
501585 @ Nullable protected String quotaProjectId ;
502586 @ Nullable protected String universeDomain ;
587+ @ Nullable String source ;
503588
504589 protected Builder () {}
505590
@@ -541,6 +626,11 @@ public String getUniverseDomain() {
541626 return this .universeDomain ;
542627 }
543628
629+ Builder setSource (String source ) {
630+ this .source = source ;
631+ return this ;
632+ }
633+
544634 @ Override
545635 @ CanIgnoreReturnValue
546636 public Builder setAccessToken (AccessToken token ) {
0 commit comments