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 org.junit.Assert.*; 36 import static org.junit.Assert.assertEquals; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNull; 39 import static org.junit.Assert.assertTrue; 40 import static org.junit.Assert.fail; 41 42 import com.google.api.client.json.GenericJson; 43 import com.google.api.client.json.JsonParser; 44 import com.google.api.client.testing.http.MockLowLevelHttpRequest; 45 import com.google.api.client.util.Clock; 46 import com.google.auth.TestUtils; 47 import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory; 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.net.URI; 51 import java.net.URLDecoder; 52 import java.util.Arrays; 53 import java.util.Collections; 54 import java.util.HashMap; 55 import java.util.List; 56 import java.util.Map; 57 import java.util.function.Supplier; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.junit.runners.JUnit4; 61 62 /** Tests for {@link AwsCredentials}. */ 63 @RunWith(JUnit4.class) 64 public class AwsCredentialsTest extends BaseSerializationTest { 65 66 private static final String STS_URL = "https://sts.googleapis.com/v1/token"; 67 private static final String AWS_CREDENTIALS_URL = "https://169.254.169.254"; 68 private static final String AWS_CREDENTIALS_URL_WITH_ROLE = "https://169.254.169.254/roleName"; 69 private static final String AWS_REGION_URL = "https://169.254.169.254/region"; 70 private static final String AWS_IMDSV2_SESSION_TOKEN_URL = "https://169.254.169.254/imdsv2"; 71 private static final String AWS_IMDSV2_SESSION_TOKEN = "sessiontoken"; 72 private static final String DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL = 73 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; 74 75 private static final String GET_CALLER_IDENTITY_URL = 76 "https://sts.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"; 77 78 private static final String SERVICE_ACCOUNT_IMPERSONATION_URL = 79 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/testn@test.iam.gserviceaccount.com:generateAccessToken"; 80 81 private static final Map<String, Object> AWS_CREDENTIAL_SOURCE_MAP = 82 new HashMap<String, Object>() { 83 { 84 put("environment_id", "aws1"); 85 put("region_url", AWS_REGION_URL); 86 put("url", AWS_CREDENTIALS_URL); 87 put("regional_cred_verification_url", "regionalCredVerificationUrl"); 88 } 89 }; 90 91 private static final Map<String, Object> EMPTY_METADATA_HEADERS = Collections.emptyMap(); 92 private static final Map<String, String> EMPTY_STRING_HEADERS = Collections.emptyMap(); 93 94 private static final AwsCredentialSource AWS_CREDENTIAL_SOURCE = 95 new AwsCredentialSource(AWS_CREDENTIAL_SOURCE_MAP); 96 97 private static final AwsCredentials AWS_CREDENTIAL = 98 AwsCredentials.newBuilder() 99 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 100 .setAudience("audience") 101 .setSubjectTokenType("subjectTokenType") 102 .setTokenUrl(STS_URL) 103 .setTokenInfoUrl("tokenInfoUrl") 104 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 105 .build(); 106 107 private static final AwsSecurityCredentials programmaticAwsCreds = 108 new AwsSecurityCredentials("testAccessKey", "testSecretAccessKey", null); 109 110 private static final ExternalAccountSupplierContext emptyContext = 111 ExternalAccountSupplierContext.newBuilder().setAudience("").setSubjectTokenType("").build(); 112 113 @Test test_awsCredentialSource()114 public void test_awsCredentialSource() { 115 String keys[] = {"region_url", "url", "imdsv2_session_token_url"}; 116 for (String key : keys) { 117 Map<String, Object> credentialSourceWithInvalidUrl = buildAwsIpv6CredentialSourceMap(); 118 credentialSourceWithInvalidUrl.put(key, "https://badhost.com/fake"); 119 120 // Should succeed as no validation is done. 121 new AwsCredentialSource(credentialSourceWithInvalidUrl); 122 } 123 } 124 125 @Test refreshAccessToken_withoutServiceAccountImpersonation()126 public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException { 127 MockExternalAccountCredentialsTransportFactory transportFactory = 128 new MockExternalAccountCredentialsTransportFactory(); 129 130 AwsCredentials awsCredential = 131 AwsCredentials.newBuilder(AWS_CREDENTIAL) 132 .setTokenUrl(transportFactory.transport.getStsUrl()) 133 .setHttpTransportFactory(transportFactory) 134 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 135 .build(); 136 137 AccessToken accessToken = awsCredential.refreshAccessToken(); 138 139 assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); 140 141 // Validate metrics header is set correctly on the sts request. 142 Map<String, List<String>> headers = 143 transportFactory.transport.getRequests().get(3).getHeaders(); 144 ExternalAccountCredentialsTest.validateMetricsHeader(headers, "aws", false, false); 145 } 146 147 @Test refreshAccessToken_withServiceAccountImpersonation()148 public void refreshAccessToken_withServiceAccountImpersonation() throws IOException { 149 MockExternalAccountCredentialsTransportFactory transportFactory = 150 new MockExternalAccountCredentialsTransportFactory(); 151 152 transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); 153 154 AwsCredentials awsCredential = 155 AwsCredentials.newBuilder() 156 .setHttpTransportFactory(transportFactory) 157 .setAudience("audience") 158 .setSubjectTokenType("subjectTokenType") 159 .setTokenUrl(transportFactory.transport.getStsUrl()) 160 .setTokenInfoUrl("tokenInfoUrl") 161 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 162 .setServiceAccountImpersonationUrl( 163 transportFactory.transport.getServiceAccountImpersonationUrl()) 164 .build(); 165 166 AccessToken accessToken = awsCredential.refreshAccessToken(); 167 168 assertEquals( 169 transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); 170 171 // Validate metrics header is set correctly on the sts request. 172 Map<String, List<String>> headers = 173 transportFactory.transport.getRequests().get(6).getHeaders(); 174 ExternalAccountCredentialsTest.validateMetricsHeader(headers, "aws", true, false); 175 } 176 177 @Test refreshAccessToken_withServiceAccountImpersonationOptions()178 public void refreshAccessToken_withServiceAccountImpersonationOptions() throws IOException { 179 MockExternalAccountCredentialsTransportFactory transportFactory = 180 new MockExternalAccountCredentialsTransportFactory(); 181 182 transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); 183 184 AwsCredentials awsCredential = 185 AwsCredentials.newBuilder() 186 .setHttpTransportFactory(transportFactory) 187 .setAudience("audience") 188 .setSubjectTokenType("subjectTokenType") 189 .setTokenUrl(transportFactory.transport.getStsUrl()) 190 .setTokenInfoUrl("tokenInfoUrl") 191 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 192 .setServiceAccountImpersonationUrl( 193 transportFactory.transport.getServiceAccountImpersonationUrl()) 194 .setServiceAccountImpersonationOptions( 195 ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) 196 .build(); 197 198 AccessToken accessToken = awsCredential.refreshAccessToken(); 199 200 assertEquals( 201 transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); 202 203 // Validate that default lifetime was set correctly on the request. 204 GenericJson query = 205 OAuth2Utils.JSON_FACTORY 206 .createJsonParser(transportFactory.transport.getLastRequest().getContentAsString()) 207 .parseAndClose(GenericJson.class); 208 209 assertEquals("2800s", query.get("lifetime")); 210 211 // Validate metrics header is set correctly on the sts request. 212 Map<String, List<String>> headers = 213 transportFactory.transport.getRequests().get(6).getHeaders(); 214 ExternalAccountCredentialsTest.validateMetricsHeader(headers, "aws", true, true); 215 } 216 217 @Test refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation()218 public void refreshAccessTokenProgrammaticRefresh_withoutServiceAccountImpersonation() 219 throws IOException { 220 MockExternalAccountCredentialsTransportFactory transportFactory = 221 new MockExternalAccountCredentialsTransportFactory(); 222 223 AwsSecurityCredentialsSupplier supplier = 224 new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null, null); 225 226 AwsCredentials awsCredential = 227 AwsCredentials.newBuilder() 228 .setAwsSecurityCredentialsSupplier(supplier) 229 .setHttpTransportFactory(transportFactory) 230 .setAudience("audience") 231 .setTokenUrl(STS_URL) 232 .setSubjectTokenType("subjectTokenType") 233 .build(); 234 235 AccessToken accessToken = awsCredential.refreshAccessToken(); 236 237 assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); 238 239 // Validate metrics header is set correctly on the sts request. 240 Map<String, List<String>> headers = 241 transportFactory.transport.getRequests().get(0).getHeaders(); 242 ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", false, false); 243 } 244 245 @Test refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation()246 public void refreshAccessTokenProgrammaticRefresh_withServiceAccountImpersonation() 247 throws IOException { 248 MockExternalAccountCredentialsTransportFactory transportFactory = 249 new MockExternalAccountCredentialsTransportFactory(); 250 251 transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); 252 253 AwsSecurityCredentialsSupplier supplier = 254 new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null, null); 255 256 AwsCredentials awsCredential = 257 AwsCredentials.newBuilder() 258 .setAwsSecurityCredentialsSupplier(supplier) 259 .setHttpTransportFactory(transportFactory) 260 .setAudience("audience") 261 .setTokenUrl(STS_URL) 262 .setSubjectTokenType("subjectTokenType") 263 .setServiceAccountImpersonationUrl( 264 transportFactory.transport.getServiceAccountImpersonationUrl()) 265 .build(); 266 267 AccessToken accessToken = awsCredential.refreshAccessToken(); 268 269 assertEquals( 270 transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); 271 272 // Validate metrics header is set correctly on the sts request. 273 Map<String, List<String>> headers = 274 transportFactory.transport.getRequests().get(0).getHeaders(); 275 ExternalAccountCredentialsTest.validateMetricsHeader(headers, "programmatic", true, false); 276 } 277 278 @Test 279 @SuppressWarnings("unchecked") retrieveSubjectToken()280 public void retrieveSubjectToken() throws IOException { 281 MockExternalAccountCredentialsTransportFactory transportFactory = 282 new MockExternalAccountCredentialsTransportFactory(); 283 284 AwsCredentials awsCredential = 285 AwsCredentials.newBuilder(AWS_CREDENTIAL) 286 .setHttpTransportFactory(transportFactory) 287 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 288 .build(); 289 290 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 291 292 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 293 GenericJson json = parser.parseAndClose(GenericJson.class); 294 295 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 296 Map<String, String> headers = new HashMap<>(); 297 for (Map<String, String> header : headersList) { 298 headers.put(header.get("key"), header.get("value")); 299 } 300 301 assertEquals("POST", json.get("method")); 302 assertEquals(GET_CALLER_IDENTITY_URL, json.get("url")); 303 assertEquals(URI.create(GET_CALLER_IDENTITY_URL).getHost(), headers.get("host")); 304 assertEquals("token", headers.get("x-amz-security-token")); 305 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 306 assertTrue(headers.containsKey("x-amz-date")); 307 assertNotNull(headers.get("Authorization")); 308 309 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 310 assertEquals(3, requests.size()); 311 312 // Validate region request. 313 ValidateRequest(requests.get(0), AWS_REGION_URL, EMPTY_STRING_HEADERS); 314 315 // Validate role request. 316 ValidateRequest(requests.get(1), AWS_CREDENTIALS_URL, EMPTY_STRING_HEADERS); 317 318 // Validate security credentials request. 319 ValidateRequest(requests.get(2), AWS_CREDENTIALS_URL_WITH_ROLE, EMPTY_STRING_HEADERS); 320 } 321 322 @Test 323 @SuppressWarnings("unchecked") retrieveSubjectTokenWithSessionTokenUrl()324 public void retrieveSubjectTokenWithSessionTokenUrl() throws IOException { 325 MockExternalAccountCredentialsTransportFactory transportFactory = 326 new MockExternalAccountCredentialsTransportFactory(); 327 328 AwsCredentials awsCredential = 329 AwsCredentials.newBuilder(AWS_CREDENTIAL) 330 .setHttpTransportFactory(transportFactory) 331 .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) 332 .build(); 333 334 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 335 336 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 337 GenericJson json = parser.parseAndClose(GenericJson.class); 338 339 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 340 Map<String, String> headers = new HashMap<>(); 341 for (Map<String, String> header : headersList) { 342 headers.put(header.get("key"), header.get("value")); 343 } 344 345 assertEquals("POST", json.get("method")); 346 assertEquals(GET_CALLER_IDENTITY_URL, json.get("url")); 347 assertEquals(URI.create(GET_CALLER_IDENTITY_URL).getHost(), headers.get("host")); 348 assertEquals("token", headers.get("x-amz-security-token")); 349 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 350 assertTrue(headers.containsKey("x-amz-date")); 351 assertNotNull(headers.get("Authorization")); 352 353 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 354 assertEquals(5, requests.size()); 355 356 // Validate the session token request 357 ValidateRequest( 358 requests.get(0), 359 AWS_IMDSV2_SESSION_TOKEN_URL, 360 new HashMap<String, String>() { 361 { 362 put( 363 InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER, 364 InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_TTL); 365 } 366 }); 367 368 Map<String, String> sessionTokenHeader = 369 new HashMap<String, String>() { 370 { 371 put( 372 InternalAwsSecurityCredentialsSupplier.AWS_IMDSV2_SESSION_TOKEN_HEADER, 373 AWS_IMDSV2_SESSION_TOKEN); 374 } 375 }; 376 377 // Validate region request. 378 ValidateRequest(requests.get(1), AWS_REGION_URL, sessionTokenHeader); 379 380 // Validate role request. 381 ValidateRequest(requests.get(3), AWS_CREDENTIALS_URL, sessionTokenHeader); 382 383 // Validate security credentials request. 384 ValidateRequest(requests.get(4), AWS_CREDENTIALS_URL_WITH_ROLE, sessionTokenHeader); 385 } 386 387 @Test retrieveSubjectToken_imdsv1EnvVariablesSet_metadataServerNotCalled()388 public void retrieveSubjectToken_imdsv1EnvVariablesSet_metadataServerNotCalled() 389 throws IOException { 390 MockExternalAccountCredentialsTransportFactory transportFactory = 391 new MockExternalAccountCredentialsTransportFactory(); 392 393 // Provide AWS credentials through environment vars. 394 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 395 environmentProvider 396 .setEnv("AWS_REGION", "awsRegion") 397 .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") 398 .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey") 399 .setEnv("AWS_SESSION_TOKEN", "awsToken"); 400 401 AwsCredentials awsCredential = 402 AwsCredentials.newBuilder(AWS_CREDENTIAL) 403 .setHttpTransportFactory(transportFactory) 404 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 405 .setEnvironmentProvider(environmentProvider) 406 .build(); 407 408 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 409 410 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 411 GenericJson json = parser.parseAndClose(GenericJson.class); 412 413 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 414 Map<String, String> headers = new HashMap<>(); 415 for (Map<String, String> header : headersList) { 416 headers.put(header.get("key"), header.get("value")); 417 } 418 419 assertEquals("POST", json.get("method")); 420 assertEquals(GET_CALLER_IDENTITY_URL, json.get("url")); 421 assertEquals(URI.create(GET_CALLER_IDENTITY_URL).getHost(), headers.get("host")); 422 assertEquals("awsToken", headers.get("x-amz-security-token")); 423 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 424 assertTrue(headers.containsKey("x-amz-date")); 425 assertNotNull(headers.get("Authorization")); 426 427 // No requests should have been made since AWS credentials and region is passed through 428 // environment variables. 429 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 430 assertEquals(0, requests.size()); 431 } 432 433 @Test retrieveSubjectToken_imdsv2EnvVariablesSet_metadataServerNotCalled()434 public void retrieveSubjectToken_imdsv2EnvVariablesSet_metadataServerNotCalled() 435 throws IOException { 436 MockExternalAccountCredentialsTransportFactory transportFactory = 437 new MockExternalAccountCredentialsTransportFactory(); 438 439 // Provide AWS credentials through environment vars. 440 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 441 environmentProvider 442 .setEnv("AWS_REGION", "awsRegion") 443 .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") 444 .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey") 445 .setEnv("AWS_SESSION_TOKEN", "awsToken"); 446 447 AwsCredentials awsCredential = 448 AwsCredentials.newBuilder(AWS_CREDENTIAL) 449 .setHttpTransportFactory(transportFactory) 450 .setCredentialSource(buildAwsImdsv2CredentialSource(transportFactory)) 451 .setEnvironmentProvider(environmentProvider) 452 .build(); 453 454 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 455 456 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 457 GenericJson json = parser.parseAndClose(GenericJson.class); 458 459 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 460 Map<String, String> headers = new HashMap<>(); 461 for (Map<String, String> header : headersList) { 462 headers.put(header.get("key"), header.get("value")); 463 } 464 465 assertEquals("POST", json.get("method")); 466 assertEquals(GET_CALLER_IDENTITY_URL, json.get("url")); 467 assertEquals(URI.create(GET_CALLER_IDENTITY_URL).getHost(), headers.get("host")); 468 assertEquals("awsToken", headers.get("x-amz-security-token")); 469 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 470 assertTrue(headers.containsKey("x-amz-date")); 471 assertNotNull(headers.get("Authorization")); 472 473 // No requests should have been made since AWS credentials and region is passed through 474 // environment variables. 475 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 476 assertEquals(0, requests.size()); 477 } 478 479 @Test retrieveSubjectToken_noRegion_expectThrows()480 public void retrieveSubjectToken_noRegion_expectThrows() { 481 MockExternalAccountCredentialsTransportFactory transportFactory = 482 new MockExternalAccountCredentialsTransportFactory(); 483 484 IOException response = new IOException(); 485 transportFactory.transport.addResponseErrorSequence(response); 486 487 AwsCredentials awsCredential = 488 AwsCredentials.newBuilder(AWS_CREDENTIAL) 489 .setHttpTransportFactory(transportFactory) 490 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 491 .build(); 492 493 try { 494 awsCredential.retrieveSubjectToken(); 495 fail("Should not be able to use credential without exception."); 496 } catch (IOException exception) { 497 assertEquals("Failed to retrieve AWS region.", exception.getMessage()); 498 } 499 500 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 501 assertEquals(1, requests.size()); 502 503 // Validate region request. 504 ValidateRequest(requests.get(0), AWS_REGION_URL, EMPTY_STRING_HEADERS); 505 } 506 507 @Test retrieveSubjectToken_noRole_expectThrows()508 public void retrieveSubjectToken_noRole_expectThrows() { 509 MockExternalAccountCredentialsTransportFactory transportFactory = 510 new MockExternalAccountCredentialsTransportFactory(); 511 512 IOException response = new IOException(); 513 transportFactory.transport.addResponseErrorSequence(response); 514 transportFactory.transport.addResponseSequence(true, false); 515 516 AwsCredentials awsCredential = 517 AwsCredentials.newBuilder(AWS_CREDENTIAL) 518 .setHttpTransportFactory(transportFactory) 519 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 520 .build(); 521 522 try { 523 awsCredential.retrieveSubjectToken(); 524 fail("Should not be able to use credential without exception."); 525 } catch (IOException exception) { 526 assertEquals("Failed to retrieve AWS IAM role.", exception.getMessage()); 527 } 528 529 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 530 assertEquals(2, requests.size()); 531 532 // Validate region request. 533 ValidateRequest(requests.get(0), AWS_REGION_URL, EMPTY_STRING_HEADERS); 534 535 // Validate role request. 536 ValidateRequest(requests.get(1), AWS_CREDENTIALS_URL, EMPTY_STRING_HEADERS); 537 } 538 539 @Test retrieveSubjectToken_noCredentials_expectThrows()540 public void retrieveSubjectToken_noCredentials_expectThrows() { 541 MockExternalAccountCredentialsTransportFactory transportFactory = 542 new MockExternalAccountCredentialsTransportFactory(); 543 544 IOException response = new IOException(); 545 transportFactory.transport.addResponseErrorSequence(response); 546 transportFactory.transport.addResponseSequence(true, true, false); 547 548 AwsCredentials awsCredential = 549 AwsCredentials.newBuilder(AWS_CREDENTIAL) 550 .setHttpTransportFactory(transportFactory) 551 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 552 .build(); 553 554 try { 555 awsCredential.retrieveSubjectToken(); 556 fail("Should not be able to use credential without exception."); 557 } catch (IOException exception) { 558 assertEquals("Failed to retrieve AWS credentials.", exception.getMessage()); 559 } 560 561 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 562 assertEquals(3, requests.size()); 563 564 // Validate region request. 565 ValidateRequest(requests.get(0), AWS_REGION_URL, EMPTY_STRING_HEADERS); 566 567 // Validate role request. 568 ValidateRequest(requests.get(1), AWS_CREDENTIALS_URL, EMPTY_STRING_HEADERS); 569 570 // Validate security credentials request. 571 ValidateRequest(requests.get(2), AWS_CREDENTIALS_URL_WITH_ROLE, EMPTY_STRING_HEADERS); 572 } 573 574 @Test retrieveSubjectToken_noRegionUrlProvided()575 public void retrieveSubjectToken_noRegionUrlProvided() { 576 MockExternalAccountCredentialsTransportFactory transportFactory = 577 new MockExternalAccountCredentialsTransportFactory(); 578 579 Map<String, Object> credentialSource = new HashMap<>(); 580 credentialSource.put("environment_id", "aws1"); 581 credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 582 583 AwsCredentials awsCredential = 584 AwsCredentials.newBuilder(AWS_CREDENTIAL) 585 .setHttpTransportFactory(transportFactory) 586 .setCredentialSource(new AwsCredentialSource(credentialSource)) 587 .build(); 588 589 try { 590 awsCredential.retrieveSubjectToken(); 591 fail("Should not be able to use credential without exception."); 592 } catch (IOException exception) { 593 assertEquals( 594 "Unable to determine the AWS region. The credential source does not " 595 + "contain the region URL.", 596 exception.getMessage()); 597 } 598 599 // No requests because the credential source does not contain region URL. 600 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 601 assertTrue(requests.isEmpty()); 602 } 603 604 @Test retrieveSubjectToken_withProgrammaticRefresh()605 public void retrieveSubjectToken_withProgrammaticRefresh() throws IOException { 606 MockExternalAccountCredentialsTransportFactory transportFactory = 607 new MockExternalAccountCredentialsTransportFactory(); 608 609 AwsSecurityCredentialsSupplier supplier = 610 new TestAwsSecurityCredentialsSupplier("test", programmaticAwsCreds, null, null); 611 612 AwsCredentials awsCredential = 613 AwsCredentials.newBuilder() 614 .setAwsSecurityCredentialsSupplier(supplier) 615 .setHttpTransportFactory(transportFactory) 616 .setAudience("audience") 617 .setTokenUrl(STS_URL) 618 .setSubjectTokenType("subjectTokenType") 619 .build(); 620 621 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 622 623 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 624 GenericJson json = parser.parseAndClose(GenericJson.class); 625 626 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 627 Map<String, String> headers = new HashMap<>(); 628 for (Map<String, String> header : headersList) { 629 headers.put(header.get("key"), header.get("value")); 630 } 631 632 String expectedCredentialVerificationUrl = 633 DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace("{region}", "test"); 634 635 assertEquals("POST", json.get("method")); 636 assertEquals(expectedCredentialVerificationUrl, json.get("url")); 637 assertEquals(URI.create(expectedCredentialVerificationUrl).getHost(), headers.get("host")); 638 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 639 assertTrue(headers.containsKey("x-amz-date")); 640 assertNotNull(headers.get("Authorization")); 641 } 642 643 @Test retrieveSubjectToken_withProgrammaticRefreshSessionToken()644 public void retrieveSubjectToken_withProgrammaticRefreshSessionToken() throws IOException { 645 MockExternalAccountCredentialsTransportFactory transportFactory = 646 new MockExternalAccountCredentialsTransportFactory(); 647 648 AwsSecurityCredentials securityCredentialsWithToken = 649 new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); 650 651 AwsSecurityCredentialsSupplier supplier = 652 new TestAwsSecurityCredentialsSupplier("test", securityCredentialsWithToken, null, null); 653 654 AwsCredentials awsCredential = 655 AwsCredentials.newBuilder() 656 .setAwsSecurityCredentialsSupplier(supplier) 657 .setHttpTransportFactory(transportFactory) 658 .setAudience("audience") 659 .setTokenUrl(STS_URL) 660 .setSubjectTokenType("subjectTokenType") 661 .build(); 662 663 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 664 665 JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(subjectToken); 666 GenericJson json = parser.parseAndClose(GenericJson.class); 667 668 List<Map<String, String>> headersList = (List<Map<String, String>>) json.get("headers"); 669 Map<String, String> headers = new HashMap<>(); 670 for (Map<String, String> header : headersList) { 671 headers.put(header.get("key"), header.get("value")); 672 } 673 674 String expectedCredentialVerificationUrl = 675 DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL.replace("{region}", "test"); 676 677 assertEquals("POST", json.get("method")); 678 assertEquals(expectedCredentialVerificationUrl, json.get("url")); 679 assertEquals(URI.create(expectedCredentialVerificationUrl).getHost(), headers.get("host")); 680 assertEquals("token", headers.get("x-amz-security-token")); 681 assertEquals(awsCredential.getAudience(), headers.get("x-goog-cloud-target-resource")); 682 assertTrue(headers.containsKey("x-amz-date")); 683 assertNotNull(headers.get("Authorization")); 684 } 685 686 @Test retrieveSubjectToken_passesContext()687 public void retrieveSubjectToken_passesContext() throws IOException { 688 MockExternalAccountCredentialsTransportFactory transportFactory = 689 new MockExternalAccountCredentialsTransportFactory(); 690 691 AwsSecurityCredentials securityCredentialsWithToken = 692 new AwsSecurityCredentials("accessToken", "secretAccessKey", "token"); 693 694 ExternalAccountSupplierContext expectedContext = 695 ExternalAccountSupplierContext.newBuilder() 696 .setAudience("audience") 697 .setSubjectTokenType("subjectTokenType") 698 .build(); 699 700 AwsSecurityCredentialsSupplier supplier = 701 new TestAwsSecurityCredentialsSupplier( 702 "test", securityCredentialsWithToken, null, expectedContext); 703 704 AwsCredentials awsCredential = 705 AwsCredentials.newBuilder() 706 .setAwsSecurityCredentialsSupplier(supplier) 707 .setHttpTransportFactory(transportFactory) 708 .setAudience("audience") 709 .setTokenUrl(STS_URL) 710 .setSubjectTokenType("subjectTokenType") 711 .build(); 712 713 awsCredential.retrieveSubjectToken(); 714 } 715 716 @Test retrieveSubjectToken_withProgrammaticRefreshThrowsError()717 public void retrieveSubjectToken_withProgrammaticRefreshThrowsError() throws IOException { 718 MockExternalAccountCredentialsTransportFactory transportFactory = 719 new MockExternalAccountCredentialsTransportFactory(); 720 721 IOException testException = new IOException("test"); 722 723 AwsSecurityCredentialsSupplier supplier = 724 new TestAwsSecurityCredentialsSupplier("test", null, testException, null); 725 726 AwsCredentials awsCredential = 727 AwsCredentials.newBuilder() 728 .setAwsSecurityCredentialsSupplier(supplier) 729 .setHttpTransportFactory(transportFactory) 730 .setAudience("audience") 731 .setTokenUrl(STS_URL) 732 .setSubjectTokenType("subjectTokenType") 733 .build(); 734 735 try { 736 String subjectToken = URLDecoder.decode(awsCredential.retrieveSubjectToken(), "UTF-8"); 737 fail("retrieveSubjectToken should not succeed"); 738 } catch (IOException e) { 739 assertEquals("test", e.getMessage()); 740 } 741 } 742 743 @Test getAwsSecurityCredentials_fromEnvironmentVariablesNoToken()744 public void getAwsSecurityCredentials_fromEnvironmentVariablesNoToken() throws IOException { 745 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 746 environmentProvider 747 .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") 748 .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey"); 749 750 AwsCredentials testAwsCredentials = 751 AwsCredentials.newBuilder(AWS_CREDENTIAL) 752 .setEnvironmentProvider(environmentProvider) 753 .build(); 754 755 AwsSecurityCredentials credentials = 756 testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); 757 758 assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); 759 assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); 760 assertNull(credentials.getSessionToken()); 761 } 762 763 @Test getAwsSecurityCredentials_fromEnvironmentVariablesWithToken()764 public void getAwsSecurityCredentials_fromEnvironmentVariablesWithToken() throws IOException { 765 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 766 environmentProvider 767 .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") 768 .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey") 769 .setEnv("AWS_SESSION_TOKEN", "awsSessionToken"); 770 771 AwsCredentialSource credSource = 772 new AwsCredentialSource( 773 new HashMap<String, Object>() { 774 { 775 put("environment_id", "aws1"); 776 put("region_url", ""); 777 put("url", ""); 778 put("regional_cred_verification_url", "regionalCredVerificationUrl"); 779 } 780 }); 781 782 AwsCredentials testAwsCredentials = 783 AwsCredentials.newBuilder(AWS_CREDENTIAL) 784 .setEnvironmentProvider(environmentProvider) 785 .setCredentialSource(credSource) 786 .build(); 787 788 AwsSecurityCredentials credentials = 789 testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); 790 791 assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); 792 assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); 793 assertEquals("awsSessionToken", credentials.getSessionToken()); 794 } 795 796 @Test getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerCall()797 public void getAwsSecurityCredentials_fromEnvironmentVariables_noMetadataServerCall() 798 throws IOException { 799 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 800 environmentProvider 801 .setEnv("AWS_ACCESS_KEY_ID", "awsAccessKeyId") 802 .setEnv("AWS_SECRET_ACCESS_KEY", "awsSecretAccessKey") 803 .setEnv("AWS_SESSION_TOKEN", "awsSessionToken"); 804 805 AwsCredentials testAwsCredentials = 806 AwsCredentials.newBuilder(AWS_CREDENTIAL) 807 .setEnvironmentProvider(environmentProvider) 808 .build(); 809 810 AwsSecurityCredentials credentials = 811 testAwsCredentials.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); 812 813 assertEquals("awsAccessKeyId", credentials.getAccessKeyId()); 814 assertEquals("awsSecretAccessKey", credentials.getSecretAccessKey()); 815 assertEquals("awsSessionToken", credentials.getSessionToken()); 816 } 817 818 @Test getAwsSecurityCredentials_fromMetadataServer()819 public void getAwsSecurityCredentials_fromMetadataServer() throws IOException { 820 MockExternalAccountCredentialsTransportFactory transportFactory = 821 new MockExternalAccountCredentialsTransportFactory(); 822 823 AwsCredentials awsCredential = 824 AwsCredentials.newBuilder(AWS_CREDENTIAL) 825 .setHttpTransportFactory(transportFactory) 826 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 827 .build(); 828 829 AwsSecurityCredentials credentials = 830 awsCredential.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); 831 832 assertEquals("accessKeyId", credentials.getAccessKeyId()); 833 assertEquals("secretAccessKey", credentials.getSecretAccessKey()); 834 assertEquals("token", credentials.getSessionToken()); 835 836 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 837 assertEquals(2, requests.size()); 838 839 // Validate role request. 840 ValidateRequest(requests.get(0), AWS_CREDENTIALS_URL, EMPTY_STRING_HEADERS); 841 842 // Validate security credentials request. 843 ValidateRequest(requests.get(1), AWS_CREDENTIALS_URL_WITH_ROLE, EMPTY_STRING_HEADERS); 844 } 845 846 @Test getAwsSecurityCredentials_fromMetadataServer_noUrlProvided()847 public void getAwsSecurityCredentials_fromMetadataServer_noUrlProvided() { 848 MockExternalAccountCredentialsTransportFactory transportFactory = 849 new MockExternalAccountCredentialsTransportFactory(); 850 851 Map<String, Object> credentialSource = new HashMap<>(); 852 credentialSource.put("environment_id", "aws1"); 853 credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 854 855 AwsCredentials awsCredential = 856 AwsCredentials.newBuilder(AWS_CREDENTIAL) 857 .setHttpTransportFactory(transportFactory) 858 .setCredentialSource(new AwsCredentialSource(credentialSource)) 859 .build(); 860 861 try { 862 awsCredential.getAwsSecurityCredentialsSupplier().getCredentials(emptyContext); 863 fail("Should not be able to use credential without exception."); 864 } catch (IOException exception) { 865 assertEquals( 866 "Unable to determine the AWS IAM role name. The credential source does not contain the url field.", 867 exception.getMessage()); 868 } 869 870 // No requests because url field is not present in credential source. 871 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 872 assertTrue(requests.isEmpty()); 873 } 874 875 @Test getAwsRegion_awsRegionEnvironmentVariable()876 public void getAwsRegion_awsRegionEnvironmentVariable() throws IOException { 877 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 878 environmentProvider.setEnv("AWS_REGION", "region"); 879 environmentProvider.setEnv("AWS_DEFAULT_REGION", "defaultRegion"); 880 881 MockExternalAccountCredentialsTransportFactory transportFactory = 882 new MockExternalAccountCredentialsTransportFactory(); 883 AwsCredentials awsCredentials = 884 AwsCredentials.newBuilder(AWS_CREDENTIAL) 885 .setHttpTransportFactory(transportFactory) 886 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 887 .setEnvironmentProvider(environmentProvider) 888 .build(); 889 890 String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); 891 892 // Should attempt to retrieve the region from AWS_REGION env var first. 893 // Metadata server would return us-east-1b. 894 assertEquals("region", region); 895 896 // No requests because region is obtained from environment variables. 897 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 898 assertTrue(requests.isEmpty()); 899 } 900 901 @Test getAwsRegion_awsDefaultRegionEnvironmentVariable()902 public void getAwsRegion_awsDefaultRegionEnvironmentVariable() throws IOException { 903 TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider(); 904 environmentProvider.setEnv("AWS_DEFAULT_REGION", "defaultRegion"); 905 906 MockExternalAccountCredentialsTransportFactory transportFactory = 907 new MockExternalAccountCredentialsTransportFactory(); 908 AwsCredentials awsCredentials = 909 AwsCredentials.newBuilder(AWS_CREDENTIAL) 910 .setHttpTransportFactory(transportFactory) 911 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 912 .setEnvironmentProvider(environmentProvider) 913 .build(); 914 915 String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); 916 917 // Should attempt to retrieve the region from DEFAULT_AWS_REGION before calling the metadata 918 // server. Metadata server would return us-east-1b. 919 assertEquals("defaultRegion", region); 920 921 // No requests because region is obtained from environment variables. 922 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 923 assertTrue(requests.isEmpty()); 924 } 925 926 @Test getAwsRegion_metadataServer()927 public void getAwsRegion_metadataServer() throws IOException { 928 MockExternalAccountCredentialsTransportFactory transportFactory = 929 new MockExternalAccountCredentialsTransportFactory(); 930 AwsCredentials awsCredentials = 931 AwsCredentials.newBuilder(AWS_CREDENTIAL) 932 .setHttpTransportFactory(transportFactory) 933 .setCredentialSource(buildAwsCredentialSource(transportFactory)) 934 .build(); 935 936 String region = awsCredentials.getAwsSecurityCredentialsSupplier().getRegion(emptyContext); 937 938 // Should retrieve the region from the Metadata server. 939 String expectedRegion = 940 transportFactory 941 .transport 942 .getAwsRegion() 943 .substring(0, transportFactory.transport.getAwsRegion().length() - 1); 944 assertEquals(expectedRegion, region); 945 946 List<MockLowLevelHttpRequest> requests = transportFactory.transport.getRequests(); 947 assertEquals(1, requests.size()); 948 949 // Validate region request. 950 ValidateRequest(requests.get(0), AWS_REGION_URL, EMPTY_STRING_HEADERS); 951 } 952 953 @Test createdScoped_clonedCredentialWithAddedScopes()954 public void createdScoped_clonedCredentialWithAddedScopes() throws IOException { 955 AwsCredentials credentials = 956 AwsCredentials.newBuilder(AWS_CREDENTIAL) 957 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 958 .setQuotaProjectId("quotaProjectId") 959 .setClientId("clientId") 960 .setClientSecret("clientSecret") 961 .setUniverseDomain("universeDomain") 962 .build(); 963 964 List<String> newScopes = Arrays.asList("scope1", "scope2"); 965 966 AwsCredentials newCredentials = (AwsCredentials) credentials.createScoped(newScopes); 967 968 assertEquals(credentials.getAudience(), newCredentials.getAudience()); 969 assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType()); 970 assertEquals(credentials.getTokenUrl(), newCredentials.getTokenUrl()); 971 assertEquals(credentials.getTokenInfoUrl(), newCredentials.getTokenInfoUrl()); 972 assertEquals( 973 credentials.getServiceAccountImpersonationUrl(), 974 newCredentials.getServiceAccountImpersonationUrl()); 975 assertEquals(credentials.getCredentialSource(), newCredentials.getCredentialSource()); 976 assertEquals(credentials.getQuotaProjectId(), newCredentials.getQuotaProjectId()); 977 assertEquals(credentials.getClientId(), newCredentials.getClientId()); 978 assertEquals(credentials.getClientSecret(), newCredentials.getClientSecret()); 979 assertEquals(newScopes, newCredentials.getScopes()); 980 assertEquals(credentials.getUniverseDomain(), newCredentials.getUniverseDomain()); 981 assertEquals("universeDomain", newCredentials.getUniverseDomain()); 982 } 983 984 @Test credentialSource_invalidAwsEnvironmentId()985 public void credentialSource_invalidAwsEnvironmentId() { 986 Map<String, Object> credentialSource = new HashMap<>(); 987 credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 988 credentialSource.put("environment_id", "azure1"); 989 990 try { 991 new AwsCredentialSource(credentialSource); 992 fail("Exception should be thrown."); 993 } catch (IllegalArgumentException e) { 994 assertEquals("Invalid AWS environment ID.", e.getMessage()); 995 } 996 } 997 998 @Test credentialSource_invalidAwsEnvironmentVersion()999 public void credentialSource_invalidAwsEnvironmentVersion() { 1000 Map<String, Object> credentialSource = new HashMap<>(); 1001 int environmentVersion = 2; 1002 credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 1003 credentialSource.put("environment_id", "aws" + environmentVersion); 1004 1005 try { 1006 new AwsCredentialSource(credentialSource); 1007 fail("Exception should be thrown."); 1008 } catch (IllegalArgumentException e) { 1009 assertEquals( 1010 String.format( 1011 "AWS version %s is not supported in the current build.", environmentVersion), 1012 e.getMessage()); 1013 } 1014 } 1015 1016 @Test credentialSource_missingRegionalCredVerificationUrl()1017 public void credentialSource_missingRegionalCredVerificationUrl() { 1018 try { 1019 new AwsCredentialSource(new HashMap<String, Object>()); 1020 fail("Exception should be thrown."); 1021 } catch (IllegalArgumentException e) { 1022 assertEquals( 1023 "A regional_cred_verification_url representing the GetCallerIdentity action URL must be specified.", 1024 e.getMessage()); 1025 } 1026 } 1027 1028 @Test builder_allFields()1029 public void builder_allFields() throws IOException { 1030 List<String> scopes = Arrays.asList("scope1", "scope2"); 1031 1032 AwsCredentials credentials = 1033 AwsCredentials.newBuilder() 1034 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1035 .setAudience("audience") 1036 .setSubjectTokenType("subjectTokenType") 1037 .setTokenUrl(STS_URL) 1038 .setTokenInfoUrl("tokenInfoUrl") 1039 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1040 .setTokenInfoUrl("tokenInfoUrl") 1041 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1042 .setQuotaProjectId("quotaProjectId") 1043 .setClientId("clientId") 1044 .setClientSecret("clientSecret") 1045 .setScopes(scopes) 1046 .setUniverseDomain("universeDomain") 1047 .build(); 1048 1049 assertEquals("audience", credentials.getAudience()); 1050 assertEquals("subjectTokenType", credentials.getSubjectTokenType()); 1051 assertEquals(STS_URL, credentials.getTokenUrl()); 1052 assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); 1053 assertEquals( 1054 SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); 1055 assertEquals(AWS_CREDENTIAL_SOURCE, credentials.getCredentialSource()); 1056 assertEquals("quotaProjectId", credentials.getQuotaProjectId()); 1057 assertEquals("clientId", credentials.getClientId()); 1058 assertEquals("clientSecret", credentials.getClientSecret()); 1059 assertEquals(scopes, credentials.getScopes()); 1060 assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); 1061 assertEquals("universeDomain", credentials.getUniverseDomain()); 1062 } 1063 1064 @Test builder_missingUniverseDomain_defaults()1065 public void builder_missingUniverseDomain_defaults() throws IOException { 1066 List<String> scopes = Arrays.asList("scope1", "scope2"); 1067 1068 AwsCredentials credentials = 1069 AwsCredentials.newBuilder() 1070 .setRegionalCredentialVerificationUrlOverride("https://test.com") 1071 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1072 .setAudience("audience") 1073 .setSubjectTokenType("subjectTokenType") 1074 .setTokenUrl(STS_URL) 1075 .setTokenInfoUrl("tokenInfoUrl") 1076 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1077 .setTokenInfoUrl("tokenInfoUrl") 1078 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1079 .setQuotaProjectId("quotaProjectId") 1080 .setClientId("clientId") 1081 .setClientSecret("clientSecret") 1082 .setScopes(scopes) 1083 .build(); 1084 1085 assertEquals("https://test.com", credentials.getRegionalCredentialVerificationUrlOverride()); 1086 assertEquals("audience", credentials.getAudience()); 1087 assertEquals("subjectTokenType", credentials.getSubjectTokenType()); 1088 assertEquals(STS_URL, credentials.getTokenUrl()); 1089 assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); 1090 assertEquals( 1091 SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); 1092 assertEquals(AWS_CREDENTIAL_SOURCE, credentials.getCredentialSource()); 1093 assertEquals("quotaProjectId", credentials.getQuotaProjectId()); 1094 assertEquals("clientId", credentials.getClientId()); 1095 assertEquals("clientSecret", credentials.getClientSecret()); 1096 assertEquals(scopes, credentials.getScopes()); 1097 assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); 1098 assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); 1099 } 1100 1101 @Test newBuilder_allFields()1102 public void newBuilder_allFields() throws IOException { 1103 List<String> scopes = Arrays.asList("scope1", "scope2"); 1104 1105 AwsCredentials credentials = 1106 AwsCredentials.newBuilder() 1107 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1108 .setAudience("audience") 1109 .setSubjectTokenType("subjectTokenType") 1110 .setTokenUrl(STS_URL) 1111 .setTokenInfoUrl("tokenInfoUrl") 1112 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1113 .setTokenInfoUrl("tokenInfoUrl") 1114 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1115 .setQuotaProjectId("quotaProjectId") 1116 .setClientId("clientId") 1117 .setClientSecret("clientSecret") 1118 .setScopes(scopes) 1119 .setUniverseDomain("universeDomain") 1120 .build(); 1121 1122 AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); 1123 assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); 1124 assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); 1125 assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); 1126 assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); 1127 assertEquals( 1128 credentials.getServiceAccountImpersonationUrl(), 1129 newBuilderCreds.getServiceAccountImpersonationUrl()); 1130 assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); 1131 assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); 1132 assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); 1133 assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); 1134 assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); 1135 assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); 1136 assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain()); 1137 } 1138 1139 @Test newBuilder_noUniverseDomain_defaults()1140 public void newBuilder_noUniverseDomain_defaults() throws IOException { 1141 List<String> scopes = Arrays.asList("scope1", "scope2"); 1142 1143 AwsCredentials credentials = 1144 AwsCredentials.newBuilder() 1145 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1146 .setAudience("audience") 1147 .setSubjectTokenType("subjectTokenType") 1148 .setTokenUrl(STS_URL) 1149 .setTokenInfoUrl("tokenInfoUrl") 1150 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1151 .setTokenInfoUrl("tokenInfoUrl") 1152 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1153 .setQuotaProjectId("quotaProjectId") 1154 .setClientId("clientId") 1155 .setClientSecret("clientSecret") 1156 .setScopes(scopes) 1157 .build(); 1158 1159 AwsCredentials newBuilderCreds = AwsCredentials.newBuilder(credentials).build(); 1160 assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); 1161 assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); 1162 assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); 1163 assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); 1164 assertEquals( 1165 credentials.getServiceAccountImpersonationUrl(), 1166 newBuilderCreds.getServiceAccountImpersonationUrl()); 1167 assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); 1168 assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); 1169 assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); 1170 assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); 1171 assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); 1172 assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); 1173 assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain()); 1174 } 1175 1176 @Test builder_defaultRegionalCredentialVerificationUrlOverride()1177 public void builder_defaultRegionalCredentialVerificationUrlOverride() throws IOException { 1178 List<String> scopes = Arrays.asList("scope1", "scope2"); 1179 1180 AwsSecurityCredentialsSupplier supplier = 1181 new TestAwsSecurityCredentialsSupplier("region", null, null, null); 1182 1183 AwsCredentials credentials = 1184 AwsCredentials.newBuilder() 1185 .setAwsSecurityCredentialsSupplier(supplier) 1186 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1187 .setAudience("audience") 1188 .setSubjectTokenType("subjectTokenType") 1189 .setTokenUrl(STS_URL) 1190 .setTokenInfoUrl("tokenInfoUrl") 1191 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1192 .setQuotaProjectId("quotaProjectId") 1193 .setClientId("clientId") 1194 .setClientSecret("clientSecret") 1195 .setScopes(scopes) 1196 .build(); 1197 1198 assertNull(credentials.getRegionalCredentialVerificationUrlOverride()); 1199 assertEquals( 1200 DEFAULT_REGIONAL_CREDENTIAL_VERIFICATION_URL, 1201 credentials.getRegionalCredentialVerificationUrl()); 1202 } 1203 1204 @Test builder_supplierAndCredSourceThrows()1205 public void builder_supplierAndCredSourceThrows() throws IOException { 1206 List<String> scopes = Arrays.asList("scope1", "scope2"); 1207 1208 AwsSecurityCredentialsSupplier supplier = 1209 new TestAwsSecurityCredentialsSupplier("region", null, null, null); 1210 1211 try { 1212 AwsCredentials credentials = 1213 AwsCredentials.newBuilder() 1214 .setAwsSecurityCredentialsSupplier(supplier) 1215 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1216 .setAudience("audience") 1217 .setSubjectTokenType("subjectTokenType") 1218 .setTokenUrl(STS_URL) 1219 .setTokenInfoUrl("tokenInfoUrl") 1220 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1221 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1222 .setQuotaProjectId("quotaProjectId") 1223 .setClientId("clientId") 1224 .setClientSecret("clientSecret") 1225 .setScopes(scopes) 1226 .build(); 1227 fail("Should not be able to continue without exception."); 1228 } catch (IllegalArgumentException exception) { 1229 assertEquals( 1230 "AwsCredentials cannot have both an awsSecurityCredentialsSupplier and a credentialSource.", 1231 exception.getMessage()); 1232 } 1233 } 1234 1235 @Test builder_noSupplieOrCredSourceThrows()1236 public void builder_noSupplieOrCredSourceThrows() throws IOException { 1237 List<String> scopes = Arrays.asList("scope1", "scope2"); 1238 1239 Supplier<AwsSecurityCredentials> testSupplier = () -> null; 1240 1241 try { 1242 AwsCredentials credentials = 1243 AwsCredentials.newBuilder() 1244 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1245 .setAudience("audience") 1246 .setSubjectTokenType("subjectTokenType") 1247 .setTokenUrl(STS_URL) 1248 .setTokenInfoUrl("tokenInfoUrl") 1249 .setTokenInfoUrl("tokenInfoUrl") 1250 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1251 .setQuotaProjectId("quotaProjectId") 1252 .setClientId("clientId") 1253 .setClientSecret("clientSecret") 1254 .setScopes(scopes) 1255 .build(); 1256 fail("Should not be able to continue without exception."); 1257 } catch (IllegalArgumentException exception) { 1258 assertEquals( 1259 "An awsSecurityCredentialsSupplier or a credentialSource must be provided.", 1260 exception.getMessage()); 1261 } 1262 } 1263 1264 @Test serialize()1265 public void serialize() throws IOException, ClassNotFoundException { 1266 List<String> scopes = Arrays.asList("scope1", "scope2"); 1267 1268 AwsCredentials testCredentials = 1269 AwsCredentials.newBuilder() 1270 .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) 1271 .setAudience("audience") 1272 .setSubjectTokenType("subjectTokenType") 1273 .setTokenUrl(STS_URL) 1274 .setTokenInfoUrl("tokenInfoUrl") 1275 .setCredentialSource(AWS_CREDENTIAL_SOURCE) 1276 .setTokenInfoUrl("tokenInfoUrl") 1277 .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) 1278 .setQuotaProjectId("quotaProjectId") 1279 .setClientId("clientId") 1280 .setClientSecret("clientSecret") 1281 .setUniverseDomain("universeDomain") 1282 .setScopes(scopes) 1283 .build(); 1284 1285 AwsCredentials deserializedCredentials = serializeAndDeserialize(testCredentials); 1286 assertEquals(testCredentials, deserializedCredentials); 1287 assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode()); 1288 assertEquals(testCredentials.toString(), deserializedCredentials.toString()); 1289 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 1290 } 1291 ValidateRequest( MockLowLevelHttpRequest request, String expectedUrl, Map<String, String> expectedHeaders)1292 private static void ValidateRequest( 1293 MockLowLevelHttpRequest request, String expectedUrl, Map<String, String> expectedHeaders) { 1294 assertEquals(expectedUrl, request.getUrl()); 1295 Map<String, List<String>> actualHeaders = request.getHeaders(); 1296 1297 for (Map.Entry<String, String> expectedHeader : expectedHeaders.entrySet()) { 1298 assertTrue(actualHeaders.containsKey(expectedHeader.getKey())); 1299 List<String> actualValues = actualHeaders.get(expectedHeader.getKey()); 1300 assertEquals(1, actualValues.size()); 1301 assertEquals(expectedHeader.getValue(), actualValues.get(0)); 1302 } 1303 } 1304 buildAwsCredentialSourceMap( MockExternalAccountCredentialsTransportFactory transportFactory)1305 private static Map<String, Object> buildAwsCredentialSourceMap( 1306 MockExternalAccountCredentialsTransportFactory transportFactory) { 1307 Map<String, Object> credentialSourceMap = new HashMap<>(); 1308 credentialSourceMap.put("environment_id", "aws1"); 1309 credentialSourceMap.put("region_url", transportFactory.transport.getAwsRegionUrl()); 1310 credentialSourceMap.put("url", transportFactory.transport.getAwsCredentialsUrl()); 1311 credentialSourceMap.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 1312 return credentialSourceMap; 1313 } 1314 buildAwsCredentialSource( MockExternalAccountCredentialsTransportFactory transportFactory)1315 private static AwsCredentialSource buildAwsCredentialSource( 1316 MockExternalAccountCredentialsTransportFactory transportFactory) { 1317 return new AwsCredentialSource(buildAwsCredentialSourceMap(transportFactory)); 1318 } 1319 buildAwsImdsv2CredentialSource( MockExternalAccountCredentialsTransportFactory transportFactory)1320 static AwsCredentialSource buildAwsImdsv2CredentialSource( 1321 MockExternalAccountCredentialsTransportFactory transportFactory) { 1322 Map<String, Object> credentialSourceMap = buildAwsCredentialSourceMap(transportFactory); 1323 credentialSourceMap.put( 1324 "imdsv2_session_token_url", transportFactory.transport.getAwsImdsv2SessionTokenUrl()); 1325 return new AwsCredentialSource(credentialSourceMap); 1326 } 1327 buildAwsIpv6CredentialSourceMap()1328 private static Map<String, Object> buildAwsIpv6CredentialSourceMap() { 1329 String regionUrl = "http://[fd00:ec2::254]/region"; 1330 String url = "http://[fd00:ec2::254]"; 1331 String imdsv2SessionTokenUrl = "http://[fd00:ec2::254]/imdsv2"; 1332 Map<String, Object> credentialSourceMap = new HashMap<>(); 1333 credentialSourceMap.put("environment_id", "aws1"); 1334 credentialSourceMap.put("region_url", regionUrl); 1335 credentialSourceMap.put("url", url); 1336 credentialSourceMap.put("imdsv2_session_token_url", imdsv2SessionTokenUrl); 1337 credentialSourceMap.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 1338 1339 return credentialSourceMap; 1340 } 1341 writeAwsCredentialsStream(String stsUrl, String regionUrl, String metadataUrl)1342 static InputStream writeAwsCredentialsStream(String stsUrl, String regionUrl, String metadataUrl) 1343 throws IOException { 1344 GenericJson json = new GenericJson(); 1345 json.put("audience", "audience"); 1346 json.put("subject_token_type", "subjectTokenType"); 1347 json.put("token_url", stsUrl); 1348 json.put("token_info_url", "tokenInfoUrl"); 1349 json.put("type", ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE); 1350 1351 GenericJson credentialSource = new GenericJson(); 1352 credentialSource.put("environment_id", "aws1"); 1353 credentialSource.put("region_url", regionUrl); 1354 credentialSource.put("url", metadataUrl); 1355 credentialSource.put("regional_cred_verification_url", GET_CALLER_IDENTITY_URL); 1356 json.put("credential_source", credentialSource); 1357 1358 return TestUtils.jsonToInputStream(json); 1359 } 1360 1361 class TestAwsSecurityCredentialsSupplier implements AwsSecurityCredentialsSupplier { 1362 1363 private String region; 1364 private AwsSecurityCredentials credentials; 1365 private IOException credentialException; 1366 private ExternalAccountSupplierContext expectedContext; 1367 TestAwsSecurityCredentialsSupplier( String region, AwsSecurityCredentials credentials, IOException credentialException, ExternalAccountSupplierContext expectedContext)1368 TestAwsSecurityCredentialsSupplier( 1369 String region, 1370 AwsSecurityCredentials credentials, 1371 IOException credentialException, 1372 ExternalAccountSupplierContext expectedContext) { 1373 this.region = region; 1374 this.credentials = credentials; 1375 this.credentialException = credentialException; 1376 this.expectedContext = expectedContext; 1377 } 1378 1379 @Override getRegion(ExternalAccountSupplierContext context)1380 public String getRegion(ExternalAccountSupplierContext context) throws IOException { 1381 if (expectedContext != null) { 1382 assertEquals(expectedContext.getAudience(), context.getAudience()); 1383 assertEquals(expectedContext.getSubjectTokenType(), context.getSubjectTokenType()); 1384 } 1385 return region; 1386 } 1387 1388 @Override getCredentials(ExternalAccountSupplierContext context)1389 public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext context) 1390 throws IOException { 1391 if (credentialException != null) { 1392 throw credentialException; 1393 } 1394 if (expectedContext != null) { 1395 assertEquals(expectedContext.getAudience(), context.getAudience()); 1396 assertEquals(expectedContext.getSubjectTokenType(), context.getSubjectTokenType()); 1397 } 1398 return credentials; 1399 } 1400 } 1401 } 1402