1 /* 2 * Copyright 2015, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import static org.junit.Assert.assertArrayEquals; 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertFalse; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNull; 39 import static org.junit.Assert.assertSame; 40 import static org.junit.Assert.assertTrue; 41 import static org.junit.Assert.fail; 42 43 import com.google.api.client.http.HttpStatusCodes; 44 import com.google.api.client.http.HttpTransport; 45 import com.google.api.client.http.LowLevelHttpRequest; 46 import com.google.api.client.http.LowLevelHttpResponse; 47 import com.google.api.client.json.webtoken.JsonWebToken.Payload; 48 import com.google.api.client.testing.http.MockLowLevelHttpRequest; 49 import com.google.api.client.testing.http.MockLowLevelHttpResponse; 50 import com.google.api.client.util.ArrayMap; 51 import com.google.api.client.util.Clock; 52 import com.google.auth.Credentials; 53 import com.google.auth.ServiceAccountSigner.SigningException; 54 import com.google.auth.TestUtils; 55 import com.google.auth.http.HttpTransportFactory; 56 import com.google.auth.oauth2.DefaultCredentialsProviderTest.MockRequestCountingTransportFactory; 57 import java.io.IOException; 58 import java.net.URI; 59 import java.util.ArrayDeque; 60 import java.util.Arrays; 61 import java.util.Collection; 62 import java.util.Collections; 63 import java.util.List; 64 import java.util.Map; 65 import java.util.Queue; 66 import java.util.stream.IntStream; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.junit.runners.JUnit4; 70 71 /** Test case for {@link ComputeEngineCredentials}. */ 72 @RunWith(JUnit4.class) 73 public class ComputeEngineCredentialsTest extends BaseSerializationTest { 74 75 private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); 76 77 private static final String TOKEN_URL = 78 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"; 79 80 // Id Token which includes basic default claims 81 public static final String STANDARD_ID_TOKEN = 82 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyO" 83 + "TNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiL" 84 + "CJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwi" 85 + "aXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In0" 86 + ".redacted"; 87 88 // Id Token which includes GCE extended claims 89 public static final String FULL_ID_TOKEN = 90 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyOTNh" 91 + "ZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiLCJhe" 92 + "nAiOiIxMTIxNzkwNjI3MjAzOTEzMDU4ODUiLCJlbWFpbCI6IjEwNzEyODQxODQ0MzYtY29tcHV0ZUBkZXZlbG9wZ" 93 + "XIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NjQ1MTk0OTYsImdvb" 94 + "2dsZSI6eyJjb21wdXRlX2VuZ2luZSI6eyJpbnN0YW5jZV9jcmVhdGlvbl90aW1lc3RhbXAiOjE1NjMyMzA5MDcsI" 95 + "mluc3RhbmNlX2lkIjoiMzQ5Nzk3NDM5MzQ0MTE3OTI0MyIsImluc3RhbmNlX25hbWUiOiJpYW0iLCJwcm9qZWN0X" 96 + "2lkIjoibWluZXJhbC1taW51dGlhLTgyMCIsInByb2plY3RfbnVtYmVyIjoxMDcxMjg0MTg0NDM2LCJ6b25lIjoid" 97 + "XMtY2VudHJhbDEtYSJ9fSwiaWF0IjoxNTY0NTE1ODk2LCJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb" 98 + "20iLCJzdWIiOiIxMTIxNzkwNjI3MjAzOTEzMDU4ODUifQ.redacted"; 99 100 // Id Token which includes GCE extended claims and any VM License data (if applicable) 101 public static final String FULL_ID_TOKEN_WITH_LICENSE = 102 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOG" 103 + "I3OTIyOTNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.ew0KICAiYXVkIjogImh0dHBzOi8" 104 + "vZm9vLmJhciIsDQogICJhenAiOiAiMTEyMTc5MDYyNzIwMzkxMzA1ODg1IiwNCiAgImVtYWlsIjogIjEyMzQ1Ni1" 105 + "jb21wdXRlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwNCiAgImVtYWlsX3ZlcmlmaWVkIjogdHJ1ZSw" 106 + "NCiAgImV4cCI6IDE1NjQ1MTk0OTYsDQogICJnb29nbGUiOiB7DQogICAgImNvbXB1dGVfZW5naW5lIjogew0KICA" 107 + "gICAgImluc3RhbmNlX2NyZWF0aW9uX3RpbWVzdGFtcCI6IDE1NjMyMzA5MDcsDQogICAgICAiaW5zdGFuY2VfaWQ" 108 + "iOiAiMzQ5Nzk3NDM5MzQ0MTE3OTI0MyIsDQogICAgICAiaW5zdGFuY2VfbmFtZSI6ICJpYW0iLA0KICAgICAgInB" 109 + "yb2plY3RfaWQiOiAiZm9vLWJhci04MjAiLA0KICAgICAgInByb2plY3RfbnVtYmVyIjogMTA3MTI4NDE4NDQzNiw" 110 + "NCiAgICAgICJ6b25lIjogInVzLWNlbnRyYWwxLWEiDQogICAgfSwNCiAgICAibGljZW5zZSI6IFsNCiAgICAgICA" 111 + "iTElDRU5TRV8xIiwNCiAgICAgICAiTElDRU5TRV8yIg0KICAgIF0NCiAgfSwNCiAgImlhdCI6IDE1NjQ1MTU4OTY" 112 + "sDQogICJpc3MiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwNCiAgInN1YiI6ICIxMTIxNzkwNjI3MjA" 113 + "zOTEzMDU4ODUiDQp9.redacted"; 114 115 @Test buildTokenUrlWithScopes_null_scopes()116 public void buildTokenUrlWithScopes_null_scopes() { 117 ComputeEngineCredentials credentials = 118 ComputeEngineCredentials.newBuilder().setScopes(null).build(); 119 Collection<String> scopes = credentials.getScopes(); 120 String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); 121 122 assertEquals(TOKEN_URL, tokenUrlWithScopes); 123 assertTrue(scopes.isEmpty()); 124 } 125 126 @Test buildTokenUrlWithScopes_empty_scopes()127 public void buildTokenUrlWithScopes_empty_scopes() { 128 ComputeEngineCredentials.Builder builder = 129 ComputeEngineCredentials.newBuilder().setScopes(Collections.<String>emptyList()); 130 ComputeEngineCredentials credentials = builder.build(); 131 Collection<String> scopes = credentials.getScopes(); 132 String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); 133 134 assertEquals(TOKEN_URL, tokenUrlWithScopes); 135 assertTrue(scopes.isEmpty()); 136 assertTrue(builder.getScopes().isEmpty()); 137 } 138 139 @Test buildTokenUrlWithScopes_single_scope()140 public void buildTokenUrlWithScopes_single_scope() { 141 ComputeEngineCredentials credentials = 142 ComputeEngineCredentials.newBuilder().setScopes(Arrays.asList("foo")).build(); 143 String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); 144 Collection<String> scopes = credentials.getScopes(); 145 146 assertEquals(TOKEN_URL + "?scopes=foo", tokenUrlWithScopes); 147 assertEquals(1, scopes.size()); 148 assertEquals("foo", scopes.toArray()[0]); 149 } 150 151 @Test buildTokenUrlWithScopes_multiple_scopes()152 public void buildTokenUrlWithScopes_multiple_scopes() { 153 ComputeEngineCredentials credentials = 154 ComputeEngineCredentials.newBuilder() 155 .setScopes(Arrays.asList(null, "foo", "", "bar")) 156 .build(); 157 Collection<String> scopes = credentials.getScopes(); 158 String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); 159 160 assertEquals(TOKEN_URL + "?scopes=foo,bar", tokenUrlWithScopes); 161 assertEquals(2, scopes.size()); 162 assertEquals("foo", scopes.toArray()[0]); 163 assertEquals("bar", scopes.toArray()[1]); 164 } 165 166 @Test buildTokenUrlWithScopes_defaultScopes()167 public void buildTokenUrlWithScopes_defaultScopes() { 168 ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().build(); 169 credentials = 170 (ComputeEngineCredentials) 171 credentials.createScoped(null, Arrays.asList(null, "foo", "", "bar")); 172 Collection<String> scopes = credentials.getScopes(); 173 String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); 174 175 assertEquals(TOKEN_URL + "?scopes=foo,bar", tokenUrlWithScopes); 176 assertEquals(2, scopes.size()); 177 assertEquals("foo", scopes.toArray()[0]); 178 assertEquals("bar", scopes.toArray()[1]); 179 } 180 181 @Test buildScoped_scopesPresent()182 public void buildScoped_scopesPresent() throws IOException { 183 ComputeEngineCredentials credentials = 184 ComputeEngineCredentials.newBuilder().setScopes(null).build(); 185 ComputeEngineCredentials scopedCredentials = 186 (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); 187 Collection<String> scopes = scopedCredentials.getScopes(); 188 189 assertEquals(1, scopes.size()); 190 assertEquals("foo", scopes.toArray()[0]); 191 } 192 193 @Test buildScoped_correctMargins()194 public void buildScoped_correctMargins() throws IOException { 195 ComputeEngineCredentials credentials = 196 ComputeEngineCredentials.newBuilder().setScopes(null).build(); 197 ComputeEngineCredentials scopedCredentials = 198 (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); 199 200 assertEquals( 201 ComputeEngineCredentials.COMPUTE_EXPIRATION_MARGIN, 202 scopedCredentials.getExpirationMargin()); 203 assertEquals( 204 ComputeEngineCredentials.COMPUTE_REFRESH_MARGIN, scopedCredentials.getRefreshMargin()); 205 } 206 207 @Test buildScoped_explicitUniverse()208 public void buildScoped_explicitUniverse() throws IOException { 209 ComputeEngineCredentials credentials = 210 ComputeEngineCredentials.newBuilder() 211 .setScopes(null) 212 .setUniverseDomain("some-universe") 213 .build(); 214 ComputeEngineCredentials scopedCredentials = 215 (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); 216 217 assertEquals("some-universe", scopedCredentials.getUniverseDomain()); 218 assertEquals(true, scopedCredentials.isExplicitUniverseDomain()); 219 } 220 221 @Test createScoped_defaultScopes()222 public void createScoped_defaultScopes() { 223 GoogleCredentials credentials = 224 ComputeEngineCredentials.create().createScoped(null, Arrays.asList("foo")); 225 Collection<String> scopes = ((ComputeEngineCredentials) credentials).getScopes(); 226 227 assertEquals(1, scopes.size()); 228 assertEquals("foo", scopes.toArray()[0]); 229 } 230 231 @Test create_scoped_correctMargins()232 public void create_scoped_correctMargins() { 233 GoogleCredentials credentials = 234 ComputeEngineCredentials.create().createScoped(null, Arrays.asList("foo")); 235 236 assertEquals( 237 ComputeEngineCredentials.COMPUTE_EXPIRATION_MARGIN, credentials.getExpirationMargin()); 238 assertEquals(ComputeEngineCredentials.COMPUTE_REFRESH_MARGIN, credentials.getRefreshMargin()); 239 } 240 241 @Test getRequestMetadata_hasAccessToken()242 public void getRequestMetadata_hasAccessToken() throws IOException { 243 String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 244 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 245 transportFactory.transport.setAccessToken(accessToken); 246 ComputeEngineCredentials credentials = 247 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 248 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 249 250 TestUtils.assertContainsBearerToken(metadata, accessToken); 251 } 252 253 @Test getRequestMetadata_missingServiceAccount_throws()254 public void getRequestMetadata_missingServiceAccount_throws() { 255 String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 256 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 257 transportFactory.transport.setAccessToken(accessToken); 258 transportFactory.transport.setRequestStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); 259 ComputeEngineCredentials credentials = 260 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 261 try { 262 credentials.getRequestMetadata(CALL_URI); 263 fail("Expected error refreshing token."); 264 } catch (IOException expected) { 265 String message = expected.getMessage(); 266 assertTrue(message.contains(Integer.toString(HttpStatusCodes.STATUS_CODE_NOT_FOUND))); 267 // Message should mention scopes are missing on the VM. 268 assertTrue(message.contains("scope")); 269 } 270 } 271 272 @Test getRequestMetadata_serverError_throws()273 public void getRequestMetadata_serverError_throws() { 274 String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 275 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 276 transportFactory.transport.setAccessToken(accessToken); 277 transportFactory.transport.setRequestStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR); 278 ComputeEngineCredentials credentials = 279 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 280 try { 281 credentials.getRequestMetadata(CALL_URI); 282 fail("Expected error refreshing token."); 283 } catch (IOException expected) { 284 String message = expected.getMessage(); 285 assertTrue(message.contains(Integer.toString(HttpStatusCodes.STATUS_CODE_SERVER_ERROR))); 286 assertTrue(message.contains("Unexpected")); 287 } 288 } 289 290 @Test equals_true()291 public void equals_true() throws IOException { 292 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 293 ComputeEngineCredentials explicitUniverseCredentials = 294 ComputeEngineCredentials.newBuilder() 295 .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) 296 .setHttpTransportFactory(transportFactory) 297 .build(); 298 ComputeEngineCredentials otherCredentials = 299 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 300 assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, otherCredentials.getUniverseDomain()); 301 assertFalse(explicitUniverseCredentials.equals(otherCredentials)); 302 assertFalse(otherCredentials.equals(explicitUniverseCredentials)); 303 ComputeEngineCredentials otherExplicitUniverseCredentials = 304 ComputeEngineCredentials.newBuilder() 305 .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) 306 .setHttpTransportFactory(transportFactory) 307 .build(); 308 assertTrue(explicitUniverseCredentials.equals(otherExplicitUniverseCredentials)); 309 assertTrue(otherExplicitUniverseCredentials.equals(explicitUniverseCredentials)); 310 } 311 312 @Test equals_false_transportFactory()313 public void equals_false_transportFactory() throws IOException { 314 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 315 MockMetadataServerTransportFactory serverTransportFactory = 316 new MockMetadataServerTransportFactory(); 317 ComputeEngineCredentials credentials = 318 ComputeEngineCredentials.newBuilder() 319 .setHttpTransportFactory(serverTransportFactory) 320 .build(); 321 ComputeEngineCredentials otherCredentials = 322 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(httpTransportFactory).build(); 323 assertFalse(credentials.equals(otherCredentials)); 324 assertFalse(otherCredentials.equals(credentials)); 325 } 326 327 @Test toString_explicit_containsFields()328 public void toString_explicit_containsFields() throws IOException { 329 MockMetadataServerTransportFactory serverTransportFactory = 330 new MockMetadataServerTransportFactory(); 331 String expectedToString = 332 String.format( 333 "ComputeEngineCredentials{quotaProjectId=%s, universeDomain=%s, isExplicitUniverseDomain=%s, transportFactoryClassName=%s, scopes=%s}", 334 "some-project", 335 "some-domain", 336 true, 337 MockMetadataServerTransportFactory.class.getName(), 338 "[some scope]"); 339 GoogleCredentials credentials = 340 ComputeEngineCredentials.newBuilder() 341 .setHttpTransportFactory(serverTransportFactory) 342 .setQuotaProjectId("some-project") 343 .setUniverseDomain("some-domain") 344 .build(); 345 credentials = credentials.createScoped("some scope"); 346 assertEquals(expectedToString, credentials.toString()); 347 } 348 349 @Test hashCode_equals()350 public void hashCode_equals() throws IOException { 351 MockMetadataServerTransportFactory serverTransportFactory = 352 new MockMetadataServerTransportFactory(); 353 ComputeEngineCredentials credentials = 354 ComputeEngineCredentials.newBuilder() 355 .setHttpTransportFactory(serverTransportFactory) 356 .build(); 357 ComputeEngineCredentials otherCredentials = 358 ComputeEngineCredentials.newBuilder() 359 .setHttpTransportFactory(serverTransportFactory) 360 .build(); 361 assertEquals(credentials.hashCode(), otherCredentials.hashCode()); 362 } 363 364 @Test toBuilder()365 public void toBuilder() { 366 ComputeEngineCredentials credentials = 367 ComputeEngineCredentials.newBuilder() 368 .setHttpTransportFactory(new MockMetadataServerTransportFactory()) 369 .setQuotaProjectId("quota-project") 370 .build(); 371 372 ComputeEngineCredentials secondCredentials = credentials.toBuilder().build(); 373 374 assertEquals(credentials, secondCredentials); 375 } 376 377 @Test serialize()378 public void serialize() throws IOException, ClassNotFoundException { 379 MockMetadataServerTransportFactory serverTransportFactory = 380 new MockMetadataServerTransportFactory(); 381 ComputeEngineCredentials credentials = 382 ComputeEngineCredentials.newBuilder() 383 .setHttpTransportFactory(serverTransportFactory) 384 .build(); 385 GoogleCredentials deserializedCredentials = serializeAndDeserialize(credentials); 386 assertEquals(credentials, deserializedCredentials); 387 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 388 assertEquals(credentials.toString(), deserializedCredentials.toString()); 389 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 390 credentials = ComputeEngineCredentials.newBuilder().build(); 391 deserializedCredentials = serializeAndDeserialize(credentials); 392 assertEquals(credentials, deserializedCredentials); 393 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 394 assertEquals(credentials.toString(), deserializedCredentials.toString()); 395 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 396 } 397 398 @Test getAccount_sameAs()399 public void getAccount_sameAs() throws IOException { 400 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 401 String defaultAccountEmail = "[email protected]"; 402 403 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 404 ComputeEngineCredentials credentials = 405 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 406 407 assertEquals(defaultAccountEmail, credentials.getAccount()); 408 } 409 410 @Test getAccount_missing_throws()411 public void getAccount_missing_throws() { 412 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 413 String defaultAccountEmail = "[email protected]"; 414 415 transportFactory.transport = 416 new MockMetadataServerTransport() { 417 @Override 418 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 419 if (isGetServiceAccountsUrl(url)) { 420 return new MockLowLevelHttpRequest(url) { 421 @Override 422 public LowLevelHttpResponse execute() throws IOException { 423 return new MockLowLevelHttpResponse() 424 .setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND) 425 .setContent(""); 426 } 427 }; 428 } 429 return super.buildRequest(method, url); 430 } 431 }; 432 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 433 ComputeEngineCredentials credentials = 434 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 435 436 try { 437 credentials.getAccount(); 438 fail("Fetching default service account should have failed"); 439 } catch (RuntimeException e) { 440 assertEquals("Failed to get service account", e.getMessage()); 441 assertNotNull(e.getCause()); 442 assertTrue(e.getCause().getMessage().contains("404")); 443 } 444 } 445 446 @Test getAccount_emptyContent_throws()447 public void getAccount_emptyContent_throws() { 448 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 449 String defaultAccountEmail = "[email protected]"; 450 451 transportFactory.transport = 452 new MockMetadataServerTransport() { 453 @Override 454 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 455 if (isGetServiceAccountsUrl(url)) { 456 return new MockLowLevelHttpRequest(url) { 457 @Override 458 public LowLevelHttpResponse execute() throws IOException { 459 return new MockLowLevelHttpResponse() 460 .setStatusCode(HttpStatusCodes.STATUS_CODE_OK); 461 } 462 }; 463 } 464 return super.buildRequest(method, url); 465 } 466 }; 467 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 468 ComputeEngineCredentials credentials = 469 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 470 471 try { 472 credentials.getAccount(); 473 fail("Fetching default service account should have failed"); 474 } catch (RuntimeException e) { 475 assertEquals("Failed to get service account", e.getMessage()); 476 assertNotNull(e.getCause()); 477 assertTrue(e.getCause().getMessage().contains("Empty content")); 478 } 479 } 480 481 @Test sign_sameAs()482 public void sign_sameAs() throws IOException { 483 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 484 final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 485 String defaultAccountEmail = "[email protected]"; 486 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 487 488 transportFactory.transport.setAccessToken(accessToken); 489 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 490 transportFactory.transport.setSignature(expectedSignature); 491 ComputeEngineCredentials credentials = 492 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 493 494 assertArrayEquals(expectedSignature, credentials.sign(expectedSignature)); 495 } 496 497 @Test sign_getAccountFails()498 public void sign_getAccountFails() throws IOException { 499 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 500 final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 501 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 502 503 transportFactory.transport.setAccessToken(accessToken); 504 transportFactory.transport.setSignature(expectedSignature); 505 ComputeEngineCredentials credentials = 506 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 507 508 try { 509 credentials.sign(expectedSignature); 510 fail("Should not be able to use credential without exception."); 511 } catch (SigningException ex) { 512 assertNotNull(ex.getMessage()); 513 assertNotNull(ex.getCause()); 514 } 515 } 516 517 @Test sign_accessDenied_throws()518 public void sign_accessDenied_throws() { 519 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 520 final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 521 String defaultAccountEmail = "[email protected]"; 522 523 transportFactory.transport = 524 new MockMetadataServerTransport() { 525 @Override 526 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 527 if (isSignRequestUrl(url)) { 528 return new MockLowLevelHttpRequest(url) { 529 @Override 530 public LowLevelHttpResponse execute() throws IOException { 531 return new MockLowLevelHttpResponse() 532 .setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN) 533 .setContent(TestUtils.errorJson("Sign Error")); 534 } 535 }; 536 } 537 return super.buildRequest(method, url); 538 } 539 }; 540 541 transportFactory.transport.setAccessToken(accessToken); 542 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 543 544 ComputeEngineCredentials credentials = 545 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 546 547 try { 548 byte[] bytes = {0xD, 0xE, 0xA, 0xD}; 549 credentials.sign(bytes); 550 fail("Signing should have failed"); 551 } catch (SigningException e) { 552 assertEquals("Failed to sign the provided bytes", e.getMessage()); 553 assertNotNull(e.getCause()); 554 assertTrue(e.getCause().getMessage().contains("403")); 555 } 556 } 557 558 @Test sign_serverError_throws()559 public void sign_serverError_throws() { 560 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 561 final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 562 String defaultAccountEmail = "[email protected]"; 563 564 transportFactory.transport = 565 new MockMetadataServerTransport() { 566 @Override 567 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 568 if (isSignRequestUrl(url)) { 569 return new MockLowLevelHttpRequest(url) { 570 @Override 571 public LowLevelHttpResponse execute() throws IOException { 572 return new MockLowLevelHttpResponse() 573 .setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR) 574 .setContent(TestUtils.errorJson("Sign Error")); 575 } 576 }; 577 } 578 return super.buildRequest(method, url); 579 } 580 }; 581 582 transportFactory.transport.setAccessToken(accessToken); 583 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 584 585 ComputeEngineCredentials credentials = 586 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 587 588 try { 589 byte[] bytes = {0xD, 0xE, 0xA, 0xD}; 590 credentials.sign(bytes); 591 fail("Signing should have failed"); 592 } catch (SigningException e) { 593 assertEquals("Failed to sign the provided bytes", e.getMessage()); 594 assertNotNull(e.getCause()); 595 assertTrue(e.getCause().getMessage().contains("500")); 596 } 597 } 598 599 @Test refresh_503_retryable_throws()600 public void refresh_503_retryable_throws() { 601 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 602 603 transportFactory.transport = 604 new MockMetadataServerTransport() { 605 @Override 606 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 607 return new MockLowLevelHttpRequest(url) { 608 @Override 609 public LowLevelHttpResponse execute() throws IOException { 610 return new MockLowLevelHttpResponse() 611 .setStatusCode(HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE) 612 .setContent(TestUtils.errorJson("Some error")); 613 } 614 }; 615 } 616 }; 617 618 ComputeEngineCredentials credentials = 619 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 620 621 try { 622 credentials.refreshAccessToken(); 623 fail("Should have failed"); 624 } catch (IOException e) { 625 assertTrue(e.getCause().getMessage().contains("503")); 626 assertTrue(e instanceof GoogleAuthException); 627 assertTrue(((GoogleAuthException) e).isRetryable()); 628 } 629 } 630 631 @Test refresh_non503_ioexception_throws()632 public void refresh_non503_ioexception_throws() { 633 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 634 final Queue<Integer> responseSequence = new ArrayDeque<>(); 635 IntStream.rangeClosed(400, 600).forEach(i -> responseSequence.add(i)); 636 637 while (!responseSequence.isEmpty()) { 638 if (responseSequence.peek() == 503) { 639 responseSequence.poll(); 640 continue; 641 } 642 643 transportFactory.transport = 644 new MockMetadataServerTransport() { 645 @Override 646 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 647 return new MockLowLevelHttpRequest(url) { 648 @Override 649 public LowLevelHttpResponse execute() throws IOException { 650 return new MockLowLevelHttpResponse() 651 .setStatusCode(responseSequence.poll()) 652 .setContent(TestUtils.errorJson("Some error")); 653 } 654 }; 655 } 656 }; 657 658 ComputeEngineCredentials credentials = 659 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 660 661 try { 662 credentials.refreshAccessToken(); 663 fail("Should have failed"); 664 } catch (IOException e) { 665 assertFalse(e instanceof GoogleAuthException); 666 } 667 } 668 } 669 670 @Test getUniverseDomain_fromMetadata()671 public void getUniverseDomain_fromMetadata() throws IOException { 672 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 673 674 transportFactory.transport = 675 new MockMetadataServerTransport() { 676 @Override 677 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 678 return new MockLowLevelHttpRequest(url) { 679 @Override 680 public LowLevelHttpResponse execute() throws IOException { 681 return new MockLowLevelHttpResponse() 682 .setStatusCode(HttpStatusCodes.STATUS_CODE_OK) 683 .setContent("some-universe.xyz"); 684 } 685 }; 686 } 687 }; 688 689 ComputeEngineCredentials credentials = 690 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 691 692 String universeDomain = credentials.getUniverseDomain(); 693 assertEquals("some-universe.xyz", universeDomain); 694 assertEquals(false, credentials.isExplicitUniverseDomain()); 695 } 696 697 @Test getUniverseDomain_fromMetadata_emptyBecomesDefault()698 public void getUniverseDomain_fromMetadata_emptyBecomesDefault() throws IOException { 699 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 700 701 transportFactory.transport = 702 new MockMetadataServerTransport() { 703 @Override 704 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 705 return new MockLowLevelHttpRequest(url) { 706 @Override 707 public LowLevelHttpResponse execute() throws IOException { 708 return new MockLowLevelHttpResponse() 709 .setStatusCode(HttpStatusCodes.STATUS_CODE_OK) 710 .setContent(""); 711 } 712 }; 713 } 714 }; 715 716 ComputeEngineCredentials credentials = 717 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 718 719 String universeDomain = credentials.getUniverseDomain(); 720 assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); 721 assertEquals(false, credentials.isExplicitUniverseDomain()); 722 } 723 724 @Test getUniverseDomain_fromMetadata_404_default()725 public void getUniverseDomain_fromMetadata_404_default() throws IOException { 726 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 727 728 transportFactory.transport = 729 new MockMetadataServerTransport() { 730 @Override 731 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 732 return new MockLowLevelHttpRequest(url) { 733 @Override 734 public LowLevelHttpResponse execute() throws IOException { 735 return new MockLowLevelHttpResponse() 736 .setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND) 737 .setContent("some content"); 738 } 739 }; 740 } 741 }; 742 743 ComputeEngineCredentials credentials = 744 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 745 746 String universeDomain = credentials.getUniverseDomain(); 747 assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); 748 assertEquals(false, credentials.isExplicitUniverseDomain()); 749 } 750 751 @Test getUniverseDomain_explicitSet_NoMdsCall()752 public void getUniverseDomain_explicitSet_NoMdsCall() throws IOException { 753 MockRequestCountingTransportFactory transportFactory = 754 new MockRequestCountingTransportFactory(); 755 756 ComputeEngineCredentials credentials = 757 ComputeEngineCredentials.newBuilder() 758 .setHttpTransportFactory(transportFactory) 759 .setUniverseDomain("explicit.universe") 760 .build(); 761 762 String universeDomain = credentials.getUniverseDomain(); 763 assertEquals("explicit.universe", universeDomain); 764 assertEquals(true, credentials.isExplicitUniverseDomain()); 765 assertEquals(0, transportFactory.transport.getRequestCount()); 766 } 767 768 @Test getUniverseDomain_explicitGduSet_NoMdsCall()769 public void getUniverseDomain_explicitGduSet_NoMdsCall() throws IOException { 770 MockRequestCountingTransportFactory transportFactory = 771 new MockRequestCountingTransportFactory(); 772 773 ComputeEngineCredentials credentials = 774 ComputeEngineCredentials.newBuilder() 775 .setHttpTransportFactory(transportFactory) 776 .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) 777 .build(); 778 779 String universeDomain = credentials.getUniverseDomain(); 780 assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); 781 assertEquals(true, credentials.isExplicitUniverseDomain()); 782 assertEquals(0, transportFactory.transport.getRequestCount()); 783 } 784 785 @Test getUniverseDomain_fromMetadata_non404error_throws()786 public void getUniverseDomain_fromMetadata_non404error_throws() throws IOException { 787 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 788 MockMetadataServerTransport transport = transportFactory.transport; 789 790 ComputeEngineCredentials credentials = 791 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 792 793 for (int status = 400; status < 600; status++) { 794 // 404 should not throw and tested separately 795 if (status == 404) { 796 continue; 797 } 798 try { 799 transportFactory.transport.setRequestStatusCode(status); 800 credentials.getUniverseDomain(); 801 fail("Should not be able to use credential without exception."); 802 } catch (GoogleAuthException ex) { 803 assertTrue(ex.isRetryable()); 804 } 805 } 806 } 807 808 @Test sign_emptyContent_throws()809 public void sign_emptyContent_throws() { 810 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 811 String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; 812 String defaultAccountEmail = "[email protected]"; 813 814 transportFactory.transport = 815 new MockMetadataServerTransport() { 816 @Override 817 public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { 818 if (isSignRequestUrl(url)) { 819 return new MockLowLevelHttpRequest(url) { 820 @Override 821 public LowLevelHttpResponse execute() throws IOException { 822 return new MockLowLevelHttpResponse() 823 .setStatusCode(HttpStatusCodes.STATUS_CODE_OK); 824 } 825 }; 826 } 827 return super.buildRequest(method, url); 828 } 829 }; 830 831 transportFactory.transport.setAccessToken(accessToken); 832 transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); 833 834 ComputeEngineCredentials credentials = 835 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 836 837 try { 838 byte[] bytes = {0xD, 0xE, 0xA, 0xD}; 839 credentials.sign(bytes); 840 fail("Signing should have failed"); 841 } catch (SigningException e) { 842 assertEquals("Failed to sign the provided bytes", e.getMessage()); 843 assertNotNull(e.getCause()); 844 assertTrue(e.getCause().getMessage().contains("Empty content")); 845 } 846 } 847 848 @Test idTokenWithAudience_sameAs()849 public void idTokenWithAudience_sameAs() throws IOException { 850 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 851 transportFactory.transport.setIdToken(STANDARD_ID_TOKEN); 852 ComputeEngineCredentials credentials = 853 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 854 855 String targetAudience = "https://foo.bar"; 856 IdTokenCredentials tokenCredential = 857 IdTokenCredentials.newBuilder() 858 .setIdTokenProvider(credentials) 859 .setTargetAudience(targetAudience) 860 .build(); 861 tokenCredential.refresh(); 862 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); 863 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); 864 assertEquals( 865 targetAudience, 866 (String) tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudience()); 867 } 868 869 @Test idTokenWithAudience_standard()870 public void idTokenWithAudience_standard() throws IOException { 871 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 872 ComputeEngineCredentials credentials = 873 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 874 875 String targetAudience = "https://foo.bar"; 876 IdTokenCredentials tokenCredential = 877 IdTokenCredentials.newBuilder() 878 .setIdTokenProvider(credentials) 879 .setTargetAudience(targetAudience) 880 .build(); 881 tokenCredential.refresh(); 882 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); 883 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); 884 assertNull(tokenCredential.getIdToken().getJsonWebSignature().getPayload().get("google")); 885 } 886 887 @Test 888 @SuppressWarnings("unchecked") idTokenWithAudience_full()889 public void idTokenWithAudience_full() throws IOException { 890 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 891 ComputeEngineCredentials credentials = 892 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 893 894 String targetAudience = "https://foo.bar"; 895 IdTokenCredentials tokenCredential = 896 IdTokenCredentials.newBuilder() 897 .setIdTokenProvider(credentials) 898 .setTargetAudience(targetAudience) 899 .setOptions(Arrays.asList(IdTokenProvider.Option.FORMAT_FULL)) 900 .build(); 901 tokenCredential.refresh(); 902 Payload p = tokenCredential.getIdToken().getJsonWebSignature().getPayload(); 903 assertTrue("Full ID Token format not provided", p.containsKey("google")); 904 ArrayMap<String, ArrayMap> googleClaim = (ArrayMap<String, ArrayMap>) p.get("google"); 905 assertTrue(googleClaim.containsKey("compute_engine")); 906 } 907 908 @Test 909 @SuppressWarnings("unchecked") idTokenWithAudience_license()910 public void idTokenWithAudience_license() throws IOException { 911 MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); 912 ComputeEngineCredentials credentials = 913 ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); 914 915 String targetAudience = "https://foo.bar"; 916 IdTokenCredentials tokenCredential = 917 IdTokenCredentials.newBuilder() 918 .setIdTokenProvider(credentials) 919 .setTargetAudience(targetAudience) 920 .setOptions( 921 Arrays.asList( 922 IdTokenProvider.Option.FORMAT_FULL, IdTokenProvider.Option.LICENSES_TRUE)) 923 .build(); 924 tokenCredential.refresh(); 925 Payload p = tokenCredential.getIdToken().getJsonWebSignature().getPayload(); 926 assertTrue("Full ID Token format not provided", p.containsKey("google")); 927 ArrayMap<String, ArrayMap> googleClaim = (ArrayMap<String, ArrayMap>) p.get("google"); 928 assertTrue(googleClaim.containsKey("license")); 929 } 930 931 static class MockMetadataServerTransportFactory implements HttpTransportFactory { 932 933 MockMetadataServerTransport transport = new MockMetadataServerTransport(); 934 935 @Override create()936 public HttpTransport create() { 937 return transport; 938 } 939 } 940 } 941