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.assertEquals; 35 import static org.junit.Assert.assertFalse; 36 import static org.junit.Assert.assertNotEquals; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNull; 39 import static org.junit.Assert.assertSame; 40 import static org.junit.Assert.assertTrue; 41 import static org.junit.Assert.fail; 42 43 import com.google.api.client.json.GenericJson; 44 import com.google.api.client.testing.http.MockLowLevelHttpResponse; 45 import com.google.api.client.util.Clock; 46 import com.google.auth.RequestMetadataCallback; 47 import com.google.auth.TestUtils; 48 import com.google.auth.http.AuthHttpConstants; 49 import com.google.common.collect.ImmutableList; 50 import com.google.common.collect.ImmutableMap; 51 import java.io.ByteArrayInputStream; 52 import java.io.File; 53 import java.io.FileInputStream; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.net.URI; 57 import java.time.Duration; 58 import java.time.temporal.ChronoUnit; 59 import java.util.Collection; 60 import java.util.Collections; 61 import java.util.Date; 62 import java.util.List; 63 import java.util.Map; 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 UserCredentials}. */ 70 @RunWith(JUnit4.class) 71 public class UserCredentialsTest extends BaseSerializationTest { 72 73 private static final String CLIENT_SECRET = "jakuaL9YyieakhECKL2SwZcu"; 74 private static final String CLIENT_ID = "ya29.1.AADtN_UtlxN3PuGAxrN2XQnZTVRvDyVWnYq4I6dws"; 75 private static final String REFRESH_TOKEN = "1/Tl6awhpFjkMkSJoj1xsli0H2eL5YsMgU_NKPY2TyGWY"; 76 private static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; 77 private static final String QUOTA_PROJECT = "sample-quota-project-id"; 78 private static final Collection<String> SCOPES = Collections.singletonList("dummy.scope"); 79 private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); 80 public static final String DEFAULT_ID_TOKEN = 81 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyO" 82 + "TNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiL" 83 + "CJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwi" 84 + "aXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In0" 85 + ".redacted"; 86 87 @Test(expected = IllegalStateException.class) constructor_accessAndRefreshTokenNull_throws()88 public void constructor_accessAndRefreshTokenNull_throws() { 89 UserCredentials.newBuilder().setClientId(CLIENT_ID).setClientSecret(CLIENT_SECRET).build(); 90 } 91 92 @Test constructor()93 public void constructor() { 94 UserCredentials credentials = 95 UserCredentials.newBuilder() 96 .setClientId(CLIENT_ID) 97 .setClientSecret(CLIENT_SECRET) 98 .setRefreshToken(REFRESH_TOKEN) 99 .setQuotaProjectId(QUOTA_PROJECT) 100 .build(); 101 assertEquals(CLIENT_ID, credentials.getClientId()); 102 assertEquals(CLIENT_SECRET, credentials.getClientSecret()); 103 assertEquals(REFRESH_TOKEN, credentials.getRefreshToken()); 104 assertEquals(QUOTA_PROJECT, credentials.getQuotaProjectId()); 105 } 106 107 @Test createScoped_same()108 public void createScoped_same() { 109 UserCredentials userCredentials = 110 UserCredentials.newBuilder() 111 .setClientId(CLIENT_ID) 112 .setClientSecret(CLIENT_SECRET) 113 .setRefreshToken(REFRESH_TOKEN) 114 .build(); 115 assertSame(userCredentials, userCredentials.createScoped(SCOPES)); 116 } 117 118 @Test createScopedRequired_false()119 public void createScopedRequired_false() { 120 UserCredentials userCredentials = 121 UserCredentials.newBuilder() 122 .setClientId(CLIENT_ID) 123 .setClientSecret(CLIENT_SECRET) 124 .setRefreshToken(REFRESH_TOKEN) 125 .build(); 126 assertFalse(userCredentials.createScopedRequired()); 127 } 128 129 @Test fromJson_hasAccessToken()130 public void fromJson_hasAccessToken() throws IOException { 131 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 132 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 133 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 134 GenericJson json = writeUserJson(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, null); 135 136 GoogleCredentials credentials = UserCredentials.fromJson(json, transportFactory); 137 138 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 139 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 140 } 141 142 @Test fromJson_hasQuotaProjectId()143 public void fromJson_hasQuotaProjectId() throws IOException { 144 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 145 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 146 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 147 GenericJson json = writeUserJson(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); 148 149 GoogleCredentials credentials = UserCredentials.fromJson(json, transportFactory); 150 151 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 152 assertTrue(metadata.containsKey(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY)); 153 assertEquals( 154 metadata.get(GoogleCredentials.QUOTA_PROJECT_ID_HEADER_KEY), 155 Collections.singletonList(QUOTA_PROJECT)); 156 } 157 158 @Test getRequestMetadata_initialToken_hasAccessToken()159 public void getRequestMetadata_initialToken_hasAccessToken() throws IOException { 160 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 161 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 162 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 163 UserCredentials userCredentials = 164 UserCredentials.newBuilder() 165 .setClientId(CLIENT_ID) 166 .setClientSecret(CLIENT_SECRET) 167 .setAccessToken(accessToken) 168 .setHttpTransportFactory(transportFactory) 169 .build(); 170 171 Map<String, List<String>> metadata = userCredentials.getRequestMetadata(CALL_URI); 172 173 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 174 } 175 176 @Test getRequestMetadata_initialTokenRefreshed_throws()177 public void getRequestMetadata_initialTokenRefreshed_throws() throws IOException { 178 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 179 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 180 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 181 UserCredentials userCredentials = 182 UserCredentials.newBuilder() 183 .setClientId(CLIENT_ID) 184 .setClientSecret(CLIENT_SECRET) 185 .setAccessToken(accessToken) 186 .setHttpTransportFactory(transportFactory) 187 .build(); 188 189 try { 190 userCredentials.refresh(); 191 fail("Should not be able to refresh without refresh token."); 192 } catch (IllegalStateException expected) { 193 // Expected 194 } 195 } 196 197 @Test getRequestMetadata_fromRefreshToken_hasAccessToken()198 public void getRequestMetadata_fromRefreshToken_hasAccessToken() throws IOException { 199 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 200 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 201 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 202 UserCredentials userCredentials = 203 UserCredentials.newBuilder() 204 .setClientId(CLIENT_ID) 205 .setClientSecret(CLIENT_SECRET) 206 .setRefreshToken(REFRESH_TOKEN) 207 .setHttpTransportFactory(transportFactory) 208 .build(); 209 210 Map<String, List<String>> metadata = userCredentials.getRequestMetadata(CALL_URI); 211 212 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 213 } 214 215 @Test getRequestMetadata_customTokenServer_hasAccessToken()216 public void getRequestMetadata_customTokenServer_hasAccessToken() throws IOException { 217 final URI TOKEN_SERVER = URI.create("https://foo.com/bar"); 218 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 219 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 220 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 221 transportFactory.transport.setTokenServerUri(TOKEN_SERVER); 222 UserCredentials userCredentials = 223 UserCredentials.newBuilder() 224 .setClientId(CLIENT_ID) 225 .setClientSecret(CLIENT_SECRET) 226 .setRefreshToken(REFRESH_TOKEN) 227 .setHttpTransportFactory(transportFactory) 228 .setTokenServerUri(TOKEN_SERVER) 229 .build(); 230 231 Map<String, List<String>> metadata = userCredentials.getRequestMetadata(CALL_URI); 232 233 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 234 } 235 236 @Test equals_true()237 public void equals_true() throws IOException { 238 final URI tokenServer = URI.create("https://foo.com/bar"); 239 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 240 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 241 UserCredentials credentials = 242 UserCredentials.newBuilder() 243 .setClientId(CLIENT_ID) 244 .setClientSecret(CLIENT_SECRET) 245 .setRefreshToken(REFRESH_TOKEN) 246 .setAccessToken(accessToken) 247 .setHttpTransportFactory(transportFactory) 248 .setTokenServerUri(tokenServer) 249 .setQuotaProjectId(QUOTA_PROJECT) 250 .build(); 251 UserCredentials otherCredentials = 252 UserCredentials.newBuilder() 253 .setClientId(CLIENT_ID) 254 .setClientSecret(CLIENT_SECRET) 255 .setRefreshToken(REFRESH_TOKEN) 256 .setAccessToken(accessToken) 257 .setHttpTransportFactory(transportFactory) 258 .setTokenServerUri(tokenServer) 259 .setQuotaProjectId(QUOTA_PROJECT) 260 .build(); 261 assertTrue(credentials.equals(otherCredentials)); 262 assertTrue(otherCredentials.equals(credentials)); 263 } 264 265 @Test equals_false_clientId()266 public void equals_false_clientId() throws IOException { 267 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 268 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 269 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 270 UserCredentials credentials = 271 UserCredentials.newBuilder() 272 .setClientId(CLIENT_ID) 273 .setClientSecret(CLIENT_SECRET) 274 .setRefreshToken(REFRESH_TOKEN) 275 .setAccessToken(accessToken) 276 .setHttpTransportFactory(httpTransportFactory) 277 .setTokenServerUri(tokenServer1) 278 .build(); 279 UserCredentials otherCredentials = 280 UserCredentials.newBuilder() 281 .setClientId("other client id") 282 .setClientSecret(CLIENT_SECRET) 283 .setRefreshToken(REFRESH_TOKEN) 284 .setAccessToken(accessToken) 285 .setHttpTransportFactory(httpTransportFactory) 286 .setTokenServerUri(tokenServer1) 287 .build(); 288 assertFalse(credentials.equals(otherCredentials)); 289 assertFalse(otherCredentials.equals(credentials)); 290 } 291 292 @Test equals_false_clientSecret()293 public void equals_false_clientSecret() throws IOException { 294 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 295 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 296 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 297 UserCredentials credentials = 298 UserCredentials.newBuilder() 299 .setClientId(CLIENT_ID) 300 .setClientSecret(CLIENT_SECRET) 301 .setRefreshToken(REFRESH_TOKEN) 302 .setAccessToken(accessToken) 303 .setHttpTransportFactory(httpTransportFactory) 304 .setTokenServerUri(tokenServer1) 305 .build(); 306 UserCredentials otherCredentials = 307 UserCredentials.newBuilder() 308 .setClientId(CLIENT_ID) 309 .setClientSecret("other client secret") 310 .setRefreshToken(REFRESH_TOKEN) 311 .setAccessToken(accessToken) 312 .setHttpTransportFactory(httpTransportFactory) 313 .setTokenServerUri(tokenServer1) 314 .build(); 315 assertFalse(credentials.equals(otherCredentials)); 316 assertFalse(otherCredentials.equals(credentials)); 317 } 318 319 @Test equals_false_refreshToken()320 public void equals_false_refreshToken() throws IOException { 321 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 322 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 323 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 324 OAuth2Credentials credentials = 325 UserCredentials.newBuilder() 326 .setClientId(CLIENT_ID) 327 .setClientSecret(CLIENT_SECRET) 328 .setRefreshToken(REFRESH_TOKEN) 329 .setAccessToken(accessToken) 330 .setHttpTransportFactory(httpTransportFactory) 331 .setTokenServerUri(tokenServer1) 332 .build(); 333 OAuth2Credentials otherCredentials = 334 UserCredentials.newBuilder() 335 .setClientId(CLIENT_ID) 336 .setClientSecret(CLIENT_SECRET) 337 .setRefreshToken("otherRefreshToken") 338 .setAccessToken(accessToken) 339 .setHttpTransportFactory(httpTransportFactory) 340 .setTokenServerUri(tokenServer1) 341 .build(); 342 assertFalse(credentials.equals(otherCredentials)); 343 assertFalse(otherCredentials.equals(credentials)); 344 } 345 346 @Test equals_false_accessToken()347 public void equals_false_accessToken() throws IOException { 348 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 349 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 350 AccessToken otherAccessToken = new AccessToken("otherAccessToken", null); 351 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 352 UserCredentials credentials = 353 UserCredentials.newBuilder() 354 .setClientId(CLIENT_ID) 355 .setClientSecret(CLIENT_SECRET) 356 .setRefreshToken(REFRESH_TOKEN) 357 .setAccessToken(accessToken) 358 .setHttpTransportFactory(httpTransportFactory) 359 .setTokenServerUri(tokenServer1) 360 .build(); 361 UserCredentials otherCredentials = 362 UserCredentials.newBuilder() 363 .setClientId(CLIENT_ID) 364 .setClientSecret(CLIENT_SECRET) 365 .setRefreshToken(REFRESH_TOKEN) 366 .setAccessToken(otherAccessToken) 367 .setHttpTransportFactory(httpTransportFactory) 368 .setTokenServerUri(tokenServer1) 369 .build(); 370 assertFalse(credentials.equals(otherCredentials)); 371 assertFalse(otherCredentials.equals(credentials)); 372 assertNotEquals(credentials.hashCode(), otherAccessToken.hashCode()); 373 } 374 375 @Test equals_false_transportFactory()376 public void equals_false_transportFactory() throws IOException { 377 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 378 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 379 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 380 MockTokenServerTransportFactory serverTransportFactory = new MockTokenServerTransportFactory(); 381 UserCredentials credentials = 382 UserCredentials.newBuilder() 383 .setClientId(CLIENT_ID) 384 .setClientSecret(CLIENT_SECRET) 385 .setRefreshToken(REFRESH_TOKEN) 386 .setAccessToken(accessToken) 387 .setHttpTransportFactory(httpTransportFactory) 388 .setTokenServerUri(tokenServer1) 389 .build(); 390 UserCredentials otherCredentials = 391 UserCredentials.newBuilder() 392 .setClientId(CLIENT_ID) 393 .setClientSecret(CLIENT_SECRET) 394 .setRefreshToken(REFRESH_TOKEN) 395 .setAccessToken(accessToken) 396 .setHttpTransportFactory(serverTransportFactory) 397 .setTokenServerUri(tokenServer1) 398 .build(); 399 assertFalse(credentials.equals(otherCredentials)); 400 assertFalse(otherCredentials.equals(credentials)); 401 } 402 403 @Test equals_false_tokenServer()404 public void equals_false_tokenServer() throws IOException { 405 final URI tokenServer1 = URI.create("https://foo1.com/bar"); 406 final URI tokenServer2 = URI.create("https://foo2.com/bar"); 407 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 408 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 409 UserCredentials credentials = 410 UserCredentials.newBuilder() 411 .setClientId(CLIENT_ID) 412 .setClientSecret(CLIENT_SECRET) 413 .setRefreshToken(REFRESH_TOKEN) 414 .setAccessToken(accessToken) 415 .setHttpTransportFactory(httpTransportFactory) 416 .setTokenServerUri(tokenServer1) 417 .build(); 418 UserCredentials otherCredentials = 419 UserCredentials.newBuilder() 420 .setClientId(CLIENT_ID) 421 .setClientSecret(CLIENT_SECRET) 422 .setRefreshToken(REFRESH_TOKEN) 423 .setAccessToken(accessToken) 424 .setHttpTransportFactory(httpTransportFactory) 425 .setTokenServerUri(tokenServer2) 426 .build(); 427 assertFalse(credentials.equals(otherCredentials)); 428 assertFalse(otherCredentials.equals(credentials)); 429 } 430 431 @Test equals_false_quotaProjectId()432 public void equals_false_quotaProjectId() throws IOException { 433 final String quotaProject1 = "sample-id-1"; 434 final String quotaProject2 = "sample-id-2"; 435 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 436 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 437 UserCredentials credentials = 438 UserCredentials.newBuilder() 439 .setClientId(CLIENT_ID) 440 .setClientSecret(CLIENT_SECRET) 441 .setRefreshToken(REFRESH_TOKEN) 442 .setAccessToken(accessToken) 443 .setHttpTransportFactory(httpTransportFactory) 444 .setQuotaProjectId(quotaProject1) 445 .build(); 446 UserCredentials otherCredentials = 447 UserCredentials.newBuilder() 448 .setClientId(CLIENT_ID) 449 .setClientSecret(CLIENT_SECRET) 450 .setRefreshToken(REFRESH_TOKEN) 451 .setAccessToken(accessToken) 452 .setHttpTransportFactory(httpTransportFactory) 453 .setQuotaProjectId(quotaProject2) 454 .build(); 455 assertFalse(credentials.equals(otherCredentials)); 456 assertFalse(otherCredentials.equals(credentials)); 457 } 458 459 @Test toString_containsFields()460 public void toString_containsFields() throws IOException { 461 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 462 final URI tokenServer = URI.create("https://foo.com/bar"); 463 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 464 UserCredentials credentials = 465 UserCredentials.newBuilder() 466 .setClientId(CLIENT_ID) 467 .setClientSecret(CLIENT_SECRET) 468 .setRefreshToken(REFRESH_TOKEN) 469 .setAccessToken(accessToken) 470 .setHttpTransportFactory(transportFactory) 471 .setTokenServerUri(tokenServer) 472 .setQuotaProjectId(QUOTA_PROJECT) 473 .build(); 474 475 String expectedToString = 476 String.format( 477 "UserCredentials{requestMetadata=%s, temporaryAccess=%s, clientId=%s, refreshToken=%s, " 478 + "tokenServerUri=%s, transportFactoryClassName=%s, quotaProjectId=%s}", 479 ImmutableMap.of( 480 AuthHttpConstants.AUTHORIZATION, 481 ImmutableList.of(OAuth2Utils.BEARER_PREFIX + accessToken.getTokenValue())), 482 accessToken.toString(), 483 CLIENT_ID, 484 REFRESH_TOKEN, 485 tokenServer, 486 MockTokenServerTransportFactory.class.getName(), 487 QUOTA_PROJECT); 488 assertEquals(expectedToString, credentials.toString()); 489 } 490 491 @Test hashCode_equals()492 public void hashCode_equals() throws IOException { 493 final URI tokenServer = URI.create("https://foo.com/bar"); 494 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 495 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 496 UserCredentials credentials = 497 UserCredentials.newBuilder() 498 .setClientId(CLIENT_ID) 499 .setClientSecret(CLIENT_SECRET) 500 .setRefreshToken(REFRESH_TOKEN) 501 .setAccessToken(accessToken) 502 .setHttpTransportFactory(transportFactory) 503 .setTokenServerUri(tokenServer) 504 .setQuotaProjectId(QUOTA_PROJECT) 505 .build(); 506 UserCredentials otherCredentials = 507 UserCredentials.newBuilder() 508 .setClientId(CLIENT_ID) 509 .setClientSecret(CLIENT_SECRET) 510 .setRefreshToken(REFRESH_TOKEN) 511 .setAccessToken(accessToken) 512 .setHttpTransportFactory(transportFactory) 513 .setTokenServerUri(tokenServer) 514 .setQuotaProjectId(QUOTA_PROJECT) 515 .build(); 516 assertEquals(credentials.hashCode(), otherCredentials.hashCode()); 517 } 518 519 @Test serialize()520 public void serialize() throws IOException, ClassNotFoundException { 521 final URI tokenServer = URI.create("https://foo.com/bar"); 522 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 523 AccessToken accessToken = new AccessToken(ACCESS_TOKEN, null); 524 UserCredentials credentials = 525 UserCredentials.newBuilder() 526 .setClientId(CLIENT_ID) 527 .setClientSecret(CLIENT_SECRET) 528 .setRefreshToken(REFRESH_TOKEN) 529 .setAccessToken(accessToken) 530 .setHttpTransportFactory(transportFactory) 531 .setTokenServerUri(tokenServer) 532 .build(); 533 UserCredentials deserializedCredentials = serializeAndDeserialize(credentials); 534 assertEquals(credentials, deserializedCredentials); 535 assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); 536 assertEquals(credentials.toString(), deserializedCredentials.toString()); 537 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 538 } 539 540 @Test fromStream_nullTransport_throws()541 public void fromStream_nullTransport_throws() throws IOException { 542 InputStream stream = new ByteArrayInputStream("foo".getBytes()); 543 try { 544 UserCredentials.fromStream(stream, null); 545 fail("Should throw if HttpTransportFactory is null"); 546 } catch (NullPointerException expected) { 547 // Expected 548 } 549 } 550 551 @Test fromStream_nullStream_throws()552 public void fromStream_nullStream_throws() throws IOException { 553 MockHttpTransportFactory transportFactory = new MockHttpTransportFactory(); 554 try { 555 UserCredentials.fromStream(null, transportFactory); 556 fail("Should throw if InputStream is null"); 557 } catch (NullPointerException expected) { 558 // Expected 559 } 560 } 561 562 @Test fromStream_user_providesToken()563 public void fromStream_user_providesToken() throws IOException { 564 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 565 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 566 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 567 InputStream userStream = 568 writeUserStream(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); 569 570 UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); 571 572 assertNotNull(credentials); 573 Map<String, List<String>> metadata = credentials.getRequestMetadata(CALL_URI); 574 TestUtils.assertContainsBearerToken(metadata, ACCESS_TOKEN); 575 } 576 577 @Test fromStream_userNoClientId_throws()578 public void fromStream_userNoClientId_throws() throws IOException { 579 InputStream userStream = writeUserStream(null, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); 580 581 testFromStreamException(userStream, "client_id"); 582 } 583 584 @Test fromStream_userNoClientSecret_throws()585 public void fromStream_userNoClientSecret_throws() throws IOException { 586 InputStream userStream = writeUserStream(CLIENT_ID, null, REFRESH_TOKEN, QUOTA_PROJECT); 587 588 testFromStreamException(userStream, "client_secret"); 589 } 590 591 @Test fromStream_userNoRefreshToken_throws()592 public void fromStream_userNoRefreshToken_throws() throws IOException { 593 InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, null, QUOTA_PROJECT); 594 595 testFromStreamException(userStream, "refresh_token"); 596 } 597 598 @Test saveUserCredentials_saved_throws()599 public void saveUserCredentials_saved_throws() throws IOException { 600 UserCredentials userCredentials = 601 UserCredentials.newBuilder() 602 .setClientId(CLIENT_ID) 603 .setClientSecret(CLIENT_SECRET) 604 .setRefreshToken(REFRESH_TOKEN) 605 .build(); 606 File file = File.createTempFile("GOOGLE_APPLICATION_CREDENTIALS", null, null); 607 file.deleteOnExit(); 608 609 String filePath = file.getAbsolutePath(); 610 userCredentials.save(filePath); 611 } 612 613 @Test saveAndRestoreUserCredential_saveAndRestored_throws()614 public void saveAndRestoreUserCredential_saveAndRestored_throws() throws IOException { 615 UserCredentials userCredentials = 616 UserCredentials.newBuilder() 617 .setClientId(CLIENT_ID) 618 .setClientSecret(CLIENT_SECRET) 619 .setRefreshToken(REFRESH_TOKEN) 620 .build(); 621 622 File file = File.createTempFile("GOOGLE_APPLICATION_CREDENTIALS", null, null); 623 file.deleteOnExit(); 624 625 String filePath = file.getAbsolutePath(); 626 627 userCredentials.save(filePath); 628 629 FileInputStream inputStream = new FileInputStream(new File(filePath)); 630 631 UserCredentials restoredCredentials = UserCredentials.fromStream(inputStream); 632 633 assertEquals(userCredentials.getClientId(), restoredCredentials.getClientId()); 634 assertEquals(userCredentials.getClientSecret(), restoredCredentials.getClientSecret()); 635 assertEquals(userCredentials.getRefreshToken(), restoredCredentials.getRefreshToken()); 636 } 637 638 @Test getRequestMetadataSetsQuotaProjectId()639 public void getRequestMetadataSetsQuotaProjectId() throws IOException { 640 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 641 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 642 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 643 644 UserCredentials userCredentials = 645 UserCredentials.newBuilder() 646 .setClientId(CLIENT_ID) 647 .setClientSecret(CLIENT_SECRET) 648 .setRefreshToken(REFRESH_TOKEN) 649 .setQuotaProjectId("my-quota-project-id") 650 .setHttpTransportFactory(transportFactory) 651 .build(); 652 653 Map<String, List<String>> metadata = userCredentials.getRequestMetadata(CALL_URI); 654 assertTrue(metadata.containsKey("x-goog-user-project")); 655 List<String> headerValues = metadata.get("x-goog-user-project"); 656 assertEquals(1, headerValues.size()); 657 assertEquals("my-quota-project-id", headerValues.get(0)); 658 } 659 660 @Test getRequestMetadataNoQuotaProjectId()661 public void getRequestMetadataNoQuotaProjectId() throws IOException { 662 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 663 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 664 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 665 666 UserCredentials userCredentials = 667 UserCredentials.newBuilder() 668 .setClientId(CLIENT_ID) 669 .setClientSecret(CLIENT_SECRET) 670 .setRefreshToken(REFRESH_TOKEN) 671 .setHttpTransportFactory(transportFactory) 672 .build(); 673 674 Map<String, List<String>> metadata = userCredentials.getRequestMetadata(CALL_URI); 675 assertFalse(metadata.containsKey("x-goog-user-project")); 676 } 677 678 @Test getRequestMetadataWithCallback()679 public void getRequestMetadataWithCallback() throws IOException { 680 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 681 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 682 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 683 684 UserCredentials userCredentials = 685 UserCredentials.newBuilder() 686 .setClientId(CLIENT_ID) 687 .setClientSecret(CLIENT_SECRET) 688 .setRefreshToken(REFRESH_TOKEN) 689 .setQuotaProjectId("my-quota-project-id") 690 .setHttpTransportFactory(transportFactory) 691 .build(); 692 final Map<String, List<String>> plainMetadata = userCredentials.getRequestMetadata(CALL_URI); 693 final AtomicBoolean success = new AtomicBoolean(false); 694 userCredentials.getRequestMetadata( 695 null, 696 null, 697 new RequestMetadataCallback() { 698 @Override 699 public void onSuccess(Map<String, List<String>> metadata) { 700 assertEquals(plainMetadata, metadata); 701 success.set(true); 702 } 703 704 @Override 705 public void onFailure(Throwable exception) { 706 fail("Should not throw a failure."); 707 } 708 }); 709 710 assertTrue("Should have run onSuccess() callback", success.get()); 711 } 712 713 @Test IdTokenCredentials_WithUserEmailScope_success()714 public void IdTokenCredentials_WithUserEmailScope_success() throws IOException { 715 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 716 String refreshToken = MockTokenServerTransport.REFRESH_TOKEN_WITH_USER_SCOPE; 717 718 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 719 transportFactory.transport.addRefreshToken(refreshToken, ACCESS_TOKEN); 720 InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, refreshToken, QUOTA_PROJECT); 721 722 UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); 723 credentials.refresh(); 724 725 assertEquals(ACCESS_TOKEN, credentials.getAccessToken().getTokenValue()); 726 727 IdTokenCredentials tokenCredential = 728 IdTokenCredentials.newBuilder().setIdTokenProvider(credentials).build(); 729 730 assertNull(tokenCredential.getAccessToken()); 731 assertNull(tokenCredential.getIdToken()); 732 733 // trigger the refresh like it would happen during a request build 734 tokenCredential.getRequestMetadata(CALL_URI); 735 736 assertEquals(DEFAULT_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); 737 assertEquals(DEFAULT_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); 738 } 739 740 @Test IdTokenCredentials_NoRetry_RetryableStatus_throws()741 public void IdTokenCredentials_NoRetry_RetryableStatus_throws() throws IOException { 742 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 743 String refreshToken = MockTokenServerTransport.REFRESH_TOKEN_WITH_USER_SCOPE; 744 745 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 746 transportFactory.transport.addRefreshToken(refreshToken, ACCESS_TOKEN); 747 InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, refreshToken, QUOTA_PROJECT); 748 749 MockLowLevelHttpResponse response408 = new MockLowLevelHttpResponse().setStatusCode(408); 750 MockLowLevelHttpResponse response429 = new MockLowLevelHttpResponse().setStatusCode(429); 751 752 UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); 753 754 try { 755 transportFactory.transport.addResponseSequence(response408, response429); 756 credentials.refresh(); 757 fail("Should not be able to use credential without exception."); 758 } catch (GoogleAuthException ex) { 759 assertTrue(ex.getMessage().contains("com.google.api.client.http.HttpResponseException: 408")); 760 assertTrue(ex.isRetryable()); 761 assertEquals(0, ex.getRetryCount()); 762 } 763 764 IdTokenCredentials tokenCredential = 765 IdTokenCredentials.newBuilder().setIdTokenProvider(credentials).build(); 766 767 assertNull(tokenCredential.getAccessToken()); 768 assertNull(tokenCredential.getIdToken()); 769 770 // trigger the refresh like it would happen during a request build 771 try { 772 tokenCredential.getRequestMetadata(CALL_URI); 773 fail("Should not be able to use credential without exception."); 774 } catch (GoogleAuthException ex) { 775 assertTrue(ex.getMessage().contains("com.google.api.client.http.HttpResponseException: 429")); 776 assertTrue(ex.isRetryable()); 777 assertEquals(0, ex.getRetryCount()); 778 } 779 } 780 781 @Test refreshAccessToken_4xx_5xx_NonRetryableFails()782 public void refreshAccessToken_4xx_5xx_NonRetryableFails() throws IOException { 783 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 784 String refreshToken = MockTokenServerTransport.REFRESH_TOKEN_WITH_USER_SCOPE; 785 786 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 787 transportFactory.transport.addRefreshToken(refreshToken, ACCESS_TOKEN); 788 InputStream userStream = writeUserStream(CLIENT_ID, CLIENT_SECRET, refreshToken, QUOTA_PROJECT); 789 790 UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); 791 792 for (int status = 400; status < 600; status++) { 793 if (OAuth2Utils.TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES.contains(status)) { 794 continue; 795 } 796 797 MockLowLevelHttpResponse mockResponse = new MockLowLevelHttpResponse().setStatusCode(status); 798 try { 799 transportFactory.transport.addResponseSequence(mockResponse); 800 credentials.refresh(); 801 fail("Should not be able to use credential without exception."); 802 } catch (GoogleAuthException ex) { 803 assertFalse(ex.isRetryable()); 804 assertEquals(0, ex.getRetryCount()); 805 } 806 } 807 } 808 809 @Test IdTokenCredentials_NoUserEmailScope_throws()810 public void IdTokenCredentials_NoUserEmailScope_throws() throws IOException { 811 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 812 transportFactory.transport.addClient(CLIENT_ID, CLIENT_SECRET); 813 transportFactory.transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN); 814 InputStream userStream = 815 writeUserStream(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, QUOTA_PROJECT); 816 817 UserCredentials credentials = UserCredentials.fromStream(userStream, transportFactory); 818 819 IdTokenCredentials tokenCredential = 820 IdTokenCredentials.newBuilder().setIdTokenProvider(credentials).build(); 821 822 String expectedMessageContent = 823 "UserCredentials can obtain an id token only when authenticated through" 824 + " gcloud running 'gcloud auth login --update-adc' or 'gcloud auth application-default" 825 + " login'. The latter form would not work for Cloud Run, but would still generate an" 826 + " id token."; 827 828 try { 829 tokenCredential.refresh(); 830 fail("Should not be able to use credential without exception."); 831 } catch (IOException expected) { 832 assertTrue(expected.getMessage().equals(expectedMessageContent)); 833 } 834 } 835 836 @Test userCredentials_toBuilder_copyEveryAttribute()837 public void userCredentials_toBuilder_copyEveryAttribute() { 838 MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); 839 UserCredentials credentials = 840 UserCredentials.newBuilder() 841 .setClientId(CLIENT_ID) 842 .setClientSecret(CLIENT_SECRET) 843 .setRefreshToken(REFRESH_TOKEN) 844 .setAccessToken(new AccessToken(ACCESS_TOKEN, new Date())) 845 .setHttpTransportFactory(httpTransportFactory) 846 .setTokenServerUri(URI.create("https://foo1.com/bar")) 847 .setQuotaProjectId(QUOTA_PROJECT) 848 .setExpirationMargin(Duration.of(10, ChronoUnit.SECONDS)) 849 .setRefreshMargin(Duration.of(12, ChronoUnit.MINUTES)) 850 .build(); 851 852 UserCredentials otherCredentials = credentials.toBuilder().build(); 853 assertEquals(credentials, otherCredentials); 854 } 855 writeUserJson( String clientId, String clientSecret, String refreshToken, String quotaProjectId)856 static GenericJson writeUserJson( 857 String clientId, String clientSecret, String refreshToken, String quotaProjectId) { 858 GenericJson json = new GenericJson(); 859 if (clientId != null) { 860 json.put("client_id", clientId); 861 } 862 if (clientSecret != null) { 863 json.put("client_secret", clientSecret); 864 } 865 if (refreshToken != null) { 866 json.put("refresh_token", refreshToken); 867 } 868 if (quotaProjectId != null) { 869 json.put("quota_project_id", quotaProjectId); 870 } 871 json.put("type", GoogleCredentials.USER_FILE_TYPE); 872 return json; 873 } 874 writeUserStream( String clientId, String clientSecret, String refreshToken, String quotaProjectId)875 static InputStream writeUserStream( 876 String clientId, String clientSecret, String refreshToken, String quotaProjectId) 877 throws IOException { 878 GenericJson json = writeUserJson(clientId, clientSecret, refreshToken, quotaProjectId); 879 return TestUtils.jsonToInputStream(json); 880 } 881 testFromStreamException(InputStream stream, String expectedMessageContent)882 private static void testFromStreamException(InputStream stream, String expectedMessageContent) { 883 try { 884 UserCredentials.fromStream(stream); 885 fail( 886 String.format( 887 "Should throw exception with message containing '%s'", expectedMessageContent)); 888 } catch (IOException expected) { 889 assertTrue(expected.getMessage().contains(expectedMessageContent)); 890 } 891 } 892 } 893