• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
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 LLC 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 static com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE;
35 import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL;
36 import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY;
37 import static org.junit.Assert.*;
38 
39 import com.google.api.client.http.HttpTransport;
40 import com.google.api.client.json.GenericJson;
41 import com.google.api.client.util.Clock;
42 import com.google.auth.TestUtils;
43 import com.google.auth.http.HttpTransportFactory;
44 import java.io.ByteArrayInputStream;
45 import java.io.File;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.nio.charset.StandardCharsets;
49 import java.util.Arrays;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Map;
53 import javax.annotation.Nullable;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 import org.junit.runners.JUnit4;
57 
58 /** Tests for {@link IdentityPoolCredentials}. */
59 @RunWith(JUnit4.class)
60 public class IdentityPoolCredentialsTest extends BaseSerializationTest {
61 
62   private static final String STS_URL = "https://sts.googleapis.com/v1/token";
63 
64   private static final Map<String, Object> FILE_CREDENTIAL_SOURCE_MAP =
65       new HashMap<String, Object>() {
66         {
67           put("file", "file");
68         }
69       };
70 
71   private static final IdentityPoolCredentialSource FILE_CREDENTIAL_SOURCE =
72       new IdentityPoolCredentialSource(FILE_CREDENTIAL_SOURCE_MAP);
73 
74   private static final IdentityPoolCredentials FILE_SOURCED_CREDENTIAL =
75       IdentityPoolCredentials.newBuilder()
76           .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
77           .setAudience(
78               "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
79           .setSubjectTokenType("subjectTokenType")
80           .setTokenUrl(STS_URL)
81           .setTokenInfoUrl("tokenInfoUrl")
82           .setCredentialSource(FILE_CREDENTIAL_SOURCE)
83           .build();
84 
85   private static final IdentityPoolSubjectTokenSupplier testProvider =
86       (ExternalAccountSupplierContext context) -> "testSubjectToken";
87 
88   private static final ExternalAccountSupplierContext emptyContext =
89       ExternalAccountSupplierContext.newBuilder().setAudience("").setSubjectTokenType("").build();
90 
91   static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory {
92 
93     MockExternalAccountCredentialsTransport transport =
94         new MockExternalAccountCredentialsTransport();
95 
96     @Override
create()97     public HttpTransport create() {
98       return transport;
99     }
100   }
101 
102   @Test
createdScoped_clonedCredentialWithAddedScopes()103   public void createdScoped_clonedCredentialWithAddedScopes() throws IOException {
104     IdentityPoolCredentials credentials =
105         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
106             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
107             .setQuotaProjectId("quotaProjectId")
108             .setClientId("clientId")
109             .setClientSecret("clientSecret")
110             .setUniverseDomain("universeDomain")
111             .build();
112 
113     List<String> newScopes = Arrays.asList("scope1", "scope2");
114 
115     IdentityPoolCredentials newCredentials = credentials.createScoped(newScopes);
116 
117     assertEquals(credentials.getAudience(), newCredentials.getAudience());
118     assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType());
119     assertEquals(credentials.getTokenUrl(), newCredentials.getTokenUrl());
120     assertEquals(credentials.getTokenInfoUrl(), newCredentials.getTokenInfoUrl());
121     assertEquals(
122         credentials.getServiceAccountImpersonationUrl(),
123         newCredentials.getServiceAccountImpersonationUrl());
124     assertEquals(credentials.getCredentialSource(), newCredentials.getCredentialSource());
125     assertEquals(newScopes, newCredentials.getScopes());
126     assertEquals(credentials.getQuotaProjectId(), newCredentials.getQuotaProjectId());
127     assertEquals(credentials.getClientId(), newCredentials.getClientId());
128     assertEquals(credentials.getClientSecret(), newCredentials.getClientSecret());
129     assertEquals(credentials.getUniverseDomain(), newCredentials.getUniverseDomain());
130     assertEquals("universeDomain", newCredentials.getUniverseDomain());
131   }
132 
133   @Test
retrieveSubjectToken_fileSourced()134   public void retrieveSubjectToken_fileSourced() throws IOException {
135     File file =
136         File.createTempFile("RETRIEVE_SUBJECT_TOKEN", /* suffix= */ null, /* directory= */ null);
137     file.deleteOnExit();
138 
139     String credential = "credential";
140     OAuth2Utils.writeInputStreamToFile(
141         new ByteArrayInputStream(credential.getBytes(StandardCharsets.UTF_8)),
142         file.getAbsolutePath());
143 
144     Map<String, Object> credentialSourceMap = new HashMap<>();
145     credentialSourceMap.put("file", file.getAbsolutePath());
146     IdentityPoolCredentialSource credentialSource =
147         new IdentityPoolCredentialSource(credentialSourceMap);
148 
149     IdentityPoolCredentials credentials =
150         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
151             .setCredentialSource(credentialSource)
152             .build();
153 
154     String subjectToken = credentials.retrieveSubjectToken();
155 
156     assertEquals(credential, subjectToken);
157   }
158 
159   @Test
retrieveSubjectToken_fileSourcedWithJsonFormat()160   public void retrieveSubjectToken_fileSourcedWithJsonFormat() throws IOException {
161     File file =
162         File.createTempFile("RETRIEVE_SUBJECT_TOKEN", /* suffix= */ null, /* directory= */ null);
163     file.deleteOnExit();
164 
165     MockExternalAccountCredentialsTransportFactory transportFactory =
166         new MockExternalAccountCredentialsTransportFactory();
167 
168     transportFactory.transport.setMetadataServerContentType("json");
169 
170     Map<String, Object> credentialSourceMap = new HashMap<>();
171     Map<String, String> formatMap = new HashMap<>();
172     formatMap.put("type", "json");
173     formatMap.put("subject_token_field_name", "subjectToken");
174 
175     credentialSourceMap.put("file", file.getAbsolutePath());
176     credentialSourceMap.put("format", formatMap);
177 
178     IdentityPoolCredentialSource credentialSource =
179         new IdentityPoolCredentialSource(credentialSourceMap);
180 
181     GenericJson response = new GenericJson();
182     response.setFactory(JSON_FACTORY);
183     response.put("subjectToken", "subjectToken");
184 
185     OAuth2Utils.writeInputStreamToFile(
186         new ByteArrayInputStream(response.toString().getBytes(StandardCharsets.UTF_8)),
187         file.getAbsolutePath());
188 
189     IdentityPoolCredentials credential =
190         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
191             .setHttpTransportFactory(transportFactory)
192             .setCredentialSource(credentialSource)
193             .build();
194 
195     String subjectToken = credential.retrieveSubjectToken();
196 
197     assertEquals("subjectToken", subjectToken);
198   }
199 
200   @Test
retrieveSubjectToken_fileSourcedWithNullFormat_throws()201   public void retrieveSubjectToken_fileSourcedWithNullFormat_throws() throws IOException {
202     File file =
203         File.createTempFile("RETRIEVE_SUBJECT_TOKEN", /* suffix= */ null, /* directory= */ null);
204     file.deleteOnExit();
205 
206     Map<String, Object> credentialSourceMap = new HashMap<>();
207     Map<String, String> formatMap = new HashMap<>();
208     formatMap.put("type", null);
209 
210     credentialSourceMap.put("file", file.getAbsolutePath());
211     credentialSourceMap.put("format", formatMap);
212 
213     try {
214       new IdentityPoolCredentialSource(credentialSourceMap);
215       fail("Exception should be thrown due to null format.");
216     } catch (IllegalArgumentException e) {
217       assertEquals("Invalid credential source format type: null.", e.getMessage());
218     }
219   }
220 
221   @Test
retrieveSubjectToken_noFile_throws()222   public void retrieveSubjectToken_noFile_throws() {
223     Map<String, Object> credentialSourceMap = new HashMap<>();
224     String path = "badPath";
225     credentialSourceMap.put("file", path);
226     IdentityPoolCredentialSource credentialSource =
227         new IdentityPoolCredentialSource(credentialSourceMap);
228 
229     IdentityPoolCredentials credentials =
230         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
231             .setCredentialSource(credentialSource)
232             .build();
233 
234     try {
235       credentials.retrieveSubjectToken();
236       fail("Exception should be thrown.");
237     } catch (IOException e) {
238       assertEquals(
239           String.format("Invalid credential location. The file at %s does not exist.", path),
240           e.getMessage());
241     }
242   }
243 
244   @Test
retrieveSubjectToken_urlSourced()245   public void retrieveSubjectToken_urlSourced() throws IOException {
246     MockExternalAccountCredentialsTransportFactory transportFactory =
247         new MockExternalAccountCredentialsTransportFactory();
248 
249     IdentityPoolCredentials credential =
250         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
251             .setHttpTransportFactory(transportFactory)
252             .setCredentialSource(
253                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
254             .build();
255 
256     String subjectToken = credential.retrieveSubjectToken();
257 
258     assertEquals(transportFactory.transport.getSubjectToken(), subjectToken);
259   }
260 
261   @Test
retrieveSubjectToken_urlSourcedWithJsonFormat()262   public void retrieveSubjectToken_urlSourcedWithJsonFormat() throws IOException {
263     MockExternalAccountCredentialsTransportFactory transportFactory =
264         new MockExternalAccountCredentialsTransportFactory();
265 
266     transportFactory.transport.setMetadataServerContentType("json");
267 
268     Map<String, String> formatMap = new HashMap<>();
269     formatMap.put("type", "json");
270     formatMap.put("subject_token_field_name", "subjectToken");
271 
272     IdentityPoolCredentialSource credentialSource =
273         buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl(), formatMap);
274 
275     IdentityPoolCredentials credential =
276         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
277             .setHttpTransportFactory(transportFactory)
278             .setCredentialSource(credentialSource)
279             .build();
280 
281     String subjectToken = credential.retrieveSubjectToken();
282 
283     assertEquals(transportFactory.transport.getSubjectToken(), subjectToken);
284   }
285 
286   @Test
retrieveSubjectToken_urlSourcedCredential_throws()287   public void retrieveSubjectToken_urlSourcedCredential_throws() {
288     MockExternalAccountCredentialsTransportFactory transportFactory =
289         new MockExternalAccountCredentialsTransportFactory();
290 
291     IOException response = new IOException();
292     transportFactory.transport.addResponseErrorSequence(response);
293 
294     IdentityPoolCredentials credential =
295         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
296             .setHttpTransportFactory(transportFactory)
297             .setCredentialSource(
298                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
299             .build();
300 
301     try {
302       credential.retrieveSubjectToken();
303       fail("Exception should be thrown.");
304     } catch (IOException e) {
305       assertEquals(
306           String.format(
307               "Error getting subject token from metadata server: %s", response.getMessage()),
308           e.getMessage());
309     }
310   }
311 
312   @Test
retrieveSubjectToken_provider()313   public void retrieveSubjectToken_provider() throws IOException {
314 
315     IdentityPoolCredentials credentials =
316         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
317             .setCredentialSource(null)
318             .setSubjectTokenSupplier(testProvider)
319             .build();
320 
321     String subjectToken = credentials.retrieveSubjectToken();
322 
323     assertEquals(testProvider.getSubjectToken(emptyContext), subjectToken);
324   }
325 
326   @Test
retrieveSubjectToken_providerThrowsError()327   public void retrieveSubjectToken_providerThrowsError() throws IOException {
328     IOException testException = new IOException("test");
329 
330     IdentityPoolSubjectTokenSupplier errorProvider =
331         (ExternalAccountSupplierContext context) -> {
332           throw testException;
333         };
334     IdentityPoolCredentials credentials =
335         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
336             .setCredentialSource(null)
337             .setSubjectTokenSupplier(errorProvider)
338             .build();
339 
340     try {
341       String subjectToken = credentials.retrieveSubjectToken();
342       fail("retrieveSubjectToken should fail.");
343     } catch (IOException e) {
344       assertEquals("test", e.getMessage());
345     }
346   }
347 
348   @Test
retrieveSubjectToken_supplierPassesContext()349   public void retrieveSubjectToken_supplierPassesContext() throws IOException {
350     ExternalAccountSupplierContext expectedContext =
351         ExternalAccountSupplierContext.newBuilder()
352             .setAudience(FILE_SOURCED_CREDENTIAL.getAudience())
353             .setSubjectTokenType(FILE_SOURCED_CREDENTIAL.getSubjectTokenType())
354             .build();
355 
356     IdentityPoolSubjectTokenSupplier testSupplier =
357         (ExternalAccountSupplierContext context) -> {
358           assertEquals(expectedContext.getAudience(), context.getAudience());
359           assertEquals(expectedContext.getSubjectTokenType(), context.getSubjectTokenType());
360           return "token";
361         };
362     IdentityPoolCredentials credentials =
363         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
364             .setCredentialSource(null)
365             .setSubjectTokenSupplier(testSupplier)
366             .build();
367 
368     credentials.retrieveSubjectToken();
369   }
370 
371   @Test
refreshAccessToken_withoutServiceAccountImpersonation()372   public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException {
373     MockExternalAccountCredentialsTransportFactory transportFactory =
374         new MockExternalAccountCredentialsTransportFactory();
375 
376     IdentityPoolCredentials credential =
377         IdentityPoolCredentials.newBuilder()
378             .setAudience(
379                 "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
380             .setSubjectTokenType("subjectTokenType")
381             .setTokenInfoUrl("tokenInfoUrl")
382             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
383             .setTokenUrl(transportFactory.transport.getStsUrl())
384             .setHttpTransportFactory(transportFactory)
385             .setCredentialSource(
386                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
387             .build();
388 
389     AccessToken accessToken = credential.refreshAccessToken();
390 
391     assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue());
392 
393     // Validate metrics header is set correctly on the sts request.
394     Map<String, List<String>> headers =
395         transportFactory.transport.getRequests().get(1).getHeaders();
396     ExternalAccountCredentialsTest.validateMetricsHeader(headers, "url", false, false);
397   }
398 
399   @Test
refreshAccessToken_internalOptionsSet()400   public void refreshAccessToken_internalOptionsSet() throws IOException {
401     MockExternalAccountCredentialsTransportFactory transportFactory =
402         new MockExternalAccountCredentialsTransportFactory();
403 
404     IdentityPoolCredentials credential =
405         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
406             .setWorkforcePoolUserProject("userProject")
407             .setAudience(
408                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
409             .setTokenUrl(transportFactory.transport.getStsUrl())
410             .setHttpTransportFactory(transportFactory)
411             .setCredentialSource(
412                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
413             .build();
414 
415     AccessToken accessToken = credential.refreshAccessToken();
416 
417     assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue());
418 
419     // If the IdentityPoolCredential is initialized with a userProject, it must be passed
420     // to STS via internal options.
421     Map<String, String> query =
422         TestUtils.parseQuery(transportFactory.transport.getLastRequest().getContentAsString());
423     assertNotNull(query.get("options"));
424 
425     GenericJson expectedInternalOptions = new GenericJson();
426     expectedInternalOptions.setFactory(OAuth2Utils.JSON_FACTORY);
427     expectedInternalOptions.put("userProject", "userProject");
428 
429     assertEquals(expectedInternalOptions.toString(), query.get("options"));
430   }
431 
432   @Test
refreshAccessToken_withServiceAccountImpersonation()433   public void refreshAccessToken_withServiceAccountImpersonation() throws IOException {
434     MockExternalAccountCredentialsTransportFactory transportFactory =
435         new MockExternalAccountCredentialsTransportFactory();
436 
437     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
438     IdentityPoolCredentials credential =
439         IdentityPoolCredentials.newBuilder()
440             .setAudience(
441                 "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
442             .setSubjectTokenType("subjectTokenType")
443             .setTokenInfoUrl("tokenInfoUrl")
444             .setServiceAccountImpersonationUrl(
445                 transportFactory.transport.getServiceAccountImpersonationUrl())
446             .setTokenUrl(transportFactory.transport.getStsUrl())
447             .setHttpTransportFactory(transportFactory)
448             .setCredentialSource(
449                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
450             .build();
451 
452     AccessToken accessToken = credential.refreshAccessToken();
453 
454     assertEquals(
455         transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue());
456 
457     // Validate metrics header is set correctly on the sts request.
458     Map<String, List<String>> headers =
459         transportFactory.transport.getRequests().get(2).getHeaders();
460     ExternalAccountCredentialsTest.validateMetricsHeader(headers, "url", true, false);
461   }
462 
463   @Test
refreshAccessToken_withServiceAccountImpersonationOptions()464   public void refreshAccessToken_withServiceAccountImpersonationOptions() throws IOException {
465     MockExternalAccountCredentialsTransportFactory transportFactory =
466         new MockExternalAccountCredentialsTransportFactory();
467 
468     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
469     IdentityPoolCredentials credential =
470         IdentityPoolCredentials.newBuilder()
471             .setAudience(
472                 "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
473             .setSubjectTokenType("subjectTokenType")
474             .setTokenInfoUrl("tokenInfoUrl")
475             .setTokenUrl(transportFactory.transport.getStsUrl())
476             .setHttpTransportFactory(transportFactory)
477             .setServiceAccountImpersonationUrl(
478                 transportFactory.transport.getServiceAccountImpersonationUrl())
479             .setCredentialSource(
480                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
481             .setServiceAccountImpersonationOptions(
482                 ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800))
483             .build();
484 
485     AccessToken accessToken = credential.refreshAccessToken();
486 
487     assertEquals(
488         transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue());
489 
490     // Validate that default lifetime was set correctly on the request.
491     GenericJson query =
492         OAuth2Utils.JSON_FACTORY
493             .createJsonParser(transportFactory.transport.getLastRequest().getContentAsString())
494             .parseAndClose(GenericJson.class);
495 
496     assertEquals("2800s", query.get("lifetime"));
497 
498     // Validate metrics header is set correctly on the sts request.
499     Map<String, List<String>> headers =
500         transportFactory.transport.getRequests().get(2).getHeaders();
501     ExternalAccountCredentialsTest.validateMetricsHeader(headers, "url", true, true);
502   }
503 
504   @Test
refreshAccessToken_Provider()505   public void refreshAccessToken_Provider() throws IOException {
506     MockExternalAccountCredentialsTransportFactory transportFactory =
507         new MockExternalAccountCredentialsTransportFactory();
508 
509     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
510     IdentityPoolCredentials credential =
511         IdentityPoolCredentials.newBuilder()
512             .setSubjectTokenSupplier(testProvider)
513             .setAudience(
514                 "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
515             .setSubjectTokenType("subjectTokenType")
516             .setTokenInfoUrl("tokenInfoUrl")
517             .setTokenUrl(transportFactory.transport.getStsUrl())
518             .setHttpTransportFactory(transportFactory)
519             .build();
520 
521     AccessToken accessToken = credential.refreshAccessToken();
522 
523     assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue());
524 
525     // Validate metrics header is set correctly on the sts request.
526     Map<String, List<String>> headers =
527         transportFactory.transport.getRequests().get(0).getHeaders();
528     ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", false, false);
529   }
530 
531   @Test
refreshAccessToken_providerWithServiceAccountImpersonation()532   public void refreshAccessToken_providerWithServiceAccountImpersonation() throws IOException {
533     MockExternalAccountCredentialsTransportFactory transportFactory =
534         new MockExternalAccountCredentialsTransportFactory();
535 
536     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
537     IdentityPoolCredentials credential =
538         IdentityPoolCredentials.newBuilder()
539             .setSubjectTokenSupplier(testProvider)
540             .setAudience(
541                 "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider")
542             .setSubjectTokenType("subjectTokenType")
543             .setTokenInfoUrl("tokenInfoUrl")
544             .setServiceAccountImpersonationUrl(
545                 transportFactory.transport.getServiceAccountImpersonationUrl())
546             .setTokenUrl(transportFactory.transport.getStsUrl())
547             .setHttpTransportFactory(transportFactory)
548             .build();
549 
550     AccessToken accessToken = credential.refreshAccessToken();
551 
552     assertEquals(
553         transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue());
554 
555     // Validate metrics header is set correctly on the sts request.
556     Map<String, List<String>> headers =
557         transportFactory.transport.getRequests().get(0).getHeaders();
558     ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", true, false);
559   }
560 
561   @Test
refreshAccessToken_workforceWithServiceAccountImpersonation()562   public void refreshAccessToken_workforceWithServiceAccountImpersonation() throws IOException {
563     MockExternalAccountCredentialsTransportFactory transportFactory =
564         new MockExternalAccountCredentialsTransportFactory();
565 
566     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
567     IdentityPoolCredentials credential =
568         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
569             .setAudience(
570                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
571             .setTokenUrl(transportFactory.transport.getStsUrl())
572             .setServiceAccountImpersonationUrl(
573                 transportFactory.transport.getServiceAccountImpersonationUrl())
574             .setHttpTransportFactory(transportFactory)
575             .setCredentialSource(
576                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
577             .setWorkforcePoolUserProject("userProject")
578             .build();
579 
580     AccessToken accessToken = credential.refreshAccessToken();
581 
582     assertEquals(
583         transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue());
584 
585     // Validate internal options set.
586     Map<String, String> query = TestUtils.parseQuery(transportFactory.transport.getStsContent());
587 
588     GenericJson expectedInternalOptions = new GenericJson();
589     expectedInternalOptions.setFactory(OAuth2Utils.JSON_FACTORY);
590     expectedInternalOptions.put("userProject", "userProject");
591 
592     assertNotNull(query.get("options"));
593     assertEquals(expectedInternalOptions.toString(), query.get("options"));
594   }
595 
596   @Test
refreshAccessToken_workforceWithServiceAccountImpersonationOptions()597   public void refreshAccessToken_workforceWithServiceAccountImpersonationOptions()
598       throws IOException {
599     MockExternalAccountCredentialsTransportFactory transportFactory =
600         new MockExternalAccountCredentialsTransportFactory();
601 
602     transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime());
603     IdentityPoolCredentials credential =
604         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
605             .setAudience(
606                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
607             .setTokenUrl(transportFactory.transport.getStsUrl())
608             .setServiceAccountImpersonationUrl(
609                 transportFactory.transport.getServiceAccountImpersonationUrl())
610             .setHttpTransportFactory(transportFactory)
611             .setCredentialSource(
612                 buildUrlBasedCredentialSource(transportFactory.transport.getMetadataUrl()))
613             .setWorkforcePoolUserProject("userProject")
614             .setServiceAccountImpersonationOptions(
615                 ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800))
616             .build();
617 
618     AccessToken accessToken = credential.refreshAccessToken();
619 
620     // Validate that default lifetime was set correctly on the request.
621     assertEquals(
622         transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue());
623 
624     GenericJson query =
625         OAuth2Utils.JSON_FACTORY
626             .createJsonParser(transportFactory.transport.getLastRequest().getContentAsString())
627             .parseAndClose(GenericJson.class);
628 
629     assertEquals("2800s", query.get("lifetime"));
630   }
631 
632   @Test
identityPoolCredentialSource_validFormats()633   public void identityPoolCredentialSource_validFormats() {
634     Map<String, Object> credentialSourceMapWithFileTextSource = new HashMap<>();
635     Map<String, Object> credentialSourceMapWithFileJsonTextSource = new HashMap<>();
636     Map<String, Object> credentialSourceMapWithUrlTextSource = new HashMap<>();
637     Map<String, Object> credentialSourceMapWithUrlJsonTextSource = new HashMap<>();
638 
639     credentialSourceMapWithFileTextSource.put("file", "/path/to/file");
640     credentialSourceMapWithFileJsonTextSource.put("file", "/path/to/file");
641 
642     credentialSourceMapWithUrlTextSource.put("url", "https://google.com");
643     credentialSourceMapWithUrlJsonTextSource.put("url", "https://google.com");
644     Map<String, String> headersMap = new HashMap<>();
645     headersMap.put("header1", "value1");
646     headersMap.put("header2", "value2");
647     credentialSourceMapWithUrlTextSource.put("headers", headersMap);
648     credentialSourceMapWithUrlJsonTextSource.put("headers", headersMap);
649 
650     Map<String, String> textFormat = new HashMap<>();
651     textFormat.put("type", "text");
652 
653     Map<String, String> jsonTextFormat = new HashMap<>();
654     jsonTextFormat.put("type", "json");
655     jsonTextFormat.put("subject_token_field_name", "access_token");
656 
657     credentialSourceMapWithFileTextSource.put("format", textFormat);
658     credentialSourceMapWithFileJsonTextSource.put("format", jsonTextFormat);
659 
660     credentialSourceMapWithUrlTextSource.put("format", textFormat);
661     credentialSourceMapWithUrlJsonTextSource.put("format", jsonTextFormat);
662 
663     List<Map<String, Object>> sources =
664         Arrays.asList(
665             credentialSourceMapWithFileTextSource,
666             credentialSourceMapWithFileJsonTextSource,
667             credentialSourceMapWithUrlTextSource,
668             credentialSourceMapWithUrlJsonTextSource);
669     for (Map<String, Object> source : sources) {
670       // Should not throw.
671       new IdentityPoolCredentialSource(source);
672     }
673   }
674 
675   @Test
identityPoolCredentialSource_caseInsensitive()676   public void identityPoolCredentialSource_caseInsensitive() {
677     Map<String, Object> credentialSourceMapWithFileTextSource = new HashMap<>();
678     Map<String, Object> credentialSourceMapWithFileJsonTextSource = new HashMap<>();
679     Map<String, Object> credentialSourceMapWithUrlTextSource = new HashMap<>();
680     Map<String, Object> credentialSourceMapWithUrlJsonTextSource = new HashMap<>();
681 
682     credentialSourceMapWithFileTextSource.put("file", "/path/to/file");
683     credentialSourceMapWithFileJsonTextSource.put("file", "/path/to/file");
684 
685     credentialSourceMapWithUrlTextSource.put("url", "https://google.com");
686     credentialSourceMapWithUrlJsonTextSource.put("url", "https://google.com");
687     Map<String, String> headersMap = new HashMap<>();
688     headersMap.put("HeaDer1", "Value1");
689     headersMap.put("HeaDer2", "Value2");
690     credentialSourceMapWithUrlTextSource.put("headers", headersMap);
691     credentialSourceMapWithUrlJsonTextSource.put("headers", headersMap);
692 
693     Map<String, String> textFormat = new HashMap<>();
694     textFormat.put("type", "TEXT");
695 
696     Map<String, String> jsonTextFormat = new HashMap<>();
697     jsonTextFormat.put("type", "JSON");
698     jsonTextFormat.put("subject_token_field_name", "access_token");
699 
700     credentialSourceMapWithFileTextSource.put("format", textFormat);
701     credentialSourceMapWithFileJsonTextSource.put("format", jsonTextFormat);
702 
703     credentialSourceMapWithUrlTextSource.put("format", textFormat);
704     credentialSourceMapWithUrlJsonTextSource.put("format", jsonTextFormat);
705 
706     List<Map<String, Object>> sources =
707         Arrays.asList(
708             credentialSourceMapWithFileTextSource,
709             credentialSourceMapWithFileJsonTextSource,
710             credentialSourceMapWithUrlTextSource,
711             credentialSourceMapWithUrlJsonTextSource);
712     for (Map<String, Object> source : sources) {
713       // Should not throw.
714       new IdentityPoolCredentialSource(source);
715     }
716   }
717 
718   @Test
identityPoolCredentialSource_invalidSourceType()719   public void identityPoolCredentialSource_invalidSourceType() {
720     try {
721       new IdentityPoolCredentialSource(new HashMap<>());
722       fail("Should not be able to continue without exception.");
723     } catch (IllegalArgumentException exception) {
724       assertEquals(
725           "Missing credential source file location or URL. At least one must be specified.",
726           exception.getMessage());
727     }
728   }
729 
730   @Test
identityPoolCredentialSource_invalidFormatType()731   public void identityPoolCredentialSource_invalidFormatType() {
732     Map<String, Object> credentialSourceMap = new HashMap<>();
733     credentialSourceMap.put("url", "url");
734 
735     Map<String, String> format = new HashMap<>();
736     format.put("type", "unsupportedType");
737     credentialSourceMap.put("format", format);
738 
739     try {
740       new IdentityPoolCredentialSource(credentialSourceMap);
741       fail("Exception should be thrown.");
742     } catch (IllegalArgumentException e) {
743       assertEquals("Invalid credential source format type: unsupportedType.", e.getMessage());
744     }
745   }
746 
747   @Test
identityPoolCredentialSource_nullFormatType()748   public void identityPoolCredentialSource_nullFormatType() {
749     Map<String, Object> credentialSourceMap = new HashMap<>();
750     credentialSourceMap.put("url", "url");
751 
752     Map<String, String> format = new HashMap<>();
753     format.put("type", null);
754     credentialSourceMap.put("format", format);
755 
756     try {
757       new IdentityPoolCredentialSource(credentialSourceMap);
758       fail("Exception should be thrown.");
759     } catch (IllegalArgumentException e) {
760       assertEquals("Invalid credential source format type: null.", e.getMessage());
761     }
762   }
763 
764   @Test
identityPoolCredentialSource_subjectTokenFieldNameUnset()765   public void identityPoolCredentialSource_subjectTokenFieldNameUnset() {
766     Map<String, Object> credentialSourceMap = new HashMap<>();
767     credentialSourceMap.put("url", "url");
768 
769     Map<String, String> format = new HashMap<>();
770     format.put("type", "json");
771     credentialSourceMap.put("format", format);
772 
773     try {
774       new IdentityPoolCredentialSource(credentialSourceMap);
775       fail("Exception should be thrown.");
776     } catch (IllegalArgumentException e) {
777       assertEquals(
778           "When specifying a JSON credential type, the subject_token_field_name must be set.",
779           e.getMessage());
780     }
781   }
782 
783   @Test
builder_allFields()784   public void builder_allFields() throws IOException {
785     List<String> scopes = Arrays.asList("scope1", "scope2");
786 
787     IdentityPoolCredentials credentials =
788         IdentityPoolCredentials.newBuilder()
789             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
790             .setAudience("audience")
791             .setSubjectTokenType("subjectTokenType")
792             .setTokenUrl(STS_URL)
793             .setTokenInfoUrl("tokenInfoUrl")
794             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
795             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
796             .setQuotaProjectId("quotaProjectId")
797             .setClientId("clientId")
798             .setClientSecret("clientSecret")
799             .setScopes(scopes)
800             .setUniverseDomain("universeDomain")
801             .build();
802 
803     assertEquals("audience", credentials.getAudience());
804     assertEquals("subjectTokenType", credentials.getSubjectTokenType());
805     assertEquals(STS_URL, credentials.getTokenUrl());
806     assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl());
807     assertEquals(
808         SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl());
809     assertEquals(FILE_CREDENTIAL_SOURCE, credentials.getCredentialSource());
810     assertEquals("quotaProjectId", credentials.getQuotaProjectId());
811     assertEquals("clientId", credentials.getClientId());
812     assertEquals("clientSecret", credentials.getClientSecret());
813     assertEquals(scopes, credentials.getScopes());
814     assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider());
815     assertEquals("universeDomain", credentials.getUniverseDomain());
816   }
817 
818   @Test
builder_subjectTokenSupplier()819   public void builder_subjectTokenSupplier() {
820     List<String> scopes = Arrays.asList("scope1", "scope2");
821 
822     IdentityPoolCredentials credentials =
823         IdentityPoolCredentials.newBuilder()
824             .setSubjectTokenSupplier(testProvider)
825             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
826             .setAudience("audience")
827             .setSubjectTokenType("subjectTokenType")
828             .setTokenUrl(STS_URL)
829             .setTokenInfoUrl("tokenInfoUrl")
830             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
831             .setQuotaProjectId("quotaProjectId")
832             .setClientId("clientId")
833             .setClientSecret("clientSecret")
834             .setScopes(scopes)
835             .build();
836 
837     assertEquals(testProvider, credentials.getIdentityPoolSubjectTokenSupplier());
838   }
839 
840   @Test
builder_invalidWorkforceAudiences_throws()841   public void builder_invalidWorkforceAudiences_throws() {
842     List<String> invalidAudiences =
843         Arrays.asList(
844             "",
845             "//iam.googleapis.com/projects/x23/locations/global/workloadIdentityPools/pool/providers/provider",
846             "//iam.googleapis.com/locations/global/workforcepools/pool/providers/provider",
847             "//iam.googleapis.com/locations/global/workforcePools/providers/provider",
848             "//iam.googleapis.com/locations/global/workforcePools/providers",
849             "//iam.googleapis.com/locations/global/workforcePools/",
850             "//iam.googleapis.com/locations//workforcePools/providers",
851             "//iam.googleapis.com/notlocations/global/workforcePools/providers",
852             "//iam.googleapis.com/locations/global/workforce/providers");
853 
854     for (String audience : invalidAudiences) {
855       try {
856         IdentityPoolCredentials.newBuilder()
857             .setWorkforcePoolUserProject("workforcePoolUserProject")
858             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
859             .setAudience(audience)
860             .setSubjectTokenType("subjectTokenType")
861             .setTokenUrl(STS_URL)
862             .setTokenInfoUrl("tokenInfoUrl")
863             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
864             .setQuotaProjectId("quotaProjectId")
865             .build();
866         fail("Exception should be thrown.");
867       } catch (IllegalArgumentException e) {
868         assertEquals(
869             "The workforce_pool_user_project parameter should only be provided for a Workforce Pool configuration.",
870             e.getMessage());
871       }
872     }
873   }
874 
875   @Test
builder_emptyWorkforceUserProjectWithWorkforceAudience()876   public void builder_emptyWorkforceUserProjectWithWorkforceAudience() {
877     // No exception should be thrown.
878     IdentityPoolCredentials credentials =
879         IdentityPoolCredentials.newBuilder()
880             .setWorkforcePoolUserProject("")
881             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
882             .setAudience(
883                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
884             .setSubjectTokenType("subjectTokenType")
885             .setTokenUrl(STS_URL)
886             .setTokenInfoUrl("tokenInfoUrl")
887             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
888             .setQuotaProjectId("quotaProjectId")
889             .build();
890 
891     assertTrue(credentials.isWorkforcePoolConfiguration());
892   }
893 
894   @Test
builder_supplierAndCredSourceThrows()895   public void builder_supplierAndCredSourceThrows() throws IOException {
896     try {
897       IdentityPoolCredentials credentials =
898           IdentityPoolCredentials.newBuilder()
899               .setSubjectTokenSupplier(testProvider)
900               .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
901               .setAudience("audience")
902               .setSubjectTokenType("subjectTokenType")
903               .setTokenUrl(STS_URL)
904               .setCredentialSource(FILE_CREDENTIAL_SOURCE)
905               .build();
906       fail("Should not be able to continue without exception.");
907     } catch (IllegalArgumentException exception) {
908       assertEquals(
909           "IdentityPoolCredentials cannot have both a subjectTokenSupplier and a credentialSource.",
910           exception.getMessage());
911     }
912   }
913 
914   @Test
builder_noSupplierOrCredSourceThrows()915   public void builder_noSupplierOrCredSourceThrows() throws IOException {
916 
917     try {
918       IdentityPoolCredentials credentials =
919           IdentityPoolCredentials.newBuilder()
920               .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
921               .setAudience("audience")
922               .setSubjectTokenType("subjectTokenType")
923               .setTokenUrl(STS_URL)
924               .build();
925       fail("Should not be able to continue without exception.");
926     } catch (IllegalArgumentException exception) {
927       assertEquals(
928           "A subjectTokenSupplier or a credentialSource must be provided.", exception.getMessage());
929     }
930   }
931 
builder_missingUniverseDomain_defaults()932   public void builder_missingUniverseDomain_defaults() throws IOException {
933     List<String> scopes = Arrays.asList("scope1", "scope2");
934 
935     IdentityPoolCredentials credentials =
936         IdentityPoolCredentials.newBuilder()
937             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
938             .setAudience("audience")
939             .setSubjectTokenType("subjectTokenType")
940             .setTokenUrl(STS_URL)
941             .setTokenInfoUrl("tokenInfoUrl")
942             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
943             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
944             .setQuotaProjectId("quotaProjectId")
945             .setClientId("clientId")
946             .setClientSecret("clientSecret")
947             .setScopes(scopes)
948             .build();
949 
950     assertEquals("audience", credentials.getAudience());
951     assertEquals("subjectTokenType", credentials.getSubjectTokenType());
952     assertEquals(STS_URL, credentials.getTokenUrl());
953     assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl());
954     assertEquals(
955         SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl());
956     assertEquals(FILE_CREDENTIAL_SOURCE, credentials.getCredentialSource());
957     assertEquals("quotaProjectId", credentials.getQuotaProjectId());
958     assertEquals("clientId", credentials.getClientId());
959     assertEquals("clientSecret", credentials.getClientSecret());
960     assertEquals(scopes, credentials.getScopes());
961     assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider());
962     assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain());
963   }
964 
965   @Test
newBuilder_allFields()966   public void newBuilder_allFields() throws IOException {
967     List<String> scopes = Arrays.asList("scope1", "scope2");
968 
969     IdentityPoolCredentials credentials =
970         IdentityPoolCredentials.newBuilder()
971             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
972             .setAudience(
973                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
974             .setSubjectTokenType("subjectTokenType")
975             .setTokenUrl(STS_URL)
976             .setTokenInfoUrl("tokenInfoUrl")
977             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
978             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
979             .setQuotaProjectId("quotaProjectId")
980             .setClientId("clientId")
981             .setClientSecret("clientSecret")
982             .setScopes(scopes)
983             .setWorkforcePoolUserProject("workforcePoolUserProject")
984             .setUniverseDomain("universeDomain")
985             .build();
986 
987     IdentityPoolCredentials newBuilderCreds =
988         IdentityPoolCredentials.newBuilder(credentials).build();
989     assertEquals(credentials.getAudience(), newBuilderCreds.getAudience());
990     assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType());
991     assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl());
992     assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl());
993     assertEquals(
994         credentials.getServiceAccountImpersonationUrl(),
995         newBuilderCreds.getServiceAccountImpersonationUrl());
996     assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource());
997     assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId());
998     assertEquals(credentials.getClientId(), newBuilderCreds.getClientId());
999     assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret());
1000     assertEquals(credentials.getScopes(), newBuilderCreds.getScopes());
1001     assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider());
1002     assertEquals(
1003         credentials.getWorkforcePoolUserProject(), newBuilderCreds.getWorkforcePoolUserProject());
1004     assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain());
1005   }
1006 
1007   @Test
newBuilder_noUniverseDomain_defaults()1008   public void newBuilder_noUniverseDomain_defaults() throws IOException {
1009     List<String> scopes = Arrays.asList("scope1", "scope2");
1010 
1011     IdentityPoolCredentials credentials =
1012         IdentityPoolCredentials.newBuilder()
1013             .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
1014             .setAudience(
1015                 "//iam.googleapis.com/locations/global/workforcePools/pool/providers/provider")
1016             .setSubjectTokenType("subjectTokenType")
1017             .setTokenUrl(STS_URL)
1018             .setTokenInfoUrl("tokenInfoUrl")
1019             .setCredentialSource(FILE_CREDENTIAL_SOURCE)
1020             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
1021             .setQuotaProjectId("quotaProjectId")
1022             .setClientId("clientId")
1023             .setClientSecret("clientSecret")
1024             .setScopes(scopes)
1025             .setWorkforcePoolUserProject("workforcePoolUserProject")
1026             .build();
1027 
1028     IdentityPoolCredentials newBuilderCreds =
1029         IdentityPoolCredentials.newBuilder(credentials).build();
1030     assertEquals(credentials.getAudience(), newBuilderCreds.getAudience());
1031     assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType());
1032     assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl());
1033     assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl());
1034     assertEquals(
1035         credentials.getServiceAccountImpersonationUrl(),
1036         newBuilderCreds.getServiceAccountImpersonationUrl());
1037     assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource());
1038     assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId());
1039     assertEquals(credentials.getClientId(), newBuilderCreds.getClientId());
1040     assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret());
1041     assertEquals(credentials.getScopes(), newBuilderCreds.getScopes());
1042     assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider());
1043     assertEquals(
1044         credentials.getWorkforcePoolUserProject(), newBuilderCreds.getWorkforcePoolUserProject());
1045     assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain());
1046   }
1047 
1048   @Test
serialize()1049   public void serialize() throws IOException, ClassNotFoundException {
1050     IdentityPoolCredentials testCredentials =
1051         IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
1052             .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
1053             .setQuotaProjectId("quotaProjectId")
1054             .setClientId("clientId")
1055             .setClientSecret("clientSecret")
1056             .setUniverseDomain("universeDomain")
1057             .build();
1058 
1059     IdentityPoolCredentials deserializedCredentials = serializeAndDeserialize(testCredentials);
1060     assertEquals(testCredentials, deserializedCredentials);
1061     assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode());
1062     assertEquals(testCredentials.toString(), deserializedCredentials.toString());
1063     assertSame(deserializedCredentials.clock, Clock.SYSTEM);
1064   }
1065 
writeIdentityPoolCredentialsStream( String tokenUrl, String url, @Nullable String serviceAccountImpersonationUrl, @Nullable Map<String, Object> serviceAccountImpersonationOptionsMap)1066   static InputStream writeIdentityPoolCredentialsStream(
1067       String tokenUrl,
1068       String url,
1069       @Nullable String serviceAccountImpersonationUrl,
1070       @Nullable Map<String, Object> serviceAccountImpersonationOptionsMap)
1071       throws IOException {
1072     GenericJson json = new GenericJson();
1073     json.put("audience", "audience");
1074     json.put("subject_token_type", "subjectTokenType");
1075     json.put("token_url", tokenUrl);
1076     json.put("token_info_url", "tokenInfoUrl");
1077     json.put("type", ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE);
1078 
1079     if (serviceAccountImpersonationUrl != null) {
1080       json.put("service_account_impersonation_url", serviceAccountImpersonationUrl);
1081     }
1082 
1083     if (serviceAccountImpersonationOptionsMap != null) {
1084       json.put("service_account_impersonation", serviceAccountImpersonationOptionsMap);
1085     }
1086 
1087     GenericJson credentialSource = new GenericJson();
1088     GenericJson headers = new GenericJson();
1089     headers.put("Metadata-Flavor", "Google");
1090     credentialSource.put("url", url);
1091     credentialSource.put("headers", headers);
1092 
1093     json.put("credential_source", credentialSource);
1094     return TestUtils.jsonToInputStream(json);
1095   }
1096 
buildUrlBasedCredentialSource(String url)1097   private static IdentityPoolCredentialSource buildUrlBasedCredentialSource(String url) {
1098     return buildUrlBasedCredentialSource(url, /* formatMap= */ null);
1099   }
1100 
buildUrlBasedCredentialSource( String url, Map<String, String> formatMap)1101   private static IdentityPoolCredentialSource buildUrlBasedCredentialSource(
1102       String url, Map<String, String> formatMap) {
1103     Map<String, Object> credentialSourceMap = new HashMap<>();
1104     Map<String, String> headers = new HashMap<>();
1105     headers.put("Metadata-Flavor", "Google");
1106     credentialSourceMap.put("url", url);
1107     credentialSourceMap.put("headers", headers);
1108     credentialSourceMap.put("format", formatMap);
1109 
1110     return new IdentityPoolCredentialSource(credentialSourceMap);
1111   }
1112 }
1113