• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *    * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *    * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *
15  *    * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 package com.google.auth.oauth2;
33 
34 import com.google.api.client.json.GenericJson;
35 import com.google.api.client.json.JsonFactory;
36 import com.google.api.client.json.JsonObjectParser;
37 import com.google.api.client.util.Preconditions;
38 import com.google.auth.Credentials;
39 import com.google.auth.http.HttpTransportFactory;
40 import com.google.common.annotations.VisibleForTesting;
41 import com.google.common.base.MoreObjects;
42 import com.google.common.base.MoreObjects.ToStringHelper;
43 import com.google.common.collect.ImmutableList;
44 import com.google.errorprone.annotations.CanIgnoreReturnValue;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.nio.charset.StandardCharsets;
48 import java.time.Duration;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Objects;
55 import javax.annotation.Nullable;
56 
57 /** Base type for credentials for authorizing calls to Google APIs using OAuth2. */
58 public class GoogleCredentials extends OAuth2Credentials implements QuotaProjectIdProvider {
59 
60   private static final long serialVersionUID = -1522852442442473691L;
61 
62   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";
66 
67   private final String universeDomain;
68   private final boolean isExplicitUniverseDomain;
69 
70   protected final String quotaProjectId;
71 
72   private static final DefaultCredentialsProvider defaultCredentialsProvider =
73       new DefaultCredentialsProvider();
74 
75   /**
76    * Returns the credentials instance from the given access token.
77    *
78    * @param accessToken the access token
79    * @return the credentials instance
80    */
create(AccessToken accessToken)81   public static GoogleCredentials create(AccessToken accessToken) {
82     return GoogleCredentials.newBuilder().setAccessToken(accessToken).build();
83   }
84 
85   /**
86    * Returns the credentials instance from the given access token and universe domain.
87    *
88    * @param universeDomain the universe domain
89    * @param accessToken the access token
90    * @return the credentials instance
91    */
create(String universeDomain, AccessToken accessToken)92   public static GoogleCredentials create(String universeDomain, AccessToken accessToken) {
93     return GoogleCredentials.newBuilder()
94         .setAccessToken(accessToken)
95         .setUniverseDomain(universeDomain)
96         .build();
97   }
98 
99   /**
100    * Returns the Application Default Credentials.
101    *
102    * <p>Returns the Application Default Credentials which are used to identify and authorize the
103    * whole application. The following are searched (in order) to find the Application Default
104    * Credentials:
105    *
106    * <ol>
107    *   <li>Credentials file pointed to by the {@code GOOGLE_APPLICATION_CREDENTIALS} environment
108    *       variable
109    *   <li>Credentials provided by the Google Cloud SDK.
110    *       <ol>
111    *         <li>{@code gcloud auth application-default login} for user account credentials.
112    *         <li>{@code gcloud auth application-default login --impersonate-service-account} for
113    *             impersonated service account credentials.
114    *       </ol>
115    *   <li>Google App Engine built-in credentials
116    *   <li>Google Cloud Shell built-in credentials
117    *   <li>Google Compute Engine built-in credentials
118    * </ol>
119    *
120    * @return the credentials instance.
121    * @throws IOException if the credentials cannot be created in the current environment.
122    */
getApplicationDefault()123   public static GoogleCredentials getApplicationDefault() throws IOException {
124     return getApplicationDefault(OAuth2Utils.HTTP_TRANSPORT_FACTORY);
125   }
126 
127   /**
128    * Returns the Application Default Credentials.
129    *
130    * <p>Returns the Application Default Credentials which are used to identify and authorize the
131    * whole application. The following are searched (in order) to find the Application Default
132    * Credentials:
133    *
134    * <ol>
135    *   <li>Credentials file pointed to by the {@code GOOGLE_APPLICATION_CREDENTIALS} environment
136    *       variable
137    *   <li>Credentials provided by the Google Cloud SDK {@code gcloud auth application-default
138    *       login} command
139    *   <li>Google App Engine built-in credentials
140    *   <li>Google Cloud Shell built-in credentials
141    *   <li>Google Compute Engine built-in credentials
142    * </ol>
143    *
144    * @param transportFactory HTTP transport factory, creates the transport used to get access
145    *     tokens.
146    * @return the credentials instance.
147    * @throws IOException if the credentials cannot be created in the current environment.
148    */
getApplicationDefault(HttpTransportFactory transportFactory)149   public static GoogleCredentials getApplicationDefault(HttpTransportFactory transportFactory)
150       throws IOException {
151     Preconditions.checkNotNull(transportFactory);
152     return defaultCredentialsProvider.getDefaultCredentials(transportFactory);
153   }
154 
155   /**
156    * Returns credentials defined by a JSON file stream.
157    *
158    * <p>The stream can contain a Service Account key file in JSON format from the Google Developers
159    * Console or a stored user credential using the format supported by the Cloud SDK.
160    *
161    * @param credentialsStream the stream with the credential definition.
162    * @return the credential defined by the credentialsStream.
163    * @throws IOException if the credential cannot be created from the stream.
164    */
fromStream(InputStream credentialsStream)165   public static GoogleCredentials fromStream(InputStream credentialsStream) throws IOException {
166     return fromStream(credentialsStream, OAuth2Utils.HTTP_TRANSPORT_FACTORY);
167   }
168 
169   /**
170    * Returns credentials defined by a JSON file stream.
171    *
172    * <p>The stream can contain a Service Account key file in JSON format from the Google Developers
173    * Console or a stored user credential using the format supported by the Cloud SDK.
174    *
175    * @param credentialsStream the stream with the credential definition.
176    * @param transportFactory HTTP transport factory, creates the transport used to get access
177    *     tokens.
178    * @return the credential defined by the credentialsStream.
179    * @throws IOException if the credential cannot be created from the stream.
180    */
fromStream( InputStream credentialsStream, HttpTransportFactory transportFactory)181   public static GoogleCredentials fromStream(
182       InputStream credentialsStream, HttpTransportFactory transportFactory) throws IOException {
183     Preconditions.checkNotNull(credentialsStream);
184     Preconditions.checkNotNull(transportFactory);
185 
186     JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY;
187     JsonObjectParser parser = new JsonObjectParser(jsonFactory);
188     GenericJson fileContents =
189         parser.parseAndClose(credentialsStream, StandardCharsets.UTF_8, GenericJson.class);
190 
191     String fileType = (String) fileContents.get("type");
192     if (fileType == null) {
193       throw new IOException("Error reading credentials from stream, 'type' field not specified.");
194     }
195 
196     if (USER_FILE_TYPE.equals(fileType)) {
197       return UserCredentials.fromJson(fileContents, transportFactory);
198     }
199     if (SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) {
200       return ServiceAccountCredentials.fromJson(fileContents, transportFactory);
201     }
202     if (GDCH_SERVICE_ACCOUNT_FILE_TYPE.equals(fileType)) {
203       return GdchCredentials.fromJson(fileContents);
204     }
205     if (ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE.equals(fileType)) {
206       return ExternalAccountCredentials.fromJson(fileContents, transportFactory);
207     }
208     if (ExternalAccountAuthorizedUserCredentials.EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE_TYPE.equals(
209         fileType)) {
210       return ExternalAccountAuthorizedUserCredentials.fromJson(fileContents, transportFactory);
211     }
212     if (ImpersonatedCredentials.IMPERSONATED_CREDENTIALS_FILE_TYPE.equals(fileType)) {
213       return ImpersonatedCredentials.fromJson(fileContents, transportFactory);
214     }
215     throw new IOException(
216         String.format(
217             "Error reading credentials from stream, 'type' value '%s' not recognized."
218                 + " Valid values are '%s', '%s', '%s', '%s', '%s', '%s'.",
219             fileType,
220             USER_FILE_TYPE,
221             SERVICE_ACCOUNT_FILE_TYPE,
222             GDCH_SERVICE_ACCOUNT_FILE_TYPE,
223             ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE,
224             ExternalAccountAuthorizedUserCredentials.EXTERNAL_ACCOUNT_AUTHORIZED_USER_FILE_TYPE,
225             ImpersonatedCredentials.IMPERSONATED_CREDENTIALS_FILE_TYPE));
226   }
227 
228   /**
229    * Creates a credential with the provided quota project.
230    *
231    * @param quotaProject the quota project to set on the credential
232    * @return credential with the provided quota project
233    */
createWithQuotaProject(String quotaProject)234   public GoogleCredentials createWithQuotaProject(String quotaProject) {
235     return this.toBuilder().setQuotaProjectId(quotaProject).build();
236   }
237 
238   /**
239    * Gets the universe domain for the credential.
240    *
241    * @return An explicit universe domain if it was explicitly provided, invokes the super
242    *     implementation otherwise
243    */
244   @Override
getUniverseDomain()245   public String getUniverseDomain() throws IOException {
246     return this.universeDomain;
247   }
248 
249   /**
250    * Gets the flag indicating whether universeDomain was explicitly set by the developer.
251    *
252    * <p>If subclass has a requirement to give priority to developer-set universeDomain, this
253    * property must be used to check if the universeDomain value was provided by the user. It could
254    * be a default otherwise.
255    *
256    * @return true if universeDomain value was provided by the developer, false otherwise
257    */
258   @VisibleForTesting
isExplicitUniverseDomain()259   protected boolean isExplicitUniverseDomain() {
260     return this.isExplicitUniverseDomain;
261   }
262 
263   /**
264    * Checks if universe domain equals to {@link Credentials#GOOGLE_DEFAULT_UNIVERSE}.
265    *
266    * @return true if universeDomain equals to {@link Credentials#GOOGLE_DEFAULT_UNIVERSE}, false
267    *     otherwise
268    */
isDefaultUniverseDomain()269   boolean isDefaultUniverseDomain() {
270     return this.universeDomain.equals(Credentials.GOOGLE_DEFAULT_UNIVERSE);
271   }
272 
273   /**
274    * Adds quota project ID to requestMetadata if present.
275    *
276    * @return a new map with quotaProjectId added if needed
277    */
addQuotaProjectIdToRequestMetadata( String quotaProjectId, Map<String, List<String>> requestMetadata)278   static Map<String, List<String>> addQuotaProjectIdToRequestMetadata(
279       String quotaProjectId, Map<String, List<String>> requestMetadata) {
280     Preconditions.checkNotNull(requestMetadata);
281     Map<String, List<String>> newRequestMetadata = new HashMap<>(requestMetadata);
282     if (quotaProjectId != null && !requestMetadata.containsKey(QUOTA_PROJECT_ID_HEADER_KEY)) {
283       newRequestMetadata.put(
284           QUOTA_PROJECT_ID_HEADER_KEY, Collections.singletonList(quotaProjectId));
285     }
286     return Collections.unmodifiableMap(newRequestMetadata);
287   }
288 
289   @Override
getAdditionalHeaders()290   protected Map<String, List<String>> getAdditionalHeaders() {
291     Map<String, List<String>> headers = super.getAdditionalHeaders();
292     String quotaProjectId = this.getQuotaProjectId();
293     if (quotaProjectId != null) {
294       return addQuotaProjectIdToRequestMetadata(quotaProjectId, headers);
295     }
296     return headers;
297   }
298 
299   /** Default constructor. */
GoogleCredentials()300   protected GoogleCredentials() {
301     this(new Builder());
302   }
303 
304   /**
305    * Constructor with an explicit access token and quotaProjectId.
306    *
307    * <p>Deprecated, please use the {@link GoogleCredentials#GoogleCredentials(Builder)} constructor
308    * whenever possible.
309    *
310    * @param accessToken initial or temporary access token
311    * @param quotaProjectId a quotaProjectId, a project id to be used for billing purposes
312    */
313   @Deprecated
GoogleCredentials(AccessToken accessToken, String quotaProjectId)314   protected GoogleCredentials(AccessToken accessToken, String quotaProjectId) {
315     this(
316         GoogleCredentials.newBuilder()
317             .setAccessToken(accessToken)
318             .setQuotaProjectId(quotaProjectId));
319   }
320 
321   /**
322    * Constructor with explicit access token.
323    *
324    * @param accessToken initial or temporary access token
325    */
326   @Deprecated
GoogleCredentials(AccessToken accessToken)327   public GoogleCredentials(AccessToken accessToken) {
328     this(accessToken, null);
329   }
330 
331   /**
332    * Constructor that relies on a {@link GoogleCredential.Builder} to provide all the necessary
333    * field values for initialization.
334    *
335    * @param builder an instance of a builder
336    */
GoogleCredentials(Builder builder)337   protected GoogleCredentials(Builder builder) {
338     super(builder.getAccessToken(), builder.getRefreshMargin(), builder.getExpirationMargin());
339     this.quotaProjectId = builder.getQuotaProjectId();
340 
341     if (builder.universeDomain == null || builder.universeDomain.trim().isEmpty()) {
342       this.universeDomain = Credentials.GOOGLE_DEFAULT_UNIVERSE;
343       this.isExplicitUniverseDomain = false;
344     } else {
345       this.universeDomain = builder.getUniverseDomain();
346       this.isExplicitUniverseDomain = true;
347     }
348   }
349 
350   /**
351    * Constructor with explicit access token and refresh margins.
352    *
353    * <p>Deprecated, please use the {@link GoogleCredentials#GoogleCredentials(Builder)} constructor
354    * whenever possible.
355    *
356    * @param accessToken initial or temporary access token
357    */
358   @Deprecated
GoogleCredentials( AccessToken accessToken, Duration refreshMargin, Duration expirationMargin)359   protected GoogleCredentials(
360       AccessToken accessToken, Duration refreshMargin, Duration expirationMargin) {
361     this(
362         (Builder)
363             GoogleCredentials.newBuilder()
364                 .setAccessToken(accessToken)
365                 .setRefreshMargin(refreshMargin)
366                 .setExpirationMargin(expirationMargin));
367   }
368 
369   /**
370    * A helper for overriding the toString() method. This allows inheritance of super class fields.
371    * Extending classes can override this implementation and call super implementation and add more
372    * fields. Same cannot be done with overriding the toString() directly.
373    *
374    * @return an instance of the ToStringHelper that has public fields added
375    */
toStringHelper()376   protected ToStringHelper toStringHelper() {
377     return MoreObjects.toStringHelper(this)
378         .omitNullValues()
379         .add("quotaProjectId", this.quotaProjectId)
380         .add("universeDomain", this.universeDomain)
381         .add("isExplicitUniverseDomain", this.isExplicitUniverseDomain);
382   }
383 
384   @Override
toString()385   public String toString() {
386     return toStringHelper().toString();
387   }
388 
389   @Override
equals(Object obj)390   public boolean equals(Object obj) {
391     if (!(obj instanceof GoogleCredentials)) {
392       return false;
393     }
394     GoogleCredentials other = (GoogleCredentials) obj;
395     return Objects.equals(this.quotaProjectId, other.quotaProjectId)
396         && Objects.equals(this.universeDomain, other.universeDomain)
397         && Objects.equals(this.isExplicitUniverseDomain, other.isExplicitUniverseDomain);
398   }
399 
400   @Override
hashCode()401   public int hashCode() {
402     return Objects.hash(this.quotaProjectId, this.universeDomain, this.isExplicitUniverseDomain);
403   }
404 
newBuilder()405   public static Builder newBuilder() {
406     return new Builder();
407   }
408 
409   @Override
toBuilder()410   public Builder toBuilder() {
411     return new Builder(this);
412   }
413 
414   @Override
getQuotaProjectId()415   public String getQuotaProjectId() {
416     return this.quotaProjectId;
417   }
418 
419   /**
420    * Indicates whether the credentials require scopes to be specified via a call to {@link
421    * GoogleCredentials#createScoped} before use.
422    *
423    * @return Whether the credentials require scopes to be specified.
424    */
createScopedRequired()425   public boolean createScopedRequired() {
426     return false;
427   }
428 
429   /**
430    * If the credentials support scopes, creates a copy of the identity with the specified scopes;
431    * otherwise, returns the same instance.
432    *
433    * @param scopes Collection of scopes to request.
434    * @return GoogleCredentials with requested scopes.
435    */
createScoped(Collection<String> scopes)436   public GoogleCredentials createScoped(Collection<String> scopes) {
437     return this;
438   }
439 
440   /**
441    * If the credentials support scopes, creates a copy of the identity with the specified scopes and
442    * default scopes; otherwise, returns the same instance. This is mainly used by client libraries.
443    *
444    * @param scopes Collection of scopes to request.
445    * @param defaultScopes Collection of default scopes to request.
446    * @return GoogleCredentials with requested scopes.
447    */
createScoped( Collection<String> scopes, Collection<String> defaultScopes)448   public GoogleCredentials createScoped(
449       Collection<String> scopes, Collection<String> defaultScopes) {
450     return this;
451   }
452 
453   /**
454    * If the credentials support scopes, creates a copy of the identity with the specified scopes;
455    * otherwise, returns the same instance.
456    *
457    * @param scopes Collection of scopes to request.
458    * @return GoogleCredentials with requested scopes.
459    */
createScoped(String... scopes)460   public GoogleCredentials createScoped(String... scopes) {
461     return createScoped(ImmutableList.copyOf(scopes));
462   }
463 
464   /**
465    * If the credentials support automatic retries, creates a copy of the identity with the provided
466    * retry strategy
467    *
468    * @param defaultRetriesEnabled a flag enabling or disabling default retries
469    * @return GoogleCredentials with the new default retries configuration.
470    */
createWithCustomRetryStrategy(boolean defaultRetriesEnabled)471   public GoogleCredentials createWithCustomRetryStrategy(boolean defaultRetriesEnabled) {
472     return this;
473   }
474 
475   /**
476    * If the credentials support domain-wide delegation, creates a copy of the identity so that it
477    * impersonates the specified user; otherwise, returns the same instance.
478    *
479    * @param user User to impersonate.
480    * @return GoogleCredentials with a delegated user.
481    */
createDelegated(String user)482   public GoogleCredentials createDelegated(String user) {
483     return this;
484   }
485 
486   public static class Builder extends OAuth2Credentials.Builder {
487     @Nullable protected String quotaProjectId;
488     @Nullable protected String universeDomain;
489 
Builder()490     protected Builder() {}
491 
Builder(GoogleCredentials credentials)492     protected Builder(GoogleCredentials credentials) {
493       super(credentials);
494       this.quotaProjectId = credentials.quotaProjectId;
495       if (credentials.isExplicitUniverseDomain) {
496         this.universeDomain = credentials.universeDomain;
497       }
498     }
499 
Builder(GoogleCredentials.Builder builder)500     protected Builder(GoogleCredentials.Builder builder) {
501       setAccessToken(builder.getAccessToken());
502       this.quotaProjectId = builder.quotaProjectId;
503       this.universeDomain = builder.universeDomain;
504     }
505 
506     @Override
build()507     public GoogleCredentials build() {
508       return new GoogleCredentials(this);
509     }
510 
511     @CanIgnoreReturnValue
setQuotaProjectId(String quotaProjectId)512     public Builder setQuotaProjectId(String quotaProjectId) {
513       this.quotaProjectId = quotaProjectId;
514       return this;
515     }
516 
setUniverseDomain(String universeDomain)517     public Builder setUniverseDomain(String universeDomain) {
518       this.universeDomain = universeDomain;
519       return this;
520     }
521 
getQuotaProjectId()522     public String getQuotaProjectId() {
523       return this.quotaProjectId;
524     }
525 
getUniverseDomain()526     public String getUniverseDomain() {
527       return this.universeDomain;
528     }
529 
530     @Override
531     @CanIgnoreReturnValue
setAccessToken(AccessToken token)532     public Builder setAccessToken(AccessToken token) {
533       super.setAccessToken(token);
534       return this;
535     }
536   }
537 }
538