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.assertNotEquals; 38 import static org.junit.Assert.assertNotNull; 39 import static org.junit.Assert.assertNull; 40 import static org.junit.Assert.assertSame; 41 import static org.junit.Assert.assertTrue; 42 import static org.junit.Assert.fail; 43 44 import com.google.api.client.http.HttpResponseException; 45 import com.google.api.client.json.GenericJson; 46 import com.google.api.client.json.JsonFactory; 47 import com.google.api.client.json.gson.GsonFactory; 48 import com.google.api.client.json.webtoken.JsonWebSignature; 49 import com.google.api.client.json.webtoken.JsonWebToken; 50 import com.google.api.client.testing.http.FixedClock; 51 import com.google.api.client.testing.http.MockLowLevelHttpResponse; 52 import com.google.api.client.util.Clock; 53 import com.google.api.client.util.Joiner; 54 import com.google.auth.Credentials; 55 import com.google.auth.RequestMetadataCallback; 56 import com.google.auth.TestUtils; 57 import com.google.auth.http.AuthHttpConstants; 58 import com.google.auth.http.HttpTransportFactory; 59 import com.google.common.collect.ImmutableSet; 60 import java.io.ByteArrayInputStream; 61 import java.io.ByteArrayOutputStream; 62 import java.io.IOException; 63 import java.io.InputStream; 64 import java.io.ObjectOutputStream; 65 import java.net.URI; 66 import java.security.InvalidKeyException; 67 import java.security.NoSuchAlgorithmException; 68 import java.security.PrivateKey; 69 import java.security.Signature; 70 import java.security.SignatureException; 71 import java.time.Duration; 72 import java.time.Instant; 73 import java.util.Arrays; 74 import java.util.Collection; 75 import java.util.Collections; 76 import java.util.List; 77 import java.util.Map; 78 import java.util.concurrent.atomic.AtomicBoolean; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 import org.junit.runners.JUnit4; 82 83 /** Test case for {@link ServiceAccountCredentials}. */ 84 @RunWith(JUnit4.class) 85 public class ServiceAccountCredentialsTest extends BaseSerializationTest { 86 87 private static final String CLIENT_EMAIL = 88 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com"; 89 private static final String CLIENT_ID = 90 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr.apps.googleusercontent.com"; 91 private static final String PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d"; 92 static final String PRIVATE_KEY_PKCS8 = 93 "-----BEGIN PRIVATE KEY-----\n" 94 + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i" 95 + "kv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0" 96 + "zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw" 97 + "4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/Gr" 98 + "CtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6" 99 + "D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrP" 100 + "SXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAut" 101 + "LPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEA" 102 + "gidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJ" 103 + "ADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ" 104 + "==\n-----END PRIVATE KEY-----\n"; 105 private static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; 106 private static final Collection<String> SCOPES = Collections.singletonList("dummy.scope"); 107 private static final Collection<String> DEFAULT_SCOPES = 108 Collections.singletonList("dummy.default.scope"); 109 private static final String USER = "[email protected]"; 110 private static final String PROJECT_ID = "project-id"; 111 private static final Collection<String> EMPTY_SCOPES = Collections.emptyList(); 112 private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); 113 private static final String JWT_AUDIENCE = "http://googleapis.com/"; 114 private static final HttpTransportFactory DUMMY_TRANSPORT_FACTORY = 115 new MockTokenServerTransportFactory(); 116 public static final String DEFAULT_ID_TOKEN = 117 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyO" 118 + "TNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiL" 119 + "CJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwi" 120 + "aXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In0" 121 + ".redacted"; 122 private static final String QUOTA_PROJECT = "sample-quota-project-id"; 123 private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600; 124 private static final int INVALID_LIFETIME = 43210; 125 private static final String JWT_ACCESS_PREFIX = "Bearer "; 126 private static final String GOOGLE_DEFAULT_UNIVERSE = "googleapis.com"; 127 createDefaultBuilderWithToken(String accessToken)128 private ServiceAccountCredentials.Builder createDefaultBuilderWithToken(String accessToken) 129 throws IOException { 130 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 131 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, accessToken); 132 return createDefaultBuilder().setHttpTransportFactory(transportFactory); 133 } 134 createDefaultBuilderWithScopes( Collection<String> scopes)135 private ServiceAccountCredentials.Builder createDefaultBuilderWithScopes( 136 Collection<String> scopes) throws IOException { 137 return createDefaultBuilder().setScopes(scopes); 138 } 139 createDefaultBuilderWithKey(PrivateKey privateKey)140 private ServiceAccountCredentials.Builder createDefaultBuilderWithKey(PrivateKey privateKey) { 141 ServiceAccountCredentials.Builder builder = 142 ServiceAccountCredentials.newBuilder() 143 .setClientId(CLIENT_ID) 144 .setClientEmail(CLIENT_EMAIL) 145 .setPrivateKey(privateKey) 146 .setPrivateKeyId(PRIVATE_KEY_ID) 147 .setProjectId(PROJECT_ID) 148 .setQuotaProjectId(QUOTA_PROJECT) 149 .setHttpTransportFactory(new MockHttpTransportFactory()); 150 151 return builder; 152 } 153 createDefaultBuilder()154 private ServiceAccountCredentials.Builder createDefaultBuilder() throws IOException { 155 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 156 return createDefaultBuilderWithKey(privateKey); 157 } 158 159 @Test setLifetime()160 public void setLifetime() throws IOException { 161 ServiceAccountCredentials.Builder builder = createDefaultBuilder(); 162 assertEquals(DEFAULT_LIFETIME_IN_SECONDS, builder.getLifetime()); 163 assertEquals(DEFAULT_LIFETIME_IN_SECONDS, builder.build().getLifetime()); 164 165 builder.setLifetime(4000); 166 assertEquals(4000, builder.getLifetime()); 167 assertEquals(4000, builder.build().getLifetime()); 168 169 builder.setLifetime(0); 170 assertEquals(DEFAULT_LIFETIME_IN_SECONDS, builder.build().getLifetime()); 171 } 172 173 @Test setLifetime_invalid_lifetime()174 public void setLifetime_invalid_lifetime() throws IOException, IllegalStateException { 175 try { 176 createDefaultBuilder().setLifetime(INVALID_LIFETIME).build(); 177 fail( 178 String.format( 179 "Should throw exception with message containing '%s'", 180 "lifetime must be less than or equal to 43200")); 181 } catch (IllegalStateException expected) { 182 assertTrue(expected.getMessage().contains("lifetime must be less than or equal to 43200")); 183 } 184 } 185 186 @Test createWithCustomLifetime()187 public void createWithCustomLifetime() throws IOException { 188 ServiceAccountCredentials credentials = createDefaultBuilder().build(); 189 credentials = credentials.createWithCustomLifetime(4000); 190 assertEquals(4000, credentials.getLifetime()); 191 } 192 193 @Test createdScoped_clones()194 public void createdScoped_clones() throws IOException { 195 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 196 ServiceAccountCredentials credentials = 197 createDefaultBuilderWithKey(privateKey) 198 .setServiceAccountUser(USER) 199 .setScopes(SCOPES) 200 .build(); 201 List<String> newScopes = Arrays.asList("scope1", "scope2"); 202 203 ServiceAccountCredentials newCredentials = 204 (ServiceAccountCredentials) credentials.createScoped(newScopes); 205 206 assertEquals(CLIENT_ID, newCredentials.getClientId()); 207 assertEquals(CLIENT_EMAIL, newCredentials.getClientEmail()); 208 assertEquals(privateKey, newCredentials.getPrivateKey()); 209 assertEquals(PRIVATE_KEY_ID, newCredentials.getPrivateKeyId()); 210 assertArrayEquals(newScopes.toArray(), newCredentials.getScopes().toArray()); 211 assertEquals(USER, newCredentials.getServiceAccountUser()); 212 assertEquals(PROJECT_ID, newCredentials.getProjectId()); 213 assertEquals(GOOGLE_DEFAULT_UNIVERSE, newCredentials.getUniverseDomain()); 214 215 assertArrayEquals( 216 SCOPES.toArray(), ((ServiceAccountCredentials) credentials).getScopes().toArray()); 217 } 218 219 @Test createdDelegated_clones()220 public void createdDelegated_clones() throws IOException { 221 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 222 ServiceAccountCredentials credentials = 223 createDefaultBuilderWithKey(privateKey) 224 .setScopes(SCOPES) 225 .setServiceAccountUser(USER) 226 .build(); 227 String newServiceAccountUser = "[email protected]"; 228 229 ServiceAccountCredentials newCredentials = 230 (ServiceAccountCredentials) credentials.createDelegated(newServiceAccountUser); 231 232 assertEquals(CLIENT_ID, newCredentials.getClientId()); 233 assertEquals(CLIENT_EMAIL, newCredentials.getClientEmail()); 234 assertEquals(privateKey, newCredentials.getPrivateKey()); 235 assertEquals(PRIVATE_KEY_ID, newCredentials.getPrivateKeyId()); 236 assertArrayEquals(SCOPES.toArray(), newCredentials.getScopes().toArray()); 237 assertEquals(newServiceAccountUser, newCredentials.getServiceAccountUser()); 238 assertEquals(PROJECT_ID, newCredentials.getProjectId()); 239 assertEquals(QUOTA_PROJECT, newCredentials.getQuotaProjectId()); 240 241 assertEquals(USER, ((ServiceAccountCredentials) credentials).getServiceAccountUser()); 242 } 243 244 @Test createAssertion_correct()245 public void createAssertion_correct() throws IOException { 246 List<String> scopes = Arrays.asList("scope1", "scope2"); 247 ServiceAccountCredentials.Builder builder = createDefaultBuilderWithScopes(scopes); 248 ServiceAccountCredentials credentials = builder.setServiceAccountUser(USER).build(); 249 250 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 251 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 252 String assertion = credentials.createAssertion(jsonFactory, currentTimeMillis); 253 254 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 255 JsonWebToken.Payload payload = signature.getPayload(); 256 assertEquals(CLIENT_EMAIL, payload.getIssuer()); 257 assertEquals(OAuth2Utils.TOKEN_SERVER_URI.toString(), payload.getAudience()); 258 assertEquals(currentTimeMillis / 1000, (long) payload.getIssuedAtTimeSeconds()); 259 assertEquals(currentTimeMillis / 1000 + 3600, (long) payload.getExpirationTimeSeconds()); 260 assertEquals(USER, payload.getSubject()); 261 assertEquals(Joiner.on(' ').join(scopes), payload.get("scope")); 262 } 263 264 @Test createAssertion_defaultScopes_correct()265 public void createAssertion_defaultScopes_correct() throws IOException { 266 List<String> defaultScopes = Arrays.asList("scope1", "scope2"); 267 ServiceAccountCredentials.Builder builder = createDefaultBuilder(); 268 builder.setScopes(null, defaultScopes).setServiceAccountUser(USER); 269 270 assertEquals(2, builder.getDefaultScopes().size()); 271 ServiceAccountCredentials credentials = builder.build(); 272 273 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 274 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 275 String assertion = credentials.createAssertion(jsonFactory, currentTimeMillis); 276 277 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 278 JsonWebToken.Payload payload = signature.getPayload(); 279 assertEquals(CLIENT_EMAIL, payload.getIssuer()); 280 assertEquals(OAuth2Utils.TOKEN_SERVER_URI.toString(), payload.getAudience()); 281 assertEquals(currentTimeMillis / 1000, (long) payload.getIssuedAtTimeSeconds()); 282 assertEquals(currentTimeMillis / 1000 + 3600, (long) payload.getExpirationTimeSeconds()); 283 assertEquals(USER, payload.getSubject()); 284 assertEquals(Joiner.on(' ').join(defaultScopes), payload.get("scope")); 285 } 286 287 @Test createAssertion_custom_lifetime()288 public void createAssertion_custom_lifetime() throws IOException { 289 ServiceAccountCredentials credentials = createDefaultBuilder().setLifetime(4000).build(); 290 291 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 292 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 293 String assertion = credentials.createAssertion(jsonFactory, currentTimeMillis); 294 295 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 296 JsonWebToken.Payload payload = signature.getPayload(); 297 assertEquals(currentTimeMillis / 1000 + 4000, (long) payload.getExpirationTimeSeconds()); 298 } 299 300 @Test createAssertionForIdToken_correct()301 public void createAssertionForIdToken_correct() throws IOException { 302 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 303 ServiceAccountCredentials credentials = 304 createDefaultBuilder() 305 .setPrivateKeyId(PRIVATE_KEY_ID) 306 .setServiceAccountUser(USER) 307 .setProjectId(PROJECT_ID) 308 .build(); 309 310 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 311 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 312 String assertion = 313 credentials.createAssertionForIdToken( 314 jsonFactory, currentTimeMillis, null, "https://foo.com/bar"); 315 316 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 317 JsonWebToken.Payload payload = signature.getPayload(); 318 assertEquals(CLIENT_EMAIL, payload.getIssuer()); 319 assertEquals("https://foo.com/bar", (String) (payload.getUnknownKeys().get("target_audience"))); 320 assertEquals(currentTimeMillis / 1000, (long) payload.getIssuedAtTimeSeconds()); 321 assertEquals(currentTimeMillis / 1000 + 3600, (long) payload.getExpirationTimeSeconds()); 322 assertEquals(USER, payload.getSubject()); 323 } 324 325 @Test createAssertionForIdToken_custom_lifetime()326 public void createAssertionForIdToken_custom_lifetime() throws IOException { 327 ServiceAccountCredentials credentials = createDefaultBuilder().setLifetime(4000).build(); 328 329 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 330 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 331 String assertion = 332 credentials.createAssertionForIdToken( 333 jsonFactory, currentTimeMillis, null, "https://foo.com/bar"); 334 335 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 336 JsonWebToken.Payload payload = signature.getPayload(); 337 assertEquals(currentTimeMillis / 1000 + 4000, (long) payload.getExpirationTimeSeconds()); 338 } 339 340 @Test createAssertionForIdToken_incorrect()341 public void createAssertionForIdToken_incorrect() throws IOException { 342 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 343 ServiceAccountCredentials credentials = 344 ServiceAccountCredentials.newBuilder() 345 .setClientId(CLIENT_ID) 346 .setClientEmail(CLIENT_EMAIL) 347 .setPrivateKey(privateKey) 348 .setPrivateKeyId(PRIVATE_KEY_ID) 349 .setServiceAccountUser(USER) 350 .setProjectId(PROJECT_ID) 351 .build(); 352 353 JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY; 354 long currentTimeMillis = Clock.SYSTEM.currentTimeMillis(); 355 String assertion = 356 credentials.createAssertionForIdToken( 357 jsonFactory, currentTimeMillis, null, "https://foo.com/bar"); 358 359 JsonWebSignature signature = JsonWebSignature.parse(jsonFactory, assertion); 360 JsonWebToken.Payload payload = signature.getPayload(); 361 assertEquals(CLIENT_EMAIL, payload.getIssuer()); 362 assertNotEquals( 363 "https://bar.com/foo", (String) (payload.getUnknownKeys().get("target_audience"))); 364 assertEquals(currentTimeMillis / 1000, (long) payload.getIssuedAtTimeSeconds()); 365 assertEquals(currentTimeMillis / 1000 + 3600, (long) payload.getExpirationTimeSeconds()); 366 assertEquals(USER, payload.getSubject()); 367 } 368 369 @Test createdScoped_withAud_noUniverse_jwtWithScopesDisabled_accessToken()370 public void createdScoped_withAud_noUniverse_jwtWithScopesDisabled_accessToken() 371 throws IOException { 372 GoogleCredentials credentials = createDefaultBuilderWithToken(ACCESS_TOKEN).build(); 373 374 // No aud, no scopes gives an exception. 375 try { 376 credentials.getRequestMetadata(null); 377 fail("Should not be able to get token without scopes"); 378 } catch (IOException e) { 379 assertTrue( 380 "expected to fail with exception", 381 e.getMessage().contains("Scopes and uri are not configured for service account")); 382 } 383 384 GoogleCredentials scopedCredentials = credentials.createScoped(SCOPES); 385 assertEquals(false, credentials.isExplicitUniverseDomain()); 386 assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); 387 Map<String, List<String>> metadata = scopedCredentials.getRequestMetadata(CALL_URI); 388 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 389 } 390 391 @Test createdScoped_withUniverse_selfSignedJwt()392 public void createdScoped_withUniverse_selfSignedJwt() throws IOException { 393 ServiceAccountCredentials credentials = 394 createDefaultBuilder().setUniverseDomain("foo.bar").build(); 395 396 try { 397 credentials.getRequestMetadata(null); 398 fail("Should not be able to get token without scopes"); 399 } catch (IOException e) { 400 assertTrue( 401 "expected to fail with exception", 402 e.getMessage().contains("Scopes and uri are not configured for service account")); 403 } 404 405 GoogleCredentials scopedCredentials = credentials.createScoped("dummy.scope"); 406 Map<String, List<String>> metadata = scopedCredentials.getRequestMetadata(null); 407 verifyJwtAccess(metadata, "dummy.scope"); 408 409 // Recreate to avoid jwt caching. 410 scopedCredentials = credentials.createScoped("dummy.scope2"); 411 assertEquals(true, scopedCredentials.isExplicitUniverseDomain()); 412 assertEquals("foo.bar", scopedCredentials.getUniverseDomain()); 413 metadata = scopedCredentials.getRequestMetadata(CALL_URI); 414 verifyJwtAccess(metadata, "dummy.scope2"); 415 416 // Recreate to avoid jwt caching. 417 scopedCredentials = 418 credentials.createScoped( 419 Collections.<String>emptyList(), Arrays.asList("dummy.default.scope")); 420 metadata = scopedCredentials.getRequestMetadata(null); 421 verifyJwtAccess(metadata, "dummy.default.scope"); 422 423 // Recreate to avoid jwt caching. 424 scopedCredentials = 425 credentials.createScoped( 426 Collections.<String>emptyList(), Arrays.asList("dummy.default.scope2")); 427 metadata = scopedCredentials.getRequestMetadata(CALL_URI); 428 verifyJwtAccess(metadata, "dummy.default.scope2"); 429 } 430 431 @Test noScopes_withUniverse_selfSignedJwt()432 public void noScopes_withUniverse_selfSignedJwt() throws IOException { 433 GoogleCredentials credentials = createDefaultBuilder().setUniverseDomain("foo.bar").build(); 434 435 try { 436 credentials.getRequestMetadata(null); 437 fail("Should not be able to get token without scopes"); 438 } catch (IOException e) { 439 assertTrue( 440 "expected to fail with exception", 441 e.getMessage().contains("Scopes and uri are not configured for service account")); 442 } 443 444 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 445 assertNull(((ServiceAccountCredentials) credentials).getSelfSignedJwtCredentialsWithScope()); 446 verifyJwtAccess(metadata, null); 447 } 448 449 @Test createdScoped_defaultScopes()450 public void createdScoped_defaultScopes() throws IOException { 451 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 452 453 ServiceAccountCredentials credentials = 454 ServiceAccountCredentials.fromPkcs8( 455 CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID, SCOPES, DEFAULT_SCOPES); 456 assertEquals(1, credentials.getDefaultScopes().size()); 457 assertEquals("dummy.default.scope", credentials.getDefaultScopes().toArray()[0]); 458 459 credentials = 460 ServiceAccountCredentials.fromPkcs8( 461 CLIENT_ID, 462 CLIENT_EMAIL, 463 PRIVATE_KEY_PKCS8, 464 PRIVATE_KEY_ID, 465 SCOPES, 466 DEFAULT_SCOPES, 467 transportFactory, 468 null); 469 assertEquals(1, credentials.getDefaultScopes().size()); 470 assertEquals("dummy.default.scope", credentials.getDefaultScopes().toArray()[0]); 471 472 credentials = 473 ServiceAccountCredentials.fromPkcs8( 474 CLIENT_ID, 475 CLIENT_EMAIL, 476 PRIVATE_KEY_PKCS8, 477 PRIVATE_KEY_ID, 478 SCOPES, 479 DEFAULT_SCOPES, 480 transportFactory, 481 null, 482 "service_account_user"); 483 assertEquals(1, credentials.getDefaultScopes().size()); 484 assertEquals("dummy.default.scope", credentials.getDefaultScopes().toArray()[0]); 485 } 486 487 @Test createScopedRequired_emptyScopes()488 public void createScopedRequired_emptyScopes() throws IOException { 489 GoogleCredentials credentials = 490 ServiceAccountCredentials.fromPkcs8( 491 CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID, EMPTY_SCOPES); 492 493 assertTrue(credentials.createScopedRequired()); 494 } 495 496 @Test createScopedRequired_nonEmptyScopes()497 public void createScopedRequired_nonEmptyScopes() throws IOException { 498 GoogleCredentials credentials = 499 ServiceAccountCredentials.fromPkcs8( 500 CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID, SCOPES); 501 502 assertFalse(credentials.createScopedRequired()); 503 } 504 505 @Test createScopedRequired_nonEmptyDefaultScopes()506 public void createScopedRequired_nonEmptyDefaultScopes() throws IOException { 507 GoogleCredentials credentials = 508 ServiceAccountCredentials.fromPkcs8( 509 CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID, null, SCOPES); 510 511 assertFalse(credentials.createScopedRequired()); 512 } 513 514 @Test fromJSON_getProjectId()515 public void fromJSON_getProjectId() throws IOException { 516 GenericJson json = writeServiceAccountJson(PROJECT_ID, null, null); 517 518 ServiceAccountCredentials credentials = 519 ServiceAccountCredentials.fromJson(json, new MockTokenServerTransportFactory()); 520 assertEquals(PROJECT_ID, credentials.getProjectId()); 521 assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); 522 } 523 524 @Test fromJSON_Universe_getUniverseDomain()525 public void fromJSON_Universe_getUniverseDomain() throws IOException { 526 GenericJson json = writeServiceAccountJson(PROJECT_ID, null, "foo.bar"); 527 528 ServiceAccountCredentials credentials = 529 ServiceAccountCredentials.fromJson(json, new MockTokenServerTransportFactory()); 530 assertEquals("foo.bar", credentials.getUniverseDomain()); 531 } 532 533 @Test fromJSON_getProjectIdNull()534 public void fromJSON_getProjectIdNull() throws IOException { 535 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 536 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 537 GenericJson json = writeServiceAccountJson(null, null, null); 538 539 ServiceAccountCredentials credentials = 540 ServiceAccountCredentials.fromJson(json, transportFactory); 541 assertNull(credentials.getProjectId()); 542 } 543 544 @Test fromJSON_hasAccessToken()545 public void fromJSON_hasAccessToken() throws IOException { 546 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 547 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 548 GenericJson json = writeServiceAccountJson(PROJECT_ID, null, null); 549 550 GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); 551 552 credentials = credentials.createScoped(SCOPES); 553 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 554 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 555 } 556 557 @Test fromJSON_withUniverse_selfSignedJwt()558 public void fromJSON_withUniverse_selfSignedJwt() throws IOException { 559 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 560 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 561 GenericJson json = writeServiceAccountJson(PROJECT_ID, null, "foo.bar"); 562 563 GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); 564 565 credentials = credentials.createScoped(SCOPES); 566 Map<String, List<String>> metadata = credentials.getRequestMetadata(null); 567 verifyJwtAccess(metadata, "dummy.scope"); 568 } 569 570 @Test fromJSON_tokenServerUri()571 public void fromJSON_tokenServerUri() throws IOException { 572 final String tokenServerUri = "https://foo.com/bar"; 573 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 574 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 575 GenericJson json = writeServiceAccountJson(PROJECT_ID, null, null); 576 json.put("token_uri", tokenServerUri); 577 ServiceAccountCredentials credentials = 578 ServiceAccountCredentials.fromJson(json, transportFactory); 579 assertEquals(URI.create(tokenServerUri), credentials.getTokenServerUri()); 580 } 581 582 @Test fromJson_hasQuotaProjectId()583 public void fromJson_hasQuotaProjectId() throws IOException { 584 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 585 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 586 GenericJson json = writeServiceAccountJson(PROJECT_ID, QUOTA_PROJECT, null); 587 GoogleCredentials credentials = ServiceAccountCredentials.fromJson(json, transportFactory); 588 credentials = credentials.createScoped(SCOPES); 589 590 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 591 592 assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); 593 assertEquals( 594 metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), 595 Collections.singletonList(QUOTA_PROJECT)); 596 } 597 598 @Test getRequestMetadata_hasAccessToken()599 public void getRequestMetadata_hasAccessToken() throws IOException { 600 GoogleCredentials credentials = 601 createDefaultBuilderWithToken(ACCESS_TOKEN).setScopes(SCOPES).build(); 602 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 603 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 604 } 605 606 @Test getRequestMetadata_customTokenServer_hasAccessToken()607 public void getRequestMetadata_customTokenServer_hasAccessToken() throws IOException { 608 final URI TOKEN_SERVER = URI.create("https://foo.com/bar"); 609 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 610 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 611 transportFactory.transport.setTokenServerUri(TOKEN_SERVER); 612 OAuth2Credentials credentials = 613 createDefaultBuilder() 614 .setScopes(SCOPES) 615 .setHttpTransportFactory(transportFactory) 616 .setTokenServerUri(TOKEN_SERVER) 617 .build(); 618 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 619 620 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 621 } 622 623 @Test getUniverseDomain_defaultUniverse()624 public void getUniverseDomain_defaultUniverse() throws IOException { 625 ServiceAccountCredentials credentials = createDefaultBuilder().build(); 626 assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); 627 } 628 629 @Test refreshAccessToken_refreshesToken()630 public void refreshAccessToken_refreshesToken() throws IOException { 631 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 632 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 633 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 634 MockTokenServerTransport transport = transportFactory.transport; 635 ServiceAccountCredentials credentials = 636 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 637 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 638 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 639 640 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 641 credentials.refresh(); 642 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken2); 643 } 644 645 @Test refreshAccessToken_tokenExpiry()646 public void refreshAccessToken_tokenExpiry() throws IOException { 647 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 648 MockTokenServerTransport transport = transportFactory.transport; 649 transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 650 ServiceAccountCredentials credentials = 651 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 652 credentials.clock = new FixedClock(0L); 653 654 AccessToken accessToken = credentials.refreshAccessToken(); 655 assertEquals(ACCESS_TOKEN, accessToken.getTokenValue()); 656 assertEquals(3600 * 1000L, accessToken.getExpirationTimeMillis().longValue()); 657 658 // Test for large expires_in values (should not overflow). 659 transport.setExpiresInSeconds(3600 * 1000); 660 accessToken = credentials.refreshAccessToken(); 661 assertEquals(ACCESS_TOKEN, accessToken.getTokenValue()); 662 assertEquals(3600 * 1000 * 1000L, accessToken.getExpirationTimeMillis().longValue()); 663 } 664 665 @Test refreshAccessToken_IOException_Retry()666 public void refreshAccessToken_IOException_Retry() throws IOException { 667 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 668 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 669 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 670 MockTokenServerTransport transport = transportFactory.transport; 671 ServiceAccountCredentials credentials = 672 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 673 ; 674 675 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 676 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 677 678 transport.addResponseErrorSequence(new IOException()); 679 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 680 credentials.refresh(); 681 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken2); 682 } 683 684 @Test refreshAccessToken_retriesServerErrors()685 public void refreshAccessToken_retriesServerErrors() throws IOException { 686 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 687 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 688 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 689 MockTokenServerTransport transport = transportFactory.transport; 690 ServiceAccountCredentials credentials = 691 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 692 693 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 694 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 695 696 MockLowLevelHttpResponse response500 = new MockLowLevelHttpResponse().setStatusCode(500); 697 MockLowLevelHttpResponse response503 = new MockLowLevelHttpResponse().setStatusCode(503); 698 transport.addResponseSequence(response500, response503); 699 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 700 credentials.refresh(); 701 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken2); 702 } 703 704 @Test refreshAccessToken_retriesTimeoutAndThrottled()705 public void refreshAccessToken_retriesTimeoutAndThrottled() throws IOException { 706 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 707 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 708 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 709 MockTokenServerTransport transport = transportFactory.transport; 710 ServiceAccountCredentials credentials = 711 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 712 713 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 714 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 715 716 MockLowLevelHttpResponse response408 = new MockLowLevelHttpResponse().setStatusCode(408); 717 MockLowLevelHttpResponse response429 = new MockLowLevelHttpResponse().setStatusCode(429); 718 transport.addResponseSequence(response408, response429); 719 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 720 credentials.refresh(); 721 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken2); 722 } 723 724 @Test refreshAccessToken_defaultRetriesDisabled()725 public void refreshAccessToken_defaultRetriesDisabled() throws IOException { 726 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 727 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 728 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 729 MockTokenServerTransport transport = transportFactory.transport; 730 ServiceAccountCredentials credentials = 731 createDefaultBuilder() 732 .setScopes(SCOPES) 733 .setHttpTransportFactory(transportFactory) 734 .build() 735 .createWithCustomRetryStrategy(false); 736 737 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 738 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 739 740 MockLowLevelHttpResponse response408 = new MockLowLevelHttpResponse().setStatusCode(408); 741 MockLowLevelHttpResponse response429 = new MockLowLevelHttpResponse().setStatusCode(429); 742 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 743 744 try { 745 transport.addResponseSequence(response408, response429); 746 credentials.refresh(); 747 fail("Should not be able to use credential without exception."); 748 } catch (GoogleAuthException ex) { 749 assertTrue(ex.getMessage().contains("Error getting access token for service account: 408")); 750 assertTrue(ex.isRetryable()); 751 assertEquals(0, ex.getRetryCount()); 752 } 753 } 754 755 @Test refreshAccessToken_maxRetries_maxDelay()756 public void refreshAccessToken_maxRetries_maxDelay() throws IOException { 757 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 758 MockTokenServerTransport transport = transportFactory.transport; 759 ServiceAccountCredentials credentials = 760 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 761 762 transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 763 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), ACCESS_TOKEN); 764 765 MockLowLevelHttpResponse response408 = new MockLowLevelHttpResponse().setStatusCode(408); 766 MockLowLevelHttpResponse response429 = new MockLowLevelHttpResponse().setStatusCode(429); 767 MockLowLevelHttpResponse response500 = new MockLowLevelHttpResponse().setStatusCode(500); 768 MockLowLevelHttpResponse response503 = new MockLowLevelHttpResponse().setStatusCode(503); 769 770 Instant start = Instant.now(); 771 try { 772 transport.addResponseSequence(response408, response429, response500, response503); 773 credentials.refresh(); 774 fail("Should not be able to use credential without exception."); 775 } catch (GoogleAuthException ex) { 776 Instant finish = Instant.now(); 777 long timeElapsed = Duration.between(start, finish).toMillis(); 778 779 // we expect max retry time of 7 sec +/- jitter 780 assertTrue(timeElapsed > 5500 && timeElapsed < 10000); 781 assertTrue(ex.getMessage().contains("Error getting access token for service account: 503")); 782 assertTrue(ex.isRetryable()); 783 assertEquals(3, ex.getRetryCount()); 784 assertTrue(ex.getCause() instanceof HttpResponseException); 785 } 786 } 787 788 @Test refreshAccessToken_RequestFailure_retried()789 public void refreshAccessToken_RequestFailure_retried() throws IOException { 790 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 791 MockTokenServerTransport transport = transportFactory.transport; 792 ServiceAccountCredentials credentials = 793 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 794 795 transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 796 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), ACCESS_TOKEN); 797 798 IOException error = new IOException("Invalid grant: Account not found"); 799 MockLowLevelHttpResponse response503 = new MockLowLevelHttpResponse().setStatusCode(503); 800 801 Instant start = Instant.now(); 802 try { 803 transport.addResponseSequence(response503); 804 transport.addResponseErrorSequence(error, error, error); 805 credentials.refresh(); 806 fail("Should not be able to use credential without exception."); 807 } catch (GoogleAuthException ex) { 808 Instant finish = Instant.now(); 809 long timeElapsed = Duration.between(start, finish).toMillis(); 810 811 // we expect max retry time of 7 sec +/- jitter 812 assertTrue(timeElapsed > 5500 && timeElapsed < 10000); 813 assertTrue( 814 ex.getMessage() 815 .contains("Error getting access token for service account: Invalid grant")); 816 assertTrue(ex.isRetryable()); 817 assertEquals(3, ex.getRetryCount()); 818 assertTrue(ex.getCause() instanceof IOException); 819 } 820 } 821 822 @Test refreshAccessToken_4xx_5xx_NonRetryableFails()823 public void refreshAccessToken_4xx_5xx_NonRetryableFails() throws IOException { 824 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 825 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 826 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 827 MockTokenServerTransport transport = transportFactory.transport; 828 ServiceAccountCredentials credentials = 829 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 830 831 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 832 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 833 834 for (int status = 400; status < 600; status++) { 835 if (OAuth2Utils.TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES.contains(status)) { 836 continue; 837 } 838 839 MockLowLevelHttpResponse mockResponse = new MockLowLevelHttpResponse().setStatusCode(status); 840 try { 841 transport.addResponseSequence(mockResponse); 842 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 843 credentials.refresh(); 844 fail("Should not be able to use credential without exception."); 845 } catch (GoogleAuthException ex) { 846 assertFalse(ex.isRetryable()); 847 assertEquals(0, ex.getRetryCount()); 848 } 849 } 850 } 851 852 @Test idTokenWithAudience_correct()853 public void idTokenWithAudience_correct() throws IOException { 854 String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 855 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 856 MockTokenServerTransport transport = transportFactory.transport; 857 ServiceAccountCredentials credentials = 858 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 859 860 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 861 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 862 863 String targetAudience = "https://foo.bar"; 864 IdTokenCredentials tokenCredential = 865 IdTokenCredentials.newBuilder() 866 .setIdTokenProvider(credentials) 867 .setTargetAudience(targetAudience) 868 .build(); 869 tokenCredential.refresh(); 870 assertEquals(DEFAULT_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); 871 assertEquals(DEFAULT_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); 872 assertEquals( 873 targetAudience, 874 (String) tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudience()); 875 } 876 877 @Test idTokenWithAudience_incorrect()878 public void idTokenWithAudience_incorrect() throws IOException { 879 String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 880 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 881 MockTokenServerTransport transport = transportFactory.transport; 882 ServiceAccountCredentials credentials = 883 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 884 885 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 886 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken1); 887 888 String targetAudience = "https://bar"; 889 IdTokenCredentials tokenCredential = 890 IdTokenCredentials.newBuilder() 891 .setIdTokenProvider(credentials) 892 .setTargetAudience(targetAudience) 893 .build(); 894 tokenCredential.refresh(); 895 assertNotEquals( 896 targetAudience, 897 (String) tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudience()); 898 } 899 900 @Test getScopes_nullReturnsEmpty()901 public void getScopes_nullReturnsEmpty() throws IOException { 902 ServiceAccountCredentials credentials = createDefaultBuilder().build(); 903 Collection<String> scopes = credentials.getScopes(); 904 905 assertNotNull(scopes); 906 assertTrue(scopes.isEmpty()); 907 } 908 909 @Test getAccount_sameAs()910 public void getAccount_sameAs() throws IOException { 911 ServiceAccountCredentials credentials = createDefaultBuilder().build(); 912 assertEquals(CLIENT_EMAIL, credentials.getAccount()); 913 } 914 915 @Test sign_sameAs()916 public void sign_sameAs() 917 throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { 918 byte[] toSign = {0xD, 0xE, 0xA, 0xD}; 919 ServiceAccountCredentials credentials = createDefaultBuilder().build(); 920 921 byte[] signedBytes = credentials.sign(toSign); 922 Signature signature = Signature.getInstance(OAuth2Utils.SIGNATURE_ALGORITHM); 923 signature.initSign(credentials.getPrivateKey()); 924 signature.update(toSign); 925 926 assertArrayEquals(signature.sign(), signedBytes); 927 } 928 929 @Test equals_true()930 public void equals_true() throws IOException { 931 final URI tokenServer = URI.create("https://foo.com/bar"); 932 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 933 OAuth2Credentials credentials = 934 ServiceAccountCredentials.fromPkcs8( 935 CLIENT_ID, 936 CLIENT_EMAIL, 937 PRIVATE_KEY_PKCS8, 938 PRIVATE_KEY_ID, 939 SCOPES, 940 transportFactory, 941 tokenServer); 942 OAuth2Credentials otherCredentials = 943 ServiceAccountCredentials.fromPkcs8( 944 CLIENT_ID, 945 CLIENT_EMAIL, 946 PRIVATE_KEY_PKCS8, 947 PRIVATE_KEY_ID, 948 SCOPES, 949 transportFactory, 950 tokenServer); 951 assertTrue(credentials.equals(otherCredentials)); 952 assertTrue(otherCredentials.equals(credentials)); 953 } 954 955 @Test equals_false_clientId()956 public void equals_false_clientId() throws IOException { 957 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 958 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 959 OAuth2Credentials credentials = 960 ServiceAccountCredentials.fromPkcs8( 961 CLIENT_ID, 962 CLIENT_EMAIL, 963 PRIVATE_KEY_PKCS8, 964 PRIVATE_KEY_ID, 965 SCOPES, 966 serverTransportFactory, 967 tokenServer1); 968 OAuth2Credentials otherCredentials = 969 ServiceAccountCredentials.fromPkcs8( 970 "otherClientId", 971 CLIENT_EMAIL, 972 PRIVATE_KEY_PKCS8, 973 PRIVATE_KEY_ID, 974 SCOPES, 975 serverTransportFactory, 976 tokenServer1); 977 assertFalse(credentials.equals(otherCredentials)); 978 assertFalse(otherCredentials.equals(credentials)); 979 } 980 981 @Test equals_false_email()982 public void equals_false_email() throws IOException { 983 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 984 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 985 OAuth2Credentials credentials = 986 ServiceAccountCredentials.fromPkcs8( 987 CLIENT_ID, 988 CLIENT_EMAIL, 989 PRIVATE_KEY_PKCS8, 990 PRIVATE_KEY_ID, 991 SCOPES, 992 serverTransportFactory, 993 tokenServer1); 994 OAuth2Credentials otherCredentials = 995 ServiceAccountCredentials.fromPkcs8( 996 CLIENT_ID, 997 "otherEmail", 998 PRIVATE_KEY_PKCS8, 999 PRIVATE_KEY_ID, 1000 SCOPES, 1001 serverTransportFactory, 1002 tokenServer1); 1003 assertFalse(credentials.equals(otherCredentials)); 1004 assertFalse(otherCredentials.equals(credentials)); 1005 } 1006 1007 @Test equals_false_super()1008 public void equals_false_super() throws IOException { 1009 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 1010 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 1011 OAuth2Credentials credentials = 1012 ServiceAccountCredentials.fromPkcs8( 1013 CLIENT_ID, 1014 CLIENT_EMAIL, 1015 PRIVATE_KEY_PKCS8, 1016 PRIVATE_KEY_ID, 1017 SCOPES, 1018 serverTransportFactory, 1019 tokenServer1); 1020 OAuth2Credentials otherCredentials = 1021 ServiceAccountCredentials.fromPkcs8( 1022 CLIENT_ID, 1023 CLIENT_EMAIL, 1024 PRIVATE_KEY_PKCS8, 1025 PRIVATE_KEY_ID, 1026 SCOPES, 1027 serverTransportFactory, 1028 tokenServer1) 1029 .toBuilder() 1030 .setUniverseDomain("universe.com") 1031 .build(); 1032 assertFalse(credentials.equals(otherCredentials)); 1033 assertFalse(otherCredentials.equals(credentials)); 1034 } 1035 1036 @Test equals_false_keyId()1037 public void equals_false_keyId() throws IOException { 1038 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 1039 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 1040 OAuth2Credentials credentials = 1041 ServiceAccountCredentials.fromPkcs8( 1042 CLIENT_ID, 1043 CLIENT_EMAIL, 1044 PRIVATE_KEY_PKCS8, 1045 PRIVATE_KEY_ID, 1046 SCOPES, 1047 serverTransportFactory, 1048 tokenServer1); 1049 OAuth2Credentials otherCredentials = 1050 ServiceAccountCredentials.fromPkcs8( 1051 CLIENT_ID, 1052 CLIENT_EMAIL, 1053 PRIVATE_KEY_PKCS8, 1054 "otherId", 1055 SCOPES, 1056 serverTransportFactory, 1057 tokenServer1); 1058 assertFalse(credentials.equals(otherCredentials)); 1059 assertFalse(otherCredentials.equals(credentials)); 1060 } 1061 1062 @Test equals_false_scopes()1063 public void equals_false_scopes() throws IOException { 1064 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 1065 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 1066 OAuth2Credentials credentials = 1067 ServiceAccountCredentials.fromPkcs8( 1068 CLIENT_ID, 1069 CLIENT_EMAIL, 1070 PRIVATE_KEY_PKCS8, 1071 PRIVATE_KEY_ID, 1072 SCOPES, 1073 serverTransportFactory, 1074 tokenServer1); 1075 OAuth2Credentials otherCredentials = 1076 ServiceAccountCredentials.fromPkcs8( 1077 CLIENT_ID, 1078 CLIENT_EMAIL, 1079 PRIVATE_KEY_PKCS8, 1080 PRIVATE_KEY_ID, 1081 ImmutableSet.<String>of(), 1082 serverTransportFactory, 1083 tokenServer1); 1084 assertFalse(credentials.equals(otherCredentials)); 1085 assertFalse(otherCredentials.equals(credentials)); 1086 } 1087 1088 @Test equals_false_transportFactory()1089 public void equals_false_transportFactory() throws IOException { 1090 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 1091 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 1092 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 1093 OAuth2Credentials credentials = 1094 ServiceAccountCredentials.fromPkcs8( 1095 CLIENT_ID, 1096 CLIENT_EMAIL, 1097 PRIVATE_KEY_PKCS8, 1098 PRIVATE_KEY_ID, 1099 SCOPES, 1100 serverTransportFactory, 1101 tokenServer1); 1102 OAuth2Credentials otherCredentials = 1103 ServiceAccountCredentials.fromPkcs8( 1104 CLIENT_ID, 1105 CLIENT_EMAIL, 1106 PRIVATE_KEY_PKCS8, 1107 PRIVATE_KEY_ID, 1108 SCOPES, 1109 httpTransportFactory, 1110 tokenServer1); 1111 assertFalse(credentials.equals(otherCredentials)); 1112 assertFalse(otherCredentials.equals(credentials)); 1113 } 1114 1115 @Test equals_false_tokenServer()1116 public void equals_false_tokenServer() throws IOException { 1117 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 1118 final URI tokenServer2 = URI.create("https://foo2.com/bar"); 1119 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 1120 OAuth2Credentials credentials = 1121 ServiceAccountCredentials.fromPkcs8( 1122 CLIENT_ID, 1123 CLIENT_EMAIL, 1124 PRIVATE_KEY_PKCS8, 1125 PRIVATE_KEY_ID, 1126 SCOPES, 1127 serverTransportFactory, 1128 tokenServer1); 1129 OAuth2Credentials otherCredentials = 1130 ServiceAccountCredentials.fromPkcs8( 1131 CLIENT_ID, 1132 CLIENT_EMAIL, 1133 PRIVATE_KEY_PKCS8, 1134 PRIVATE_KEY_ID, 1135 SCOPES, 1136 serverTransportFactory, 1137 tokenServer2); 1138 assertFalse(credentials.equals(otherCredentials)); 1139 assertFalse(otherCredentials.equals(credentials)); 1140 } 1141 1142 @Test toString_containsFields()1143 public void toString_containsFields() throws IOException { 1144 final URI tokenServer = URI.create("https://foo.com/bar"); 1145 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1146 1147 ServiceAccountCredentials.Builder builder = 1148 ServiceAccountCredentials.newBuilder() 1149 .setClientId(CLIENT_ID) 1150 .setClientEmail(CLIENT_EMAIL) 1151 .setPrivateKeyId(PRIVATE_KEY_ID) 1152 .setScopes(SCOPES, DEFAULT_SCOPES) 1153 .setHttpTransportFactory(transportFactory) 1154 .setTokenServerUri(tokenServer) 1155 .setServiceAccountUser(USER) 1156 .setQuotaProjectId(QUOTA_PROJECT); 1157 1158 OAuth2Credentials credentials = ServiceAccountCredentials.fromPkcs8(PRIVATE_KEY_PKCS8, builder); 1159 String expectedToString = 1160 String.format( 1161 "ServiceAccountCredentials{quotaProjectId=%s, universeDomain=%s, isExplicitUniverseDomain=false, clientId=%s, clientEmail=%s, " 1162 + "privateKeyId=%s, transportFactoryClassName=%s, tokenServerUri=%s, scopes=%s, defaultScopes=%s, " 1163 + "serviceAccountUser=%s, lifetime=3600, useJwtAccessWithScope=false, defaultRetriesEnabled=true}", 1164 QUOTA_PROJECT, 1165 Credentials.GOOGLE_DEFAULT_UNIVERSE, 1166 CLIENT_ID, 1167 CLIENT_EMAIL, 1168 PRIVATE_KEY_ID, 1169 MockTokenServerTransportFactory.class.getName(), 1170 tokenServer, 1171 SCOPES, 1172 DEFAULT_SCOPES, 1173 USER); 1174 assertEquals(expectedToString, credentials.toString()); 1175 } 1176 1177 @Test hashCode_equals()1178 public void hashCode_equals() throws IOException { 1179 final URI tokenServer = URI.create("https://foo.com/bar"); 1180 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1181 OAuth2Credentials credentials = 1182 ServiceAccountCredentials.fromPkcs8( 1183 CLIENT_ID, 1184 CLIENT_EMAIL, 1185 PRIVATE_KEY_PKCS8, 1186 PRIVATE_KEY_ID, 1187 SCOPES, 1188 transportFactory, 1189 tokenServer) 1190 .createWithQuotaProject(QUOTA_PROJECT) 1191 .toBuilder() 1192 .setUniverseDomain("universe.com") 1193 .build(); 1194 OAuth2Credentials otherCredentials = 1195 ServiceAccountCredentials.fromPkcs8( 1196 CLIENT_ID, 1197 CLIENT_EMAIL, 1198 PRIVATE_KEY_PKCS8, 1199 PRIVATE_KEY_ID, 1200 SCOPES, 1201 transportFactory, 1202 tokenServer) 1203 .createWithQuotaProject(QUOTA_PROJECT) 1204 .toBuilder() 1205 .setUniverseDomain("universe.com") 1206 .build(); 1207 assertEquals(credentials.hashCode(), otherCredentials.hashCode()); 1208 } 1209 1210 @Test hashCode_not_equals_quota()1211 public void hashCode_not_equals_quota() throws IOException { 1212 final URI tokenServer = URI.create("https://foo.com/bar"); 1213 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1214 OAuth2Credentials credentials = 1215 ServiceAccountCredentials.fromPkcs8( 1216 CLIENT_ID, 1217 CLIENT_EMAIL, 1218 PRIVATE_KEY_PKCS8, 1219 PRIVATE_KEY_ID, 1220 SCOPES, 1221 transportFactory, 1222 tokenServer); 1223 OAuth2Credentials otherCredentials = 1224 ServiceAccountCredentials.fromPkcs8( 1225 CLIENT_ID, 1226 CLIENT_EMAIL, 1227 PRIVATE_KEY_PKCS8, 1228 PRIVATE_KEY_ID, 1229 SCOPES, 1230 transportFactory, 1231 tokenServer) 1232 .createWithQuotaProject("some_quota"); 1233 assertNotEquals(credentials.hashCode(), otherCredentials.hashCode()); 1234 } 1235 1236 @Test serialize()1237 public void serialize() throws IOException, ClassNotFoundException { 1238 final URI tokenServer = URI.create("https://foo.com/bar"); 1239 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1240 ServiceAccountCredentials credentials = 1241 ServiceAccountCredentials.fromPkcs8( 1242 CLIENT_ID, 1243 CLIENT_EMAIL, 1244 PRIVATE_KEY_PKCS8, 1245 PRIVATE_KEY_ID, 1246 SCOPES, 1247 transportFactory, 1248 tokenServer); 1249 1250 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 1251 try (ObjectOutputStream output = new ObjectOutputStream(bytes)) { 1252 output.writeObject(credentials); 1253 String s = output.toString(); 1254 } 1255 ServiceAccountCredentials deserializedCredentials = serializeAndDeserialize(credentials); 1256 assertEquals(credentials, deserializedCredentials); 1257 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 1258 assertEquals(credentials.toString(), deserializedCredentials.toString()); 1259 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 1260 assertEquals( 1261 MockTokenServerTransportFactory.class, 1262 deserializedCredentials.toBuilder().getHttpTransportFactory().getClass()); 1263 } 1264 1265 @Test fromStream_nullTransport_throws()1266 public void fromStream_nullTransport_throws() throws IOException { 1267 InputStream stream = new ByteArrayInputStream("foo".getBytes()); 1268 try { 1269 ServiceAccountCredentials.fromStream(stream, null); 1270 fail("Should throw if HttpTransportFactory is null"); 1271 } catch (NullPointerException expected) { 1272 // Expected 1273 } 1274 } 1275 1276 @Test fromStream_nullStream_throws()1277 public void fromStream_nullStream_throws() throws IOException { 1278 MockHttpTransportFactory transportFactory = new MockHttpTransportFactory(); 1279 try { 1280 ServiceAccountCredentials.fromStream(null, transportFactory); 1281 fail("Should throw if InputStream is null"); 1282 } catch (NullPointerException expected) { 1283 // Expected 1284 } 1285 } 1286 1287 @Test fromStream_providesToken()1288 public void fromStream_providesToken() throws IOException { 1289 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1290 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 1291 InputStream serviceAccountStream = 1292 writeServiceAccountStream(CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID); 1293 1294 GoogleCredentials credentials = 1295 ServiceAccountCredentials.fromStream(serviceAccountStream, transportFactory); 1296 1297 assertNotNull(credentials); 1298 credentials = credentials.createScoped(SCOPES); 1299 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1300 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 1301 } 1302 1303 @Test fromStream_noClientId_throws()1304 public void fromStream_noClientId_throws() throws IOException { 1305 InputStream serviceAccountStream = 1306 writeServiceAccountStream(null, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID); 1307 1308 testFromStreamException(serviceAccountStream, "client_id"); 1309 } 1310 1311 @Test fromStream_noClientEmail_throws()1312 public void fromStream_noClientEmail_throws() throws IOException { 1313 InputStream serviceAccountStream = 1314 writeServiceAccountStream(CLIENT_ID, null, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID); 1315 1316 testFromStreamException(serviceAccountStream, "client_email"); 1317 } 1318 1319 @Test getIdTokenWithAudience_badEmailError_issClaimTraced()1320 public void getIdTokenWithAudience_badEmailError_issClaimTraced() throws IOException { 1321 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1322 MockTokenServerTransport transport = transportFactory.transport; 1323 transport.setError(new IOException("Invalid grant: Account not found")); 1324 ServiceAccountCredentials credentials = 1325 createDefaultBuilder().setScopes(SCOPES).setHttpTransportFactory(transportFactory).build(); 1326 1327 String targetAudience = "https://bar"; 1328 IdTokenCredentials tokenCredential = 1329 IdTokenCredentials.newBuilder() 1330 .setIdTokenProvider(credentials) 1331 .setTargetAudience(targetAudience) 1332 .build(); 1333 1334 String expectedErrorMessage = String.format("iss: %s", CLIENT_EMAIL); 1335 1336 try { 1337 tokenCredential.refresh(); 1338 fail("Should not be able to use credential without exception."); 1339 } catch (IOException expected) { 1340 assertTrue(expected.getMessage().contains(expectedErrorMessage)); 1341 } 1342 } 1343 1344 @Test fromStream_noPrivateKey_throws()1345 public void fromStream_noPrivateKey_throws() throws IOException { 1346 InputStream serviceAccountStream = 1347 writeServiceAccountStream(CLIENT_ID, CLIENT_EMAIL, null, PRIVATE_KEY_ID); 1348 1349 testFromStreamException(serviceAccountStream, "private_key"); 1350 } 1351 1352 @Test fromStream_noPrivateKeyId_throws()1353 public void fromStream_noPrivateKeyId_throws() throws IOException { 1354 InputStream serviceAccountStream = 1355 writeServiceAccountStream(CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, null); 1356 1357 testFromStreamException(serviceAccountStream, "private_key_id"); 1358 } 1359 1360 @Test getUriForSelfSignedJWT()1361 public void getUriForSelfSignedJWT() { 1362 assertNull(ServiceAccountCredentials.getUriForSelfSignedJWT(null)); 1363 1364 URI uri = URI.create("https://compute.googleapis.com/compute/v1/projects/"); 1365 URI expected = URI.create("https://compute.googleapis.com/"); 1366 assertEquals(expected, ServiceAccountCredentials.getUriForSelfSignedJWT(uri)); 1367 } 1368 1369 @Test getUriForSelfSignedJWT_noHost()1370 public void getUriForSelfSignedJWT_noHost() { 1371 URI uri = URI.create("file:foo"); 1372 URI expected = URI.create("file:foo"); 1373 assertEquals(expected, ServiceAccountCredentials.getUriForSelfSignedJWT(uri)); 1374 } 1375 1376 @Test getUriForSelfSignedJWT_forStaticAudience_returnsURI()1377 public void getUriForSelfSignedJWT_forStaticAudience_returnsURI() { 1378 URI uri = URI.create("compute.googleapis.com"); 1379 URI expected = URI.create("compute.googleapis.com"); 1380 assertEquals(expected, ServiceAccountCredentials.getUriForSelfSignedJWT(uri)); 1381 } 1382 1383 @Test getRequestMetadata_setsQuotaProjectId()1384 public void getRequestMetadata_setsQuotaProjectId() throws IOException { 1385 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1386 transportFactory.transport.addClient(CLIENT_ID, "unused-client-secret"); 1387 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 1388 1389 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1390 GoogleCredentials credentials = 1391 ServiceAccountCredentials.newBuilder() 1392 .setClientId(CLIENT_ID) 1393 .setClientEmail(CLIENT_EMAIL) 1394 .setPrivateKey(privateKey) 1395 .setPrivateKeyId(PRIVATE_KEY_ID) 1396 .setScopes(SCOPES) 1397 .setServiceAccountUser(USER) 1398 .setProjectId(PROJECT_ID) 1399 .setQuotaProjectId("my-quota-project-id") 1400 .setHttpTransportFactory(transportFactory) 1401 .build(); 1402 1403 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1404 assertTrue(metadata.containsKey("x-goog-user-project")); 1405 List<String> headerValues = metadata.get("x-goog-user-project"); 1406 assertEquals(1, headerValues.size()); 1407 assertEquals("my-quota-project-id", headerValues.get(0)); 1408 } 1409 1410 @Test getRequestMetadata_noQuotaProjectId()1411 public void getRequestMetadata_noQuotaProjectId() throws IOException { 1412 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1413 transportFactory.transport.addClient(CLIENT_ID, "unused-client-secret"); 1414 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 1415 1416 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1417 GoogleCredentials credentials = 1418 ServiceAccountCredentials.newBuilder() 1419 .setClientId(CLIENT_ID) 1420 .setClientEmail(CLIENT_EMAIL) 1421 .setPrivateKey(privateKey) 1422 .setPrivateKeyId(PRIVATE_KEY_ID) 1423 .setScopes(SCOPES) 1424 .setServiceAccountUser(USER) 1425 .setProjectId(PROJECT_ID) 1426 .setHttpTransportFactory(transportFactory) 1427 .build(); 1428 1429 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1430 assertFalse(metadata.containsKey("x-goog-user-project")); 1431 } 1432 1433 @Test getRequestMetadata_withCallback()1434 public void getRequestMetadata_withCallback() throws IOException { 1435 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1436 transportFactory.transport.addClient(CLIENT_ID, "unused-client-secret"); 1437 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 1438 1439 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1440 GoogleCredentials credentials = 1441 ServiceAccountCredentials.newBuilder() 1442 .setClientId(CLIENT_ID) 1443 .setClientEmail(CLIENT_EMAIL) 1444 .setPrivateKey(privateKey) 1445 .setPrivateKeyId(PRIVATE_KEY_ID) 1446 .setScopes(SCOPES) 1447 .setProjectId(PROJECT_ID) 1448 .setQuotaProjectId("my-quota-project-id") 1449 .setHttpTransportFactory(transportFactory) 1450 .build(); 1451 1452 final Map<String, List<String>> plainMetadata = credentials.getRequestMetadata(); 1453 final AtomicBoolean success = new AtomicBoolean(false); 1454 credentials.getRequestMetadata( 1455 null, 1456 null, 1457 new RequestMetadataCallback() { 1458 @Override 1459 public void onSuccess(Map<String, List<String>> metadata) { 1460 assertEquals(plainMetadata, metadata); 1461 success.set(true); 1462 } 1463 1464 @Override 1465 public void onFailure(Throwable exception) { 1466 fail("Should not throw a failure."); 1467 } 1468 }); 1469 1470 assertTrue("Should have run onSuccess() callback", success.get()); 1471 } 1472 1473 @Test getRequestMetadata_withScopes_withUniverseDomain_SelfSignedJwt()1474 public void getRequestMetadata_withScopes_withUniverseDomain_SelfSignedJwt() throws IOException { 1475 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1476 transportFactory.transport.addClient(CLIENT_ID, "unused-client-secret"); 1477 transportFactory.transport.addServiceAccount(CLIENT_EMAIL, ACCESS_TOKEN); 1478 1479 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1480 GoogleCredentials credentials = 1481 ServiceAccountCredentials.newBuilder() 1482 .setClientId(CLIENT_ID) 1483 .setClientEmail(CLIENT_EMAIL) 1484 .setPrivateKey(privateKey) 1485 .setPrivateKeyId(PRIVATE_KEY_ID) 1486 .setScopes(SCOPES) 1487 .setProjectId(PROJECT_ID) 1488 .setHttpTransportFactory(transportFactory) 1489 .setUniverseDomain("foo.bar") 1490 .build(); 1491 1492 final Map<String, List<String>> plainMetadata = credentials.getRequestMetadata(); 1493 final AtomicBoolean success = new AtomicBoolean(false); 1494 credentials.getRequestMetadata( 1495 null, 1496 null, 1497 new RequestMetadataCallback() { 1498 @Override 1499 public void onSuccess(Map<String, List<String>> metadata) { 1500 assertEquals(plainMetadata, metadata); 1501 success.set(true); 1502 } 1503 1504 @Override 1505 public void onFailure(Throwable exception) { 1506 fail("Should not throw a failure."); 1507 } 1508 }); 1509 1510 assertTrue("Should have run onSuccess() callback", success.get()); 1511 } 1512 1513 @Test getRequestMetadata_withScopes_selfSignedJWT()1514 public void getRequestMetadata_withScopes_selfSignedJWT() throws IOException { 1515 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1516 GoogleCredentials credentials = 1517 ServiceAccountCredentials.newBuilder() 1518 .setClientId(CLIENT_ID) 1519 .setClientEmail(CLIENT_EMAIL) 1520 .setPrivateKey(privateKey) 1521 .setPrivateKeyId(PRIVATE_KEY_ID) 1522 .setScopes(SCOPES) 1523 .setProjectId(PROJECT_ID) 1524 .setHttpTransportFactory(new MockTokenServerTransportFactory()) 1525 .setUseJwtAccessWithScope(true) 1526 .build(); 1527 1528 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1529 assertNotNull(((ServiceAccountCredentials) credentials).getSelfSignedJwtCredentialsWithScope()); 1530 verifyJwtAccess(metadata, "dummy.scope"); 1531 } 1532 1533 @Test refreshAccessToken_withDomainDelegation_selfSignedJWT_disabled()1534 public void refreshAccessToken_withDomainDelegation_selfSignedJWT_disabled() throws IOException { 1535 final String accessToken1 = "1/MkSJoj1xsli0AccessToken_NKPY2"; 1536 final String accessToken2 = "2/MkSJoj1xsli0AccessToken_NKPY2"; 1537 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 1538 MockTokenServerTransport transport = transportFactory.transport; 1539 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1540 GoogleCredentials credentials = 1541 ServiceAccountCredentials.newBuilder() 1542 .setClientId(CLIENT_ID) 1543 .setClientEmail(CLIENT_EMAIL) 1544 .setPrivateKey(privateKey) 1545 .setPrivateKeyId(PRIVATE_KEY_ID) 1546 .setScopes(SCOPES) 1547 .setServiceAccountUser(USER) 1548 .setProjectId(PROJECT_ID) 1549 .setHttpTransportFactory(transportFactory) 1550 .setUseJwtAccessWithScope(true) 1551 .build(); 1552 1553 transport.addServiceAccount(CLIENT_EMAIL, accessToken1); 1554 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1555 TestUtils.assertContainsBearerToken(metadata, accessToken1); 1556 1557 try { 1558 verifyJwtAccess(metadata, "dummy.scope"); 1559 fail("jwt access should fail with ServiceAccountUser"); 1560 } catch (Exception ex) { 1561 // expected 1562 } 1563 1564 transport.addServiceAccount(CLIENT_EMAIL, accessToken2); 1565 credentials.refresh(); 1566 TestUtils.assertContainsBearerToken(credentials.getRequestMetadata(CALL_URI), accessToken2); 1567 } 1568 1569 @Test getRequestMetadata_withAudience_selfSignedJWT()1570 public void getRequestMetadata_withAudience_selfSignedJWT() throws IOException { 1571 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1572 GoogleCredentials credentials = 1573 ServiceAccountCredentials.newBuilder() 1574 .setClientId(CLIENT_ID) 1575 .setClientEmail(CLIENT_EMAIL) 1576 .setPrivateKey(privateKey) 1577 .setPrivateKeyId(PRIVATE_KEY_ID) 1578 .setProjectId(PROJECT_ID) 1579 .setHttpTransportFactory(new MockTokenServerTransportFactory()) 1580 .build(); 1581 1582 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 1583 assertNull(((ServiceAccountCredentials) credentials).getSelfSignedJwtCredentialsWithScope()); 1584 verifyJwtAccess(metadata, null); 1585 } 1586 1587 @Test getRequestMetadata_withDefaultScopes_selfSignedJWT()1588 public void getRequestMetadata_withDefaultScopes_selfSignedJWT() throws IOException { 1589 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1590 GoogleCredentials credentials = 1591 ServiceAccountCredentials.newBuilder() 1592 .setClientId(CLIENT_ID) 1593 .setClientEmail(CLIENT_EMAIL) 1594 .setPrivateKey(privateKey) 1595 .setPrivateKeyId(PRIVATE_KEY_ID) 1596 .setScopes(null, SCOPES) 1597 .setProjectId(PROJECT_ID) 1598 .setHttpTransportFactory(new MockTokenServerTransportFactory()) 1599 .setUseJwtAccessWithScope(true) 1600 .build(); 1601 1602 Map<String, List<String>> metadata = credentials.getRequestMetadata(null); 1603 verifyJwtAccess(metadata, "dummy.scope"); 1604 } 1605 1606 @Test getRequestMetadataWithCallback_selfSignedJWT()1607 public void getRequestMetadataWithCallback_selfSignedJWT() throws IOException { 1608 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(PRIVATE_KEY_PKCS8); 1609 GoogleCredentials credentials = 1610 ServiceAccountCredentials.newBuilder() 1611 .setClientId(CLIENT_ID) 1612 .setClientEmail(CLIENT_EMAIL) 1613 .setPrivateKey(privateKey) 1614 .setPrivateKeyId(PRIVATE_KEY_ID) 1615 .setProjectId(PROJECT_ID) 1616 .setQuotaProjectId("my-quota-project-id") 1617 .setHttpTransportFactory(new MockTokenServerTransportFactory()) 1618 .setUseJwtAccessWithScope(true) 1619 .setScopes(SCOPES) 1620 .build(); 1621 1622 final AtomicBoolean success = new AtomicBoolean(false); 1623 credentials.getRequestMetadata( 1624 CALL_URI, 1625 null, 1626 new RequestMetadataCallback() { 1627 @Override 1628 public void onSuccess(Map<String, List<String>> metadata) { 1629 try { 1630 verifyJwtAccess(metadata, "dummy.scope"); 1631 } catch (IOException e) { 1632 fail("Should not throw a failure"); 1633 } 1634 success.set(true); 1635 } 1636 1637 @Override 1638 public void onFailure(Throwable exception) { 1639 fail("Should not throw a failure."); 1640 } 1641 }); 1642 1643 assertTrue("Should have run onSuccess() callback", success.get()); 1644 } 1645 verifyJwtAccess(Map<String, List<String>> metadata, String expectedScopeClaim)1646 private void verifyJwtAccess(Map<String, List<String>> metadata, String expectedScopeClaim) 1647 throws IOException { 1648 assertNotNull(metadata); 1649 List<String> authorizations = metadata.get(AuthHttpConstants.AUTHORIZATION); 1650 assertNotNull("Authorization headers not found", authorizations); 1651 String assertion = null; 1652 for (String authorization : authorizations) { 1653 if (authorization.startsWith(JWT_ACCESS_PREFIX)) { 1654 assertNull("Multiple bearer assertions found", assertion); 1655 assertion = authorization.substring(JWT_ACCESS_PREFIX.length()); 1656 } 1657 } 1658 assertNotNull("Bearer assertion not found", assertion); 1659 JsonWebSignature signature = 1660 JsonWebSignature.parse(GsonFactory.getDefaultInstance(), assertion); 1661 assertEquals(CLIENT_EMAIL, signature.getPayload().getIssuer()); 1662 assertEquals(CLIENT_EMAIL, signature.getPayload().getSubject()); 1663 if (expectedScopeClaim != null) { 1664 assertEquals(expectedScopeClaim, signature.getPayload().get("scope")); 1665 assertFalse(signature.getPayload().containsKey("aud")); 1666 } else { 1667 assertEquals(JWT_AUDIENCE, signature.getPayload().getAudience()); 1668 assertFalse(signature.getPayload().containsKey("scope")); 1669 } 1670 assertEquals(PRIVATE_KEY_ID, signature.getHeader().getKeyId()); 1671 } 1672 writeServiceAccountJson( String projectId, String quotaProjectId, String universeDomain)1673 static GenericJson writeServiceAccountJson( 1674 String projectId, String quotaProjectId, String universeDomain) { 1675 return writeServiceAccountJson( 1676 CLIENT_ID, 1677 CLIENT_EMAIL, 1678 PRIVATE_KEY_PKCS8, 1679 PRIVATE_KEY_ID, 1680 projectId, 1681 quotaProjectId, 1682 universeDomain); 1683 } 1684 writeServiceAccountJson( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, String projectId, String quotaProjectId, String universeDomain)1685 static GenericJson writeServiceAccountJson( 1686 String clientId, 1687 String clientEmail, 1688 String privateKeyPkcs8, 1689 String privateKeyId, 1690 String projectId, 1691 String quotaProjectId, 1692 String universeDomain) { 1693 GenericJson json = new GenericJson(); 1694 if (clientId != null) { 1695 json.put("client_id", clientId); 1696 } 1697 if (clientEmail != null) { 1698 json.put("client_email", clientEmail); 1699 } 1700 if (privateKeyPkcs8 != null) { 1701 json.put("private_key", privateKeyPkcs8); 1702 } 1703 if (privateKeyId != null) { 1704 json.put("private_key_id", privateKeyId); 1705 } 1706 if (projectId != null) { 1707 json.put("project_id", projectId); 1708 } 1709 if (quotaProjectId != null) { 1710 json.put("quota_project_id", quotaProjectId); 1711 } 1712 if (universeDomain != null) { 1713 json.put("universe_domain", universeDomain); 1714 } 1715 json.put("type", GoogleCredentials.SERVICE_ACCOUNT_FILE_TYPE); 1716 return json; 1717 } 1718 writeServiceAccountStream( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId)1719 static InputStream writeServiceAccountStream( 1720 String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId) 1721 throws IOException { 1722 return writeServiceAccountStream(clientId, clientEmail, privateKeyPkcs8, privateKeyId, null); 1723 } 1724 writeServiceAccountStream( String clientId, String clientEmail, String privateKeyPkcs8, String privateKeyId, String universeDomain)1725 static InputStream writeServiceAccountStream( 1726 String clientId, 1727 String clientEmail, 1728 String privateKeyPkcs8, 1729 String privateKeyId, 1730 String universeDomain) 1731 throws IOException { 1732 GenericJson json = 1733 writeServiceAccountJson( 1734 clientId, clientEmail, privateKeyPkcs8, privateKeyId, null, null, universeDomain); 1735 return TestUtils.jsonToInputStream(json); 1736 } 1737 testFromStreamException(InputStream stream, String expectedMessageContent)1738 private static void testFromStreamException(InputStream stream, String expectedMessageContent) { 1739 try { 1740 ServiceAccountCredentials.fromStream(stream, DUMMY_TRANSPORT_FACTORY); 1741 fail( 1742 String.format( 1743 "Should throw exception with message containing '%s'", expectedMessageContent)); 1744 } catch (IOException expected) { 1745 assertTrue(expected.getMessage().contains(expectedMessageContent)); 1746 } 1747 } 1748 } 1749