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.json.JsonFactory; 45 import com.google.api.client.json.gson.GsonFactory; 46 import com.google.api.client.json.webtoken.JsonWebSignature; 47 import com.google.api.client.util.Clock; 48 import com.google.auth.Credentials; 49 import com.google.auth.RequestMetadataCallback; 50 import com.google.auth.TestClock; 51 import com.google.auth.http.AuthHttpConstants; 52 import java.io.IOException; 53 import java.io.InputStream; 54 import java.net.URI; 55 import java.security.InvalidKeyException; 56 import java.security.NoSuchAlgorithmException; 57 import java.security.PrivateKey; 58 import java.security.Signature; 59 import java.security.SignatureException; 60 import java.util.Collections; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.concurrent.TimeUnit; 64 import java.util.concurrent.atomic.AtomicBoolean; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.junit.runners.JUnit4; 68 69 /** Test case for {@link ServiceAccountCredentials}. */ 70 @RunWith(JUnit4.class) 71 public class ServiceAccountJwtAccessCredentialsTest extends BaseSerializationTest { 72 73 private static final String SA_CLIENT_EMAIL = 74 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com"; 75 private static final String SA_CLIENT_ID = 76 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr.apps.googleusercontent.com"; 77 private static final String SA_PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d"; 78 private static final String SA_PRIVATE_KEY_PKCS8 = 79 "-----BEGIN PRIVATE KEY-----\n" 80 + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i" 81 + "kv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0" 82 + "zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw" 83 + "4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/Gr" 84 + "CtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6" 85 + "D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrP" 86 + "SXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAut" 87 + "LPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEA" 88 + "gidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJ" 89 + "ADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ" 90 + "==\n-----END PRIVATE KEY-----\n"; 91 private static final String JWT_ACCESS_PREFIX = 92 ServiceAccountJwtAccessCredentials.JWT_ACCESS_PREFIX; 93 private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); 94 private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); 95 private static final String QUOTA_PROJECT = "sample-quota-project-id"; 96 97 @Test constructor_allParameters_constructs()98 public void constructor_allParameters_constructs() throws IOException { 99 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 100 ServiceAccountJwtAccessCredentials credentials = 101 ServiceAccountJwtAccessCredentials.newBuilder() 102 .setClientId(SA_CLIENT_ID) 103 .setClientEmail(SA_CLIENT_EMAIL) 104 .setPrivateKey(privateKey) 105 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 106 .setQuotaProjectId(QUOTA_PROJECT) 107 .build(); 108 109 assertEquals(SA_CLIENT_ID, credentials.getClientId()); 110 assertEquals(SA_CLIENT_EMAIL, credentials.getClientEmail()); 111 assertEquals(privateKey, credentials.getPrivateKey()); 112 assertEquals(SA_PRIVATE_KEY_ID, credentials.getPrivateKeyId()); 113 assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); 114 } 115 116 @Test constructor_noClientId_constructs()117 public void constructor_noClientId_constructs() throws IOException { 118 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 119 ServiceAccountJwtAccessCredentials.newBuilder() 120 .setClientEmail(SA_CLIENT_EMAIL) 121 .setPrivateKey(privateKey) 122 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 123 .build(); 124 } 125 126 @Test constructor_noPrivateKeyId_constructs()127 public void constructor_noPrivateKeyId_constructs() throws IOException { 128 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 129 ServiceAccountJwtAccessCredentials.newBuilder() 130 .setClientId(SA_CLIENT_ID) 131 .setClientEmail(SA_CLIENT_EMAIL) 132 .setPrivateKey(privateKey) 133 .build(); 134 } 135 136 @Test constructor_noEmail_throws()137 public void constructor_noEmail_throws() throws IOException { 138 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 139 try { 140 ServiceAccountJwtAccessCredentials.newBuilder() 141 .setClientId(SA_CLIENT_ID) 142 .setPrivateKey(privateKey) 143 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 144 .build(); 145 fail("exception expected"); 146 } catch (NullPointerException e) { 147 // Expected 148 } 149 } 150 151 @Test constructor_noPrivateKey_throws()152 public void constructor_noPrivateKey_throws() { 153 try { 154 ServiceAccountJwtAccessCredentials.newBuilder() 155 .setClientId(SA_CLIENT_ID) 156 .setClientEmail(SA_CLIENT_EMAIL) 157 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 158 .build(); 159 fail("exception expected"); 160 } catch (NullPointerException e) { 161 // Expected 162 } 163 } 164 165 @Test getAuthenticationType_returnsJwtAccess()166 public void getAuthenticationType_returnsJwtAccess() throws IOException { 167 Credentials credentials = 168 ServiceAccountJwtAccessCredentials.fromPkcs8( 169 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 170 assertEquals(credentials.getAuthenticationType(), "JWTAccess"); 171 } 172 173 @Test hasRequestMetadata_returnsTrue()174 public void hasRequestMetadata_returnsTrue() throws IOException { 175 Credentials credentials = 176 ServiceAccountJwtAccessCredentials.fromPkcs8( 177 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 178 assertTrue(credentials.hasRequestMetadata()); 179 } 180 181 @Test hasRequestMetadataOnly_returnsTrue()182 public void hasRequestMetadataOnly_returnsTrue() throws IOException { 183 Credentials credentials = 184 ServiceAccountJwtAccessCredentials.fromPkcs8( 185 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 186 assertTrue(credentials.hasRequestMetadataOnly()); 187 } 188 189 @Test getRequestMetadata_blocking_hasJwtAccess()190 public void getRequestMetadata_blocking_hasJwtAccess() throws IOException { 191 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 192 ServiceAccountJwtAccessCredentials credentials = 193 ServiceAccountJwtAccessCredentials.newBuilder() 194 .setClientId(SA_CLIENT_ID) 195 .setClientEmail(SA_CLIENT_EMAIL) 196 .setPrivateKey(privateKey) 197 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 198 .build(); 199 200 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 201 202 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 203 } 204 205 @Test getRequestMetadata_blocking_defaultURI_hasJwtAccess()206 public void getRequestMetadata_blocking_defaultURI_hasJwtAccess() throws IOException { 207 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 208 Credentials credentials = 209 ServiceAccountJwtAccessCredentials.newBuilder() 210 .setClientId(SA_CLIENT_ID) 211 .setClientEmail(SA_CLIENT_EMAIL) 212 .setPrivateKey(privateKey) 213 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 214 .setDefaultAudience(CALL_URI) 215 .build(); 216 217 Map<String, List<String>> metadata = credentials.getRequestMetadata(); 218 219 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 220 } 221 222 @Test getRequestMetadata_blocking_noURI_throws()223 public void getRequestMetadata_blocking_noURI_throws() throws IOException { 224 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 225 ServiceAccountJwtAccessCredentials credentials = 226 ServiceAccountJwtAccessCredentials.newBuilder() 227 .setClientId(SA_CLIENT_ID) 228 .setClientEmail(SA_CLIENT_EMAIL) 229 .setPrivateKey(privateKey) 230 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 231 .build(); 232 233 try { 234 credentials.getRequestMetadata(); 235 fail("exception expected"); 236 } catch (IOException e) { 237 // Expected 238 } 239 } 240 241 @Test getRequestMetadata_blocking_cached()242 public void getRequestMetadata_blocking_cached() throws IOException { 243 TestClock testClock = new TestClock(); 244 245 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 246 ServiceAccountJwtAccessCredentials credentials = 247 ServiceAccountJwtAccessCredentials.newBuilder() 248 .setClientId(SA_CLIENT_ID) 249 .setClientEmail(SA_CLIENT_EMAIL) 250 .setPrivateKey(privateKey) 251 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 252 .build(); 253 credentials.clock = testClock; 254 255 Map<String, List<String>> metadata1 = credentials.getRequestMetadata(CALL_URI); 256 257 // Fast forward time a little 258 long lifeSpanMs = TimeUnit.SECONDS.toMillis(10); 259 testClock.setCurrentTime(lifeSpanMs); 260 261 Map<String, List<String>> metadata2 = credentials.getRequestMetadata(CALL_URI); 262 263 assertEquals(metadata1, metadata2); 264 } 265 266 @Test getRequestMetadata_blocking_cache_expired()267 public void getRequestMetadata_blocking_cache_expired() throws IOException { 268 TestClock testClock = new TestClock(); 269 270 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 271 ServiceAccountJwtAccessCredentials credentials = 272 ServiceAccountJwtAccessCredentials.newBuilder() 273 .setClientId(SA_CLIENT_ID) 274 .setClientEmail(SA_CLIENT_EMAIL) 275 .setPrivateKey(privateKey) 276 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 277 .build(); 278 credentials.clock = testClock; 279 280 Map<String, List<String>> metadata1 = credentials.getRequestMetadata(CALL_URI); 281 282 // Fast forward time past the expiration 283 long lifeSpanMs = TimeUnit.SECONDS.toMillis(ServiceAccountJwtAccessCredentials.LIFE_SPAN_SECS); 284 testClock.setCurrentTime(lifeSpanMs); 285 286 Map<String, List<String>> metadata2 = credentials.getRequestMetadata(CALL_URI); 287 288 assertNotEquals(metadata1, metadata2); 289 } 290 291 @Test getRequestMetadata_async_hasJwtAccess()292 public void getRequestMetadata_async_hasJwtAccess() throws IOException { 293 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 294 ServiceAccountJwtAccessCredentials credentials = 295 ServiceAccountJwtAccessCredentials.newBuilder() 296 .setClientId(SA_CLIENT_ID) 297 .setClientEmail(SA_CLIENT_EMAIL) 298 .setPrivateKey(privateKey) 299 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 300 .build(); 301 MockExecutor executor = new MockExecutor(); 302 MockRequestMetadataCallback callback = new MockRequestMetadataCallback(); 303 304 credentials.getRequestMetadata(CALL_URI, executor, callback); 305 assertEquals(0, executor.numTasks()); 306 assertNotNull(callback.metadata); 307 verifyJwtAccess(callback.metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 308 } 309 310 @Test getRequestMetadata_async_defaultURI_hasJwtAccess()311 public void getRequestMetadata_async_defaultURI_hasJwtAccess() throws IOException { 312 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 313 Credentials credentials = 314 ServiceAccountJwtAccessCredentials.newBuilder() 315 .setClientId(SA_CLIENT_ID) 316 .setClientEmail(SA_CLIENT_EMAIL) 317 .setPrivateKey(privateKey) 318 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 319 .setDefaultAudience(CALL_URI) 320 .build(); 321 MockExecutor executor = new MockExecutor(); 322 MockRequestMetadataCallback callback = new MockRequestMetadataCallback(); 323 324 credentials.getRequestMetadata(null, executor, callback); 325 assertEquals(0, executor.numTasks()); 326 assertNotNull(callback.metadata); 327 verifyJwtAccess(callback.metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 328 } 329 330 @Test getRequestMetadata_async_noURI_exception()331 public void getRequestMetadata_async_noURI_exception() throws IOException { 332 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 333 ServiceAccountJwtAccessCredentials credentials = 334 ServiceAccountJwtAccessCredentials.newBuilder() 335 .setClientId(SA_CLIENT_ID) 336 .setClientEmail(SA_CLIENT_EMAIL) 337 .setPrivateKey(privateKey) 338 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 339 .build(); 340 MockExecutor executor = new MockExecutor(); 341 MockRequestMetadataCallback callback = new MockRequestMetadataCallback(); 342 343 credentials.getRequestMetadata(null, executor, callback); 344 assertEquals(0, executor.numTasks()); 345 assertNotNull(callback.exception); 346 } 347 348 @Test getRequestMetadata_async_cache_expired()349 public void getRequestMetadata_async_cache_expired() throws IOException { 350 TestClock testClock = new TestClock(); 351 352 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 353 ServiceAccountJwtAccessCredentials credentials = 354 ServiceAccountJwtAccessCredentials.newBuilder() 355 .setClientId(SA_CLIENT_ID) 356 .setClientEmail(SA_CLIENT_EMAIL) 357 .setPrivateKey(privateKey) 358 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 359 .build(); 360 credentials.clock = testClock; 361 MockExecutor executor = new MockExecutor(); 362 363 MockRequestMetadataCallback callback1 = new MockRequestMetadataCallback(); 364 credentials.getRequestMetadata(CALL_URI, executor, callback1); 365 366 // Fast forward time past the expiration 367 long lifeSpanMs = TimeUnit.SECONDS.toMillis(ServiceAccountJwtAccessCredentials.LIFE_SPAN_SECS); 368 testClock.setCurrentTime(lifeSpanMs); 369 370 MockRequestMetadataCallback callback2 = new MockRequestMetadataCallback(); 371 credentials.getRequestMetadata(CALL_URI, executor, callback2); 372 373 assertNotEquals(callback1.metadata, callback2.metadata); 374 } 375 376 @Test getRequestMetadata_async_cached()377 public void getRequestMetadata_async_cached() throws IOException { 378 TestClock testClock = new TestClock(); 379 380 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 381 ServiceAccountJwtAccessCredentials credentials = 382 ServiceAccountJwtAccessCredentials.newBuilder() 383 .setClientId(SA_CLIENT_ID) 384 .setClientEmail(SA_CLIENT_EMAIL) 385 .setPrivateKey(privateKey) 386 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 387 .build(); 388 credentials.clock = testClock; 389 MockExecutor executor = new MockExecutor(); 390 391 MockRequestMetadataCallback callback1 = new MockRequestMetadataCallback(); 392 credentials.getRequestMetadata(CALL_URI, executor, callback1); 393 394 // Fast forward time a little 395 long lifeSpanMs = TimeUnit.SECONDS.toMillis(10); 396 testClock.setCurrentTime(lifeSpanMs); 397 398 MockRequestMetadataCallback callback2 = new MockRequestMetadataCallback(); 399 credentials.getRequestMetadata(CALL_URI, executor, callback2); 400 401 assertEquals(callback1.metadata, callback2.metadata); 402 } 403 404 @Test getRequestMetadata_contains_quotaProjectId()405 public void getRequestMetadata_contains_quotaProjectId() throws IOException { 406 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 407 Credentials credentials = 408 ServiceAccountJwtAccessCredentials.newBuilder() 409 .setClientId(SA_CLIENT_ID) 410 .setClientEmail(SA_CLIENT_EMAIL) 411 .setPrivateKey(privateKey) 412 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 413 .setDefaultAudience(CALL_URI) 414 .setQuotaProjectId(QUOTA_PROJECT) 415 .build(); 416 417 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 418 assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); 419 assertEquals( 420 metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), 421 Collections.singletonList(QUOTA_PROJECT)); 422 } 423 424 @Test getAccount_sameAs()425 public void getAccount_sameAs() throws IOException { 426 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 427 ServiceAccountJwtAccessCredentials credentials = 428 ServiceAccountJwtAccessCredentials.newBuilder() 429 .setClientId(SA_CLIENT_ID) 430 .setClientEmail(SA_CLIENT_EMAIL) 431 .setPrivateKey(privateKey) 432 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 433 .build(); 434 assertEquals(SA_CLIENT_EMAIL, credentials.getAccount()); 435 } 436 437 @Test sign_sameAs()438 public void sign_sameAs() 439 throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { 440 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 441 byte[] toSign = {0xD, 0xE, 0xA, 0xD}; 442 ServiceAccountJwtAccessCredentials credentials = 443 ServiceAccountJwtAccessCredentials.newBuilder() 444 .setClientId(SA_CLIENT_ID) 445 .setClientEmail(SA_CLIENT_EMAIL) 446 .setPrivateKey(privateKey) 447 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 448 .build(); 449 byte[] signedBytes = credentials.sign(toSign); 450 Signature signature = Signature.getInstance(OAuth2Utils.SIGNATURE_ALGORITHM); 451 signature.initSign(credentials.getPrivateKey()); 452 signature.update(toSign); 453 assertArrayEquals(signature.sign(), signedBytes); 454 } 455 456 @Test equals_true()457 public void equals_true() throws IOException { 458 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 459 ServiceAccountJwtAccessCredentials credentials = 460 ServiceAccountJwtAccessCredentials.newBuilder() 461 .setClientId(SA_CLIENT_ID) 462 .setClientEmail(SA_CLIENT_EMAIL) 463 .setPrivateKey(privateKey) 464 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 465 .setDefaultAudience(CALL_URI) 466 .build(); 467 ServiceAccountJwtAccessCredentials otherCredentials = 468 ServiceAccountJwtAccessCredentials.newBuilder() 469 .setClientId(SA_CLIENT_ID) 470 .setClientEmail(SA_CLIENT_EMAIL) 471 .setPrivateKey(privateKey) 472 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 473 .setDefaultAudience(CALL_URI) 474 .build(); 475 assertTrue(credentials.equals(otherCredentials)); 476 assertTrue(otherCredentials.equals(credentials)); 477 } 478 479 @Test equals_false_clientId()480 public void equals_false_clientId() throws IOException { 481 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 482 ServiceAccountJwtAccessCredentials credentials = 483 ServiceAccountJwtAccessCredentials.newBuilder() 484 .setClientId(SA_CLIENT_ID) 485 .setClientEmail(SA_CLIENT_EMAIL) 486 .setPrivateKey(privateKey) 487 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 488 .setDefaultAudience(CALL_URI) 489 .build(); 490 ServiceAccountJwtAccessCredentials otherCredentials = 491 ServiceAccountJwtAccessCredentials.newBuilder() 492 .setClientId("otherClientId") 493 .setClientEmail(SA_CLIENT_EMAIL) 494 .setPrivateKey(privateKey) 495 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 496 .setDefaultAudience(CALL_URI) 497 .build(); 498 assertFalse(credentials.equals(otherCredentials)); 499 assertFalse(otherCredentials.equals(credentials)); 500 } 501 502 @Test equals_false_email()503 public void equals_false_email() throws IOException { 504 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 505 ServiceAccountJwtAccessCredentials credentials = 506 ServiceAccountJwtAccessCredentials.newBuilder() 507 .setClientId(SA_CLIENT_ID) 508 .setClientEmail(SA_CLIENT_EMAIL) 509 .setPrivateKey(privateKey) 510 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 511 .setDefaultAudience(CALL_URI) 512 .build(); 513 ServiceAccountJwtAccessCredentials otherCredentials = 514 ServiceAccountJwtAccessCredentials.newBuilder() 515 .setClientId(SA_CLIENT_ID) 516 .setClientEmail("otherClientEmail") 517 .setPrivateKey(privateKey) 518 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 519 .setDefaultAudience(CALL_URI) 520 .build(); 521 assertFalse(credentials.equals(otherCredentials)); 522 assertFalse(otherCredentials.equals(credentials)); 523 } 524 525 @Test equals_false_keyId()526 public void equals_false_keyId() throws IOException { 527 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 528 ServiceAccountJwtAccessCredentials credentials = 529 ServiceAccountJwtAccessCredentials.newBuilder() 530 .setClientId(SA_CLIENT_ID) 531 .setClientEmail(SA_CLIENT_EMAIL) 532 .setPrivateKey(privateKey) 533 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 534 .setDefaultAudience(CALL_URI) 535 .build(); 536 ServiceAccountJwtAccessCredentials otherCredentials = 537 ServiceAccountJwtAccessCredentials.newBuilder() 538 .setClientId(SA_CLIENT_ID) 539 .setClientEmail(SA_CLIENT_EMAIL) 540 .setPrivateKey(privateKey) 541 .setPrivateKeyId("otherKeyId") 542 .setDefaultAudience(CALL_URI) 543 .build(); 544 assertFalse(credentials.equals(otherCredentials)); 545 assertFalse(otherCredentials.equals(credentials)); 546 } 547 548 @Test equals_false_callUri()549 public void equals_false_callUri() throws IOException { 550 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 551 final URI otherCallUri = URI.create("https://foo.com/bar"); 552 ServiceAccountJwtAccessCredentials credentials = 553 ServiceAccountJwtAccessCredentials.newBuilder() 554 .setClientId(SA_CLIENT_ID) 555 .setClientEmail(SA_CLIENT_EMAIL) 556 .setPrivateKey(privateKey) 557 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 558 .setDefaultAudience(CALL_URI) 559 .build(); 560 ServiceAccountJwtAccessCredentials otherCredentials = 561 ServiceAccountJwtAccessCredentials.newBuilder() 562 .setClientId(SA_CLIENT_ID) 563 .setClientEmail(SA_CLIENT_EMAIL) 564 .setPrivateKey(privateKey) 565 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 566 .setDefaultAudience(otherCallUri) 567 .build(); 568 assertFalse(credentials.equals(otherCredentials)); 569 assertFalse(otherCredentials.equals(credentials)); 570 } 571 572 @Test toString_containsFields()573 public void toString_containsFields() throws IOException { 574 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 575 ServiceAccountJwtAccessCredentials credentials = 576 ServiceAccountJwtAccessCredentials.newBuilder() 577 .setClientId(SA_CLIENT_ID) 578 .setClientEmail(SA_CLIENT_EMAIL) 579 .setPrivateKey(privateKey) 580 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 581 .setDefaultAudience(CALL_URI) 582 .setQuotaProjectId(QUOTA_PROJECT) 583 .build(); 584 String expectedToString = 585 String.format( 586 "ServiceAccountJwtAccessCredentials{clientId=%s, clientEmail=%s, privateKeyId=%s, " 587 + "defaultAudience=%s, quotaProjectId=%s}", 588 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_ID, CALL_URI, QUOTA_PROJECT); 589 assertEquals(expectedToString, credentials.toString()); 590 } 591 592 @Test hashCode_equals()593 public void hashCode_equals() throws IOException { 594 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 595 ServiceAccountJwtAccessCredentials credentials = 596 ServiceAccountJwtAccessCredentials.newBuilder() 597 .setClientId(SA_CLIENT_ID) 598 .setClientEmail(SA_CLIENT_EMAIL) 599 .setPrivateKey(privateKey) 600 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 601 .setDefaultAudience(CALL_URI) 602 .build(); 603 ServiceAccountJwtAccessCredentials otherCredentials = 604 ServiceAccountJwtAccessCredentials.newBuilder() 605 .setClientId(SA_CLIENT_ID) 606 .setClientEmail(SA_CLIENT_EMAIL) 607 .setPrivateKey(privateKey) 608 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 609 .setDefaultAudience(CALL_URI) 610 .build(); 611 assertEquals(credentials.hashCode(), otherCredentials.hashCode()); 612 } 613 614 @Test serialize()615 public void serialize() throws IOException, ClassNotFoundException { 616 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 617 ServiceAccountJwtAccessCredentials credentials = 618 ServiceAccountJwtAccessCredentials.newBuilder() 619 .setClientId(SA_CLIENT_ID) 620 .setClientEmail(SA_CLIENT_EMAIL) 621 .setPrivateKey(privateKey) 622 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 623 .setDefaultAudience(CALL_URI) 624 .build(); 625 ServiceAccountJwtAccessCredentials deserializedCredentials = 626 serializeAndDeserialize(credentials); 627 verifyJwtAccess( 628 deserializedCredentials.getRequestMetadata(), SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 629 assertEquals(credentials, deserializedCredentials); 630 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 631 assertEquals(credentials.toString(), deserializedCredentials.toString()); 632 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 633 } 634 635 @Test fromStream_nullStream_throws()636 public void fromStream_nullStream_throws() throws IOException { 637 MockHttpTransportFactory transportFactory = new MockHttpTransportFactory(); 638 try { 639 ServiceAccountCredentials.fromStream(null, transportFactory); 640 fail("Should throw if InputStream is null"); 641 } catch (NullPointerException expected) { 642 // Expected 643 } 644 } 645 646 @Test fromStream_hasJwtAccess()647 public void fromStream_hasJwtAccess() throws IOException { 648 InputStream serviceAccountStream = 649 ServiceAccountCredentialsTest.writeServiceAccountStream( 650 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 651 652 Credentials credentials = ServiceAccountJwtAccessCredentials.fromStream(serviceAccountStream); 653 654 assertNotNull(credentials); 655 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 656 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 657 } 658 659 @Test fromStream_defaultURI_hasJwtAccess()660 public void fromStream_defaultURI_hasJwtAccess() throws IOException { 661 InputStream serviceAccountStream = 662 ServiceAccountCredentialsTest.writeServiceAccountStream( 663 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 664 665 Credentials credentials = 666 ServiceAccountJwtAccessCredentials.fromStream(serviceAccountStream, CALL_URI); 667 668 assertNotNull(credentials); 669 Map<String, List<String>> metadata = credentials.getRequestMetadata(null); 670 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, CALL_URI, SA_PRIVATE_KEY_ID); 671 } 672 673 @Test fromStream_noClientId_throws()674 public void fromStream_noClientId_throws() throws IOException { 675 InputStream serviceAccountStream = 676 ServiceAccountCredentialsTest.writeServiceAccountStream( 677 null, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 678 679 testFromStreamException(serviceAccountStream, "client_id"); 680 } 681 682 @Test fromStream_noClientEmail_throws()683 public void fromStream_noClientEmail_throws() throws IOException { 684 InputStream serviceAccountStream = 685 ServiceAccountCredentialsTest.writeServiceAccountStream( 686 SA_CLIENT_ID, null, SA_PRIVATE_KEY_PKCS8, SA_PRIVATE_KEY_ID); 687 688 testFromStreamException(serviceAccountStream, "client_email"); 689 } 690 691 @Test fromStream_noPrivateKey_throws()692 public void fromStream_noPrivateKey_throws() throws IOException { 693 InputStream serviceAccountStream = 694 ServiceAccountCredentialsTest.writeServiceAccountStream( 695 SA_CLIENT_ID, SA_CLIENT_EMAIL, null, SA_PRIVATE_KEY_ID); 696 697 testFromStreamException(serviceAccountStream, "private_key"); 698 } 699 700 @Test fromStream_noPrivateKeyId_throws()701 public void fromStream_noPrivateKeyId_throws() throws IOException { 702 InputStream serviceAccountStream = 703 ServiceAccountCredentialsTest.writeServiceAccountStream( 704 SA_CLIENT_ID, SA_CLIENT_EMAIL, SA_PRIVATE_KEY_PKCS8, null); 705 706 testFromStreamException(serviceAccountStream, "private_key_id"); 707 } 708 709 @Test jwtWithClaims_overrideAudience()710 public void jwtWithClaims_overrideAudience() throws IOException { 711 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 712 ServiceAccountJwtAccessCredentials credentials = 713 ServiceAccountJwtAccessCredentials.newBuilder() 714 .setClientId(SA_CLIENT_ID) 715 .setClientEmail(SA_CLIENT_EMAIL) 716 .setPrivateKey(privateKey) 717 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 718 .build(); 719 Credentials withAudience = 720 credentials.jwtWithClaims(JwtClaims.newBuilder().setAudience("new-audience").build()); 721 722 Map<String, List<String>> metadata = withAudience.getRequestMetadata(CALL_URI); 723 724 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, URI.create("new-audience"), SA_PRIVATE_KEY_ID); 725 } 726 727 @Test jwtWithClaims_noAudience()728 public void jwtWithClaims_noAudience() throws IOException { 729 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 730 ServiceAccountJwtAccessCredentials credentials = 731 ServiceAccountJwtAccessCredentials.newBuilder() 732 .setClientId(SA_CLIENT_ID) 733 .setClientEmail(SA_CLIENT_EMAIL) 734 .setPrivateKey(privateKey) 735 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 736 .build(); 737 try { 738 credentials.jwtWithClaims(JwtClaims.newBuilder().build()); 739 fail("Expected to throw exception for missing audience"); 740 } catch (IllegalStateException ex) { 741 // expected exception 742 } 743 } 744 745 @Test jwtWithClaims_defaultAudience()746 public void jwtWithClaims_defaultAudience() throws IOException { 747 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 748 ServiceAccountJwtAccessCredentials credentials = 749 ServiceAccountJwtAccessCredentials.newBuilder() 750 .setClientId(SA_CLIENT_ID) 751 .setClientEmail(SA_CLIENT_EMAIL) 752 .setPrivateKey(privateKey) 753 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 754 .setDefaultAudience(URI.create("default-audience")) 755 .build(); 756 Credentials withAudience = credentials.jwtWithClaims(JwtClaims.newBuilder().build()); 757 758 Map<String, List<String>> metadata = withAudience.getRequestMetadata(CALL_URI); 759 verifyJwtAccess(metadata, SA_CLIENT_EMAIL, URI.create("default-audience"), SA_PRIVATE_KEY_ID); 760 } 761 762 @Test getRequestMetadataSetsQuotaProjectId()763 public void getRequestMetadataSetsQuotaProjectId() throws IOException { 764 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 765 ServiceAccountJwtAccessCredentials credentials = 766 ServiceAccountJwtAccessCredentials.newBuilder() 767 .setClientId(SA_CLIENT_ID) 768 .setClientEmail(SA_CLIENT_EMAIL) 769 .setPrivateKey(privateKey) 770 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 771 .setQuotaProjectId("my-quota-project-id") 772 .setDefaultAudience(URI.create("default-audience")) 773 .build(); 774 775 Map<String, List<String>> metadata = credentials.getRequestMetadata(); 776 assertTrue(metadata.containsKey("x-goog-user-project")); 777 List<String> headerValues = metadata.get("x-goog-user-project"); 778 assertEquals(1, headerValues.size()); 779 assertEquals("my-quota-project-id", headerValues.get(0)); 780 } 781 782 @Test getRequestMetadataNoQuotaProjectId()783 public void getRequestMetadataNoQuotaProjectId() throws IOException { 784 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 785 ServiceAccountJwtAccessCredentials credentials = 786 ServiceAccountJwtAccessCredentials.newBuilder() 787 .setClientId(SA_CLIENT_ID) 788 .setClientEmail(SA_CLIENT_EMAIL) 789 .setPrivateKey(privateKey) 790 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 791 .setDefaultAudience(URI.create("default-audience")) 792 .build(); 793 794 Map<String, List<String>> metadata = credentials.getRequestMetadata(); 795 assertFalse(metadata.containsKey("x-goog-user-project")); 796 } 797 798 @Test getRequestMetadataWithCallback()799 public void getRequestMetadataWithCallback() throws IOException { 800 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 801 ServiceAccountJwtAccessCredentials credentials = 802 ServiceAccountJwtAccessCredentials.newBuilder() 803 .setClientId(SA_CLIENT_ID) 804 .setClientEmail(SA_CLIENT_EMAIL) 805 .setPrivateKey(privateKey) 806 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 807 .setQuotaProjectId("my-quota-project-id") 808 .setDefaultAudience(URI.create("default-audience")) 809 .build(); 810 811 final Map<String, List<String>> plainMetadata = credentials.getRequestMetadata(); 812 final AtomicBoolean success = new AtomicBoolean(false); 813 credentials.getRequestMetadata( 814 null, 815 null, 816 new RequestMetadataCallback() { 817 @Override 818 public void onSuccess(Map<String, List<String>> metadata) { 819 assertEquals(plainMetadata, metadata); 820 success.set(true); 821 } 822 823 @Override 824 public void onFailure(Throwable exception) { 825 fail("Should not throw a failure."); 826 } 827 }); 828 829 assertTrue("Should have run onSuccess() callback", success.get()); 830 } 831 verifyJwtAccess( Map<String, List<String>> metadata, String expectedEmail, URI expectedAudience, String expectedKeyId)832 private void verifyJwtAccess( 833 Map<String, List<String>> metadata, 834 String expectedEmail, 835 URI expectedAudience, 836 String expectedKeyId) 837 throws IOException { 838 assertNotNull(metadata); 839 List<String> authorizations = metadata.get(AuthHttpConstants.AUTHORIZATION); 840 assertNotNull("Authorization headers not found", authorizations); 841 String assertion = null; 842 for (String authorization : authorizations) { 843 if (authorization.startsWith(JWT_ACCESS_PREFIX)) { 844 assertNull("Multiple bearer assertions found", assertion); 845 assertion = authorization.substring(JWT_ACCESS_PREFIX.length()); 846 } 847 } 848 assertNotNull("Bearer assertion not found", assertion); 849 JsonWebSignature signature = JsonWebSignature.parse(JSON_FACTORY, assertion); 850 assertEquals(expectedEmail, signature.getPayload().getIssuer()); 851 assertEquals(expectedEmail, signature.getPayload().getSubject()); 852 assertEquals(expectedAudience.toString(), signature.getPayload().getAudience()); 853 assertEquals(expectedKeyId, signature.getHeader().getKeyId()); 854 } 855 testFromStreamException(InputStream stream, String expectedMessageContent)856 private static void testFromStreamException(InputStream stream, String expectedMessageContent) { 857 try { 858 ServiceAccountJwtAccessCredentials.fromStream(stream, CALL_URI); 859 fail( 860 String.format( 861 "Should throw exception with message containing '%s'", expectedMessageContent)); 862 } catch (IOException expected) { 863 assertTrue(expected.getMessage().contains(expectedMessageContent)); 864 } 865 } 866 } 867