1 /* 2 * Copyright 2015, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import static org.junit.Assert.assertArrayEquals; 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertFalse; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNull; 39 import static org.junit.Assert.assertSame; 40 import static org.junit.Assert.assertTrue; 41 import static org.junit.Assert.fail; 42 43 import com.google.api.client.http.HttpStatusCodes; 44 import com.google.api.client.http.HttpTransport; 45 import com.google.api.client.json.GenericJson; 46 import com.google.api.client.json.JsonFactory; 47 import com.google.api.client.json.JsonGenerator; 48 import com.google.api.client.json.gson.GsonFactory; 49 import com.google.api.client.json.webtoken.JsonWebToken.Payload; 50 import com.google.api.client.testing.http.MockLowLevelHttpRequest; 51 import com.google.api.client.util.Clock; 52 import com.google.auth.ServiceAccountSigner.SigningException; 53 import com.google.auth.TestUtils; 54 import com.google.auth.http.HttpTransportFactory; 55 import com.google.common.collect.ImmutableList; 56 import com.google.common.collect.ImmutableSet; 57 import java.io.ByteArrayOutputStream; 58 import java.io.IOException; 59 import java.io.InputStream; 60 import java.nio.charset.Charset; 61 import java.security.PrivateKey; 62 import java.text.DateFormat; 63 import java.text.SimpleDateFormat; 64 import java.time.temporal.ChronoUnit; 65 import java.util.ArrayList; 66 import java.util.Arrays; 67 import java.util.Calendar; 68 import java.util.Date; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.TimeZone; 72 import org.junit.Before; 73 import org.junit.Test; 74 import org.junit.runner.RunWith; 75 import org.junit.runners.JUnit4; 76 77 /** Test case for {@link ImpersonatedCredentials}. */ 78 @RunWith(JUnit4.class) 79 public class ImpersonatedCredentialsTest extends BaseSerializationTest { 80 81 public static final String SA_CLIENT_EMAIL = 82 "36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com"; 83 private static final String SA_PRIVATE_KEY_ID = "d84a4fefcf50791d4a90f2d7af17469d6282df9d"; 84 static final String SA_PRIVATE_KEY_PKCS8 = 85 "-----BEGIN PRIVATE KEY-----\n" 86 + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALX0PQoe1igW12i" 87 + "kv1bN/r9lN749y2ijmbc/mFHPyS3hNTyOCjDvBbXYbDhQJzWVUikh4mvGBA07qTj79Xc3yBDfKP2IeyYQIFe0t0" 88 + "zkd7R9Zdn98Y2rIQC47aAbDfubtkU1U72t4zL11kHvoa0/RuFZjncvlr42X7be7lYh4p3NAgMBAAECgYASk5wDw" 89 + "4Az2ZkmeuN6Fk/y9H+Lcb2pskJIXjrL533vrDWGOC48LrsThMQPv8cxBky8HFSEklPpkfTF95tpD43iVwJRB/Gr" 90 + "CtGTw65IfJ4/tI09h6zGc4yqvIo1cHX/LQ+SxKLGyir/dQM925rGt/VojxY5ryJR7GLbCzxPnJm/oQJBANwOCO6" 91 + "D2hy1LQYJhXh7O+RLtA/tSnT1xyMQsGT+uUCMiKS2bSKx2wxo9k7h3OegNJIu1q6nZ6AbxDK8H3+d0dUCQQDTrP" 92 + "SXagBxzp8PecbaCHjzNRSQE2in81qYnrAFNB4o3DpHyMMY6s5ALLeHKscEWnqP8Ur6X4PvzZecCWU9BKAZAkAut" 93 + "LPknAuxSCsUOvUfS1i87ex77Ot+w6POp34pEX+UWb+u5iFn2cQacDTHLV1LtE80L8jVLSbrbrlH43H0DjU5AkEA" 94 + "gidhycxS86dxpEljnOMCw8CKoUBd5I880IUahEiUltk7OLJYS/Ts1wbn3kPOVX3wyJs8WBDtBkFrDHW2ezth2QJ" 95 + "ADj3e1YhMVdjJW5jqwlD/VNddGjgzyunmiZg0uOXsHXbytYmsA545S8KRQFaJKFXYYFo2kOjqOiC1T2cAzMDjCQ" 96 + "==\n-----END PRIVATE KEY-----\n"; 97 98 // Id Token provided by the default IAM API that does not include the "email" claim 99 public static final String STANDARD_ID_TOKEN = 100 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIy" 101 + "OTNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIi" 102 + "LCJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ1MzI5NzIsImlhdCI6MTU2NDUyOTM3Miw" 103 + "iaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In" 104 + "0.redacted"; 105 106 // Id Token provided by the default IAM API that includes the "email" claim 107 public static final String TOKEN_WITH_EMAIL = 108 "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIy" 109 + "OTNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIi" 110 + "LCJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJlbWFpbCI6ImltcGVyc29uYXRlZC1hY2NvdW50QGZhYmx" 111 + "lZC1yYXktMTA0MTE3LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cC" 112 + "I6MTU2NDUzMzA0MiwiaWF0IjoxNTY0NTI5NDQyLCJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iL" 113 + "CJzdWIiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgifQ.redacted"; 114 public static final String ACCESS_TOKEN = "1/MkSJoj1xsli0AccessToken_NKPY2"; 115 116 private static final ImmutableSet<String> IMMUTABLE_SCOPES_SET = 117 ImmutableSet.of("scope1", "scope2"); 118 private static final String PROJECT_ID = "project-id"; 119 public static final String IMPERSONATED_CLIENT_EMAIL = 120 "impersonated-account@iam.gserviceaccount.com"; 121 private static final List<String> IMMUTABLE_SCOPES_LIST = ImmutableList.of("scope1", "scope2"); 122 private static final int VALID_LIFETIME = 300; 123 private static final int INVALID_LIFETIME = 43210; 124 private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); 125 126 private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssX"; 127 public static final String DEFAULT_IMPERSONATION_URL = 128 "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" 129 + IMPERSONATED_CLIENT_EMAIL 130 + ":generateAccessToken"; 131 public static final String IMPERSONATION_URL = 132 "https://us-east1-iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" 133 + IMPERSONATED_CLIENT_EMAIL 134 + ":generateAccessToken"; 135 private static final String USER_ACCOUNT_CLIENT_ID = 136 "76408650-6qr441hur.apps.googleusercontent.com"; 137 private static final String USER_ACCOUNT_CLIENT_SECRET = "d-F499q74hFpdHD0T5"; 138 public static final String QUOTA_PROJECT_ID = "quota-project-id"; 139 private static final String REFRESH_TOKEN = "dasdfasdffa4ffdfadgyjirasdfadsft"; 140 public static final List<String> DELEGATES = 141 Arrays.asList("sa1@developer.gserviceaccount.com", "sa2@developer.gserviceaccount.com"); 142 143 static class MockIAMCredentialsServiceTransportFactory implements HttpTransportFactory { 144 145 MockIAMCredentialsServiceTransport transport = new MockIAMCredentialsServiceTransport(); 146 147 @Override create()148 public HttpTransport create() { 149 return transport; 150 } 151 } 152 153 private GoogleCredentials sourceCredentials; 154 private MockIAMCredentialsServiceTransportFactory mockTransportFactory; 155 156 @Before setup()157 public void setup() throws IOException { 158 sourceCredentials = getSourceCredentials(); 159 mockTransportFactory = new MockIAMCredentialsServiceTransportFactory(); 160 } 161 getSourceCredentials()162 private GoogleCredentials getSourceCredentials() throws IOException { 163 MockTokenServerTransportFactory transportFactory = new MockTokenServerTransportFactory(); 164 PrivateKey privateKey = OAuth2Utils.privateKeyFromPkcs8(SA_PRIVATE_KEY_PKCS8); 165 ServiceAccountCredentials sourceCredentials = 166 ServiceAccountCredentials.newBuilder() 167 .setClientEmail(SA_CLIENT_EMAIL) 168 .setPrivateKey(privateKey) 169 .setPrivateKeyId(SA_PRIVATE_KEY_ID) 170 .setScopes(IMMUTABLE_SCOPES_LIST) 171 .setProjectId(PROJECT_ID) 172 .setHttpTransportFactory(transportFactory) 173 .build(); 174 transportFactory.transport.addServiceAccount(SA_CLIENT_EMAIL, ACCESS_TOKEN); 175 176 return sourceCredentials; 177 } 178 179 @Test() fromJson_userAsSource_WithQuotaProjectId()180 public void fromJson_userAsSource_WithQuotaProjectId() throws IOException { 181 GenericJson json = 182 buildImpersonationCredentialsJson( 183 IMPERSONATION_URL, 184 DELEGATES, 185 QUOTA_PROJECT_ID, 186 USER_ACCOUNT_CLIENT_ID, 187 USER_ACCOUNT_CLIENT_SECRET, 188 REFRESH_TOKEN); 189 ImpersonatedCredentials credentials = 190 ImpersonatedCredentials.fromJson(json, mockTransportFactory); 191 assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); 192 assertEquals(IMPERSONATION_URL, credentials.getIamEndpointOverride()); 193 assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); 194 assertEquals(DELEGATES, credentials.getDelegates()); 195 assertEquals(new ArrayList<String>(), credentials.getScopes()); 196 assertEquals(3600, credentials.getLifetime()); 197 GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); 198 assertTrue(sourceCredentials instanceof UserCredentials); 199 } 200 201 @Test() fromJson_userAsSource_WithoutQuotaProjectId()202 public void fromJson_userAsSource_WithoutQuotaProjectId() throws IOException { 203 GenericJson json = 204 buildImpersonationCredentialsJson( 205 IMPERSONATION_URL, 206 DELEGATES, 207 null, 208 USER_ACCOUNT_CLIENT_ID, 209 USER_ACCOUNT_CLIENT_SECRET, 210 REFRESH_TOKEN); 211 ImpersonatedCredentials credentials = 212 ImpersonatedCredentials.fromJson(json, mockTransportFactory); 213 assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); 214 assertEquals(IMPERSONATION_URL, credentials.getIamEndpointOverride()); 215 assertNull(credentials.getQuotaProjectId()); 216 assertEquals(DELEGATES, credentials.getDelegates()); 217 assertEquals(new ArrayList<String>(), credentials.getScopes()); 218 assertEquals(3600, credentials.getLifetime()); 219 GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); 220 assertTrue(sourceCredentials instanceof UserCredentials); 221 } 222 223 @Test() fromJson_userAsSource_MissingDelegatesField()224 public void fromJson_userAsSource_MissingDelegatesField() throws IOException { 225 GenericJson json = 226 buildImpersonationCredentialsJson( 227 IMPERSONATION_URL, 228 DELEGATES, 229 null, 230 USER_ACCOUNT_CLIENT_ID, 231 USER_ACCOUNT_CLIENT_SECRET, 232 REFRESH_TOKEN); 233 json.remove("delegates"); 234 ImpersonatedCredentials credentials = 235 ImpersonatedCredentials.fromJson(json, mockTransportFactory); 236 assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); 237 assertEquals(IMPERSONATION_URL, credentials.getIamEndpointOverride()); 238 assertNull(credentials.getQuotaProjectId()); 239 assertEquals(new ArrayList<String>(), credentials.getDelegates()); 240 assertEquals(new ArrayList<String>(), credentials.getScopes()); 241 assertEquals(3600, credentials.getLifetime()); 242 GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); 243 assertTrue(sourceCredentials instanceof UserCredentials); 244 } 245 246 @Test() fromJson_ServiceAccountAsSource()247 public void fromJson_ServiceAccountAsSource() throws IOException { 248 GenericJson json = 249 buildImpersonationCredentialsJson(IMPERSONATION_URL, DELEGATES, QUOTA_PROJECT_ID); 250 ImpersonatedCredentials credentials = 251 ImpersonatedCredentials.fromJson(json, mockTransportFactory); 252 assertEquals(IMPERSONATED_CLIENT_EMAIL, credentials.getAccount()); 253 assertEquals(IMPERSONATION_URL, credentials.getIamEndpointOverride()); 254 assertEquals(QUOTA_PROJECT_ID, credentials.getQuotaProjectId()); 255 assertEquals(DELEGATES, credentials.getDelegates()); 256 assertEquals(new ArrayList<String>(), credentials.getScopes()); 257 assertEquals(3600, credentials.getLifetime()); 258 GoogleCredentials sourceCredentials = credentials.getSourceCredentials(); 259 assertTrue(sourceCredentials instanceof ServiceAccountCredentials); 260 } 261 262 @Test() fromJson_InvalidFormat()263 public void fromJson_InvalidFormat() throws IOException { 264 GenericJson json = buildInvalidCredentialsJson(); 265 try { 266 ImpersonatedCredentials.fromJson(json, mockTransportFactory); 267 fail("An exception should be thrown."); 268 } catch (CredentialFormatException e) { 269 assertEquals("An invalid input stream was provided.", e.getMessage()); 270 } 271 } 272 273 @Test() createScopedRequired_True()274 public void createScopedRequired_True() { 275 ImpersonatedCredentials targetCredentials = 276 ImpersonatedCredentials.create( 277 sourceCredentials, 278 IMPERSONATED_CLIENT_EMAIL, 279 null, 280 new ArrayList<String>(), 281 VALID_LIFETIME, 282 mockTransportFactory); 283 assertTrue(targetCredentials.createScopedRequired()); 284 } 285 286 @Test() createScopedRequired_False()287 public void createScopedRequired_False() { 288 ImpersonatedCredentials targetCredentials = 289 ImpersonatedCredentials.create( 290 sourceCredentials, 291 IMPERSONATED_CLIENT_EMAIL, 292 null, 293 IMMUTABLE_SCOPES_LIST, 294 VALID_LIFETIME, 295 mockTransportFactory); 296 assertFalse(targetCredentials.createScopedRequired()); 297 } 298 299 @Test createScoped()300 public void createScoped() { 301 ImpersonatedCredentials targetCredentials = 302 ImpersonatedCredentials.create( 303 sourceCredentials, 304 IMPERSONATED_CLIENT_EMAIL, 305 DELEGATES, 306 IMMUTABLE_SCOPES_LIST, 307 VALID_LIFETIME, 308 mockTransportFactory, 309 QUOTA_PROJECT_ID); 310 311 ImpersonatedCredentials scoped_credentials = 312 (ImpersonatedCredentials) targetCredentials.createScoped(IMMUTABLE_SCOPES_LIST); 313 assertEquals(targetCredentials.getAccount(), scoped_credentials.getAccount()); 314 assertEquals(targetCredentials.getDelegates(), scoped_credentials.getDelegates()); 315 assertEquals(targetCredentials.getLifetime(), scoped_credentials.getLifetime()); 316 assertEquals( 317 targetCredentials.getSourceCredentials(), scoped_credentials.getSourceCredentials()); 318 assertEquals(targetCredentials.getQuotaProjectId(), scoped_credentials.getQuotaProjectId()); 319 assertEquals(Arrays.asList("scope1", "scope2"), scoped_credentials.getScopes()); 320 } 321 322 @Test createScopedWithImmutableScopes()323 public void createScopedWithImmutableScopes() { 324 ImpersonatedCredentials targetCredentials = 325 ImpersonatedCredentials.create( 326 sourceCredentials, 327 IMPERSONATED_CLIENT_EMAIL, 328 DELEGATES, 329 IMMUTABLE_SCOPES_LIST, 330 VALID_LIFETIME, 331 mockTransportFactory, 332 QUOTA_PROJECT_ID); 333 334 ImpersonatedCredentials scoped_credentials = 335 (ImpersonatedCredentials) targetCredentials.createScoped(IMMUTABLE_SCOPES_SET); 336 assertEquals(targetCredentials.getAccount(), scoped_credentials.getAccount()); 337 assertEquals(targetCredentials.getDelegates(), scoped_credentials.getDelegates()); 338 assertEquals(targetCredentials.getLifetime(), scoped_credentials.getLifetime()); 339 assertEquals( 340 targetCredentials.getSourceCredentials(), scoped_credentials.getSourceCredentials()); 341 assertEquals(targetCredentials.getQuotaProjectId(), scoped_credentials.getQuotaProjectId()); 342 assertEquals(Arrays.asList("scope1", "scope2"), scoped_credentials.getScopes()); 343 } 344 345 @Test createScopedWithIamEndpointOverride()346 public void createScopedWithIamEndpointOverride() { 347 ImpersonatedCredentials targetCredentials = 348 ImpersonatedCredentials.create( 349 sourceCredentials, 350 IMPERSONATED_CLIENT_EMAIL, 351 DELEGATES, 352 IMMUTABLE_SCOPES_LIST, 353 VALID_LIFETIME, 354 mockTransportFactory, 355 QUOTA_PROJECT_ID, 356 IMPERSONATION_URL); 357 358 ImpersonatedCredentials scoped_credentials = 359 (ImpersonatedCredentials) targetCredentials.createScoped(IMMUTABLE_SCOPES_SET); 360 assertEquals( 361 targetCredentials.getIamEndpointOverride(), scoped_credentials.getIamEndpointOverride()); 362 } 363 364 @Test refreshAccessToken_unauthorized()365 public void refreshAccessToken_unauthorized() throws IOException { 366 367 String expectedMessage = "The caller does not have permission"; 368 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 369 mockTransportFactory.transport.setTokenResponseErrorCode( 370 HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); 371 mockTransportFactory.transport.setTokenResponseErrorContent( 372 generateErrorJson( 373 HttpStatusCodes.STATUS_CODE_UNAUTHORIZED, expectedMessage, "global", "forbidden")); 374 ImpersonatedCredentials targetCredentials = 375 ImpersonatedCredentials.create( 376 sourceCredentials, 377 IMPERSONATED_CLIENT_EMAIL, 378 null, 379 IMMUTABLE_SCOPES_LIST, 380 VALID_LIFETIME, 381 mockTransportFactory); 382 383 try { 384 targetCredentials.refreshAccessToken().getTokenValue(); 385 fail(String.format("Should throw exception with message containing '%s'", expectedMessage)); 386 } catch (IOException expected) { 387 assertEquals("Error requesting access token", expected.getMessage()); 388 assertTrue(expected.getCause().getMessage().contains(expectedMessage)); 389 } 390 } 391 392 @Test() refreshAccessToken_malformedTarget()393 public void refreshAccessToken_malformedTarget() throws IOException { 394 395 String invalidTargetEmail = "foo"; 396 String expectedMessage = "Request contains an invalid argument"; 397 mockTransportFactory.transport.setTargetPrincipal(invalidTargetEmail); 398 mockTransportFactory.transport.setTokenResponseErrorCode( 399 HttpStatusCodes.STATUS_CODE_BAD_REQUEST); 400 mockTransportFactory.transport.setTokenResponseErrorContent( 401 generateErrorJson( 402 HttpStatusCodes.STATUS_CODE_BAD_REQUEST, expectedMessage, "global", "badRequest")); 403 ImpersonatedCredentials targetCredentials = 404 ImpersonatedCredentials.create( 405 sourceCredentials, 406 invalidTargetEmail, 407 null, 408 IMMUTABLE_SCOPES_LIST, 409 VALID_LIFETIME, 410 mockTransportFactory); 411 412 try { 413 targetCredentials.refreshAccessToken().getTokenValue(); 414 fail(String.format("Should throw exception with message containing '%s'", expectedMessage)); 415 } catch (IOException expected) { 416 assertEquals("Error requesting access token", expected.getMessage()); 417 assertTrue(expected.getCause().getMessage().contains(expectedMessage)); 418 } 419 } 420 421 @Test() credential_with_zero_lifetime()422 public void credential_with_zero_lifetime() throws IllegalStateException { 423 ImpersonatedCredentials targetCredentials = 424 ImpersonatedCredentials.create( 425 sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, IMMUTABLE_SCOPES_LIST, 0); 426 assertEquals(3600, targetCredentials.getLifetime()); 427 } 428 429 @Test() credential_with_invalid_lifetime()430 public void credential_with_invalid_lifetime() throws IOException, IllegalStateException { 431 432 try { 433 ImpersonatedCredentials targetCredentials = 434 ImpersonatedCredentials.create( 435 sourceCredentials, 436 IMPERSONATED_CLIENT_EMAIL, 437 null, 438 IMMUTABLE_SCOPES_LIST, 439 INVALID_LIFETIME); 440 targetCredentials.refreshAccessToken().getTokenValue(); 441 fail( 442 String.format( 443 "Should throw exception with message containing '%s'", 444 "lifetime must be less than or equal to 43200")); 445 } catch (IllegalStateException expected) { 446 assertTrue(expected.getMessage().contains("lifetime must be less than or equal to 43200")); 447 } 448 } 449 450 @Test() credential_with_invalid_scope()451 public void credential_with_invalid_scope() throws IOException, IllegalStateException { 452 453 try { 454 ImpersonatedCredentials targetCredentials = 455 ImpersonatedCredentials.create( 456 sourceCredentials, IMPERSONATED_CLIENT_EMAIL, null, null, VALID_LIFETIME); 457 targetCredentials.refreshAccessToken().getTokenValue(); 458 fail( 459 String.format( 460 "Should throw exception with message containing '%s'", "Scopes cannot be null")); 461 } catch (IllegalStateException expected) { 462 assertTrue(expected.getMessage().contains("Scopes cannot be null")); 463 } 464 } 465 466 @Test() refreshAccessToken_success()467 public void refreshAccessToken_success() throws IOException, IllegalStateException { 468 469 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 470 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 471 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 472 ImpersonatedCredentials targetCredentials = 473 ImpersonatedCredentials.create( 474 sourceCredentials, 475 IMPERSONATED_CLIENT_EMAIL, 476 null, 477 IMMUTABLE_SCOPES_LIST, 478 VALID_LIFETIME, 479 mockTransportFactory); 480 481 assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); 482 assertEquals(DEFAULT_IMPERSONATION_URL, mockTransportFactory.transport.getRequest().getUrl()); 483 } 484 485 @Test refreshAccessToken_endpointOverride()486 public void refreshAccessToken_endpointOverride() throws IOException, IllegalStateException { 487 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 488 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 489 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 490 mockTransportFactory.transport.setAccessTokenEndpoint(IMPERSONATION_URL); 491 492 ImpersonatedCredentials targetCredentials = 493 ImpersonatedCredentials.create( 494 sourceCredentials, 495 IMPERSONATED_CLIENT_EMAIL, 496 null, 497 IMMUTABLE_SCOPES_LIST, 498 VALID_LIFETIME, 499 mockTransportFactory, 500 QUOTA_PROJECT_ID, 501 IMPERSONATION_URL); 502 503 assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); 504 assertEquals(IMPERSONATION_URL, mockTransportFactory.transport.getRequest().getUrl()); 505 } 506 507 @Test() getRequestMetadata_withQuotaProjectId()508 public void getRequestMetadata_withQuotaProjectId() throws IOException, IllegalStateException { 509 510 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 511 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 512 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 513 ImpersonatedCredentials targetCredentials = 514 ImpersonatedCredentials.create( 515 sourceCredentials, 516 IMPERSONATED_CLIENT_EMAIL, 517 null, 518 IMMUTABLE_SCOPES_LIST, 519 VALID_LIFETIME, 520 mockTransportFactory, 521 QUOTA_PROJECT_ID); 522 523 Map<String, List<String>> metadata = targetCredentials.getRequestMetadata(); 524 assertTrue(metadata.containsKey("x-goog-user-project")); 525 List<String> headerValues = metadata.get("x-goog-user-project"); 526 assertEquals(1, headerValues.size()); 527 assertEquals(QUOTA_PROJECT_ID, headerValues.get(0)); 528 } 529 530 @Test() getRequestMetadata_withoutQuotaProjectId()531 public void getRequestMetadata_withoutQuotaProjectId() throws IOException, IllegalStateException { 532 533 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 534 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 535 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 536 ImpersonatedCredentials targetCredentials = 537 ImpersonatedCredentials.create( 538 sourceCredentials, 539 IMPERSONATED_CLIENT_EMAIL, 540 null, 541 IMMUTABLE_SCOPES_LIST, 542 VALID_LIFETIME, 543 mockTransportFactory); 544 545 Map<String, List<String>> metadata = targetCredentials.getRequestMetadata(); 546 assertFalse(metadata.containsKey("x-goog-user-project")); 547 } 548 549 @Test() refreshAccessToken_delegates_success()550 public void refreshAccessToken_delegates_success() throws IOException, IllegalStateException { 551 552 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 553 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 554 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 555 List<String> delegates = Arrays.asList("delegate-account@iam.gserviceaccount.com"); 556 ImpersonatedCredentials targetCredentials = 557 ImpersonatedCredentials.create( 558 sourceCredentials, 559 IMPERSONATED_CLIENT_EMAIL, 560 delegates, 561 IMMUTABLE_SCOPES_LIST, 562 VALID_LIFETIME, 563 mockTransportFactory); 564 565 assertEquals(ACCESS_TOKEN, targetCredentials.refreshAccessToken().getTokenValue()); 566 } 567 568 @Test refreshAccessToken_GMT_dateParsedCorrectly()569 public void refreshAccessToken_GMT_dateParsedCorrectly() 570 throws IOException, IllegalStateException { 571 Calendar c = Calendar.getInstance(); 572 c.add(Calendar.SECOND, VALID_LIFETIME); 573 574 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 575 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 576 mockTransportFactory.transport.setExpireTime(getFormattedTime(c.getTime())); 577 ImpersonatedCredentials targetCredentials = 578 ImpersonatedCredentials.create( 579 sourceCredentials, 580 IMPERSONATED_CLIENT_EMAIL, 581 null, 582 IMMUTABLE_SCOPES_LIST, 583 VALID_LIFETIME, 584 mockTransportFactory) 585 .createWithCustomCalendar( 586 // Set system timezone to GMT 587 Calendar.getInstance(TimeZone.getTimeZone("GMT"))); 588 589 assertTrue( 590 c.getTime().toInstant().truncatedTo(ChronoUnit.SECONDS).toEpochMilli() 591 == targetCredentials.refreshAccessToken().getExpirationTimeMillis()); 592 } 593 594 @Test refreshAccessToken_nonGMT_dateParsedCorrectly()595 public void refreshAccessToken_nonGMT_dateParsedCorrectly() 596 throws IOException, IllegalStateException { 597 Calendar c = Calendar.getInstance(); 598 c.add(Calendar.SECOND, VALID_LIFETIME); 599 600 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 601 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 602 mockTransportFactory.transport.setExpireTime(getFormattedTime(c.getTime())); 603 ImpersonatedCredentials targetCredentials = 604 ImpersonatedCredentials.create( 605 sourceCredentials, 606 IMPERSONATED_CLIENT_EMAIL, 607 null, 608 IMMUTABLE_SCOPES_LIST, 609 VALID_LIFETIME, 610 mockTransportFactory) 611 .createWithCustomCalendar( 612 // Set system timezone to one different than GMT 613 Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); 614 615 assertTrue( 616 c.getTime().toInstant().truncatedTo(ChronoUnit.SECONDS).toEpochMilli() 617 == targetCredentials.refreshAccessToken().getExpirationTimeMillis()); 618 } 619 620 @Test refreshAccessToken_invalidDate()621 public void refreshAccessToken_invalidDate() throws IllegalStateException { 622 623 String expectedMessage = "Unparseable date"; 624 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 625 mockTransportFactory.transport.setAccessToken("foo"); 626 mockTransportFactory.transport.setExpireTime("1973-09-29T15:01:23"); 627 ImpersonatedCredentials targetCredentials = 628 ImpersonatedCredentials.create( 629 sourceCredentials, 630 IMPERSONATED_CLIENT_EMAIL, 631 null, 632 IMMUTABLE_SCOPES_LIST, 633 VALID_LIFETIME, 634 mockTransportFactory); 635 636 try { 637 targetCredentials.refreshAccessToken().getTokenValue(); 638 fail(String.format("Should throw exception with message containing '%s'", expectedMessage)); 639 } catch (IOException expected) { 640 assertTrue(expected.getMessage().contains(expectedMessage)); 641 } 642 } 643 644 @Test getAccount_sameAs()645 public void getAccount_sameAs() { 646 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 647 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 648 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 649 ImpersonatedCredentials targetCredentials = 650 ImpersonatedCredentials.create( 651 sourceCredentials, 652 IMPERSONATED_CLIENT_EMAIL, 653 null, 654 IMMUTABLE_SCOPES_LIST, 655 VALID_LIFETIME, 656 mockTransportFactory); 657 658 assertEquals(IMPERSONATED_CLIENT_EMAIL, targetCredentials.getAccount()); 659 } 660 661 @Test sign_sameAs()662 public void sign_sameAs() { 663 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 664 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 665 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 666 ImpersonatedCredentials targetCredentials = 667 ImpersonatedCredentials.create( 668 sourceCredentials, 669 IMPERSONATED_CLIENT_EMAIL, 670 null, 671 IMMUTABLE_SCOPES_LIST, 672 VALID_LIFETIME, 673 mockTransportFactory); 674 675 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 676 677 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 678 mockTransportFactory.transport.setSignedBlob(expectedSignature); 679 680 assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); 681 } 682 683 @Test sign_requestIncludesDelegates()684 public void sign_requestIncludesDelegates() throws IOException { 685 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 686 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 687 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 688 ImpersonatedCredentials targetCredentials = 689 ImpersonatedCredentials.create( 690 sourceCredentials, 691 IMPERSONATED_CLIENT_EMAIL, 692 ImmutableList.of("delegate@example.com"), 693 IMMUTABLE_SCOPES_LIST, 694 VALID_LIFETIME, 695 mockTransportFactory); 696 697 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 698 699 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 700 mockTransportFactory.transport.setSignedBlob(expectedSignature); 701 702 assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); 703 704 MockLowLevelHttpRequest request = mockTransportFactory.transport.getRequest(); 705 GenericJson body = 706 JSON_FACTORY 707 .createJsonParser(request.getContentAsString()) 708 .parseAndClose(GenericJson.class); 709 List<String> delegates = new ArrayList<>(); 710 delegates.add("delegate@example.com"); 711 assertEquals(delegates, body.get("delegates")); 712 } 713 714 @Test sign_usesSourceCredentials()715 public void sign_usesSourceCredentials() { 716 Calendar c = Calendar.getInstance(); 717 c.add(Calendar.DATE, 1); 718 Date expiry = c.getTime(); 719 GoogleCredentials sourceCredentials = 720 new GoogleCredentials.Builder() 721 .setAccessToken(new AccessToken("source-token", expiry)) 722 .build(); 723 724 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 725 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 726 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 727 ImpersonatedCredentials targetCredentials = 728 ImpersonatedCredentials.create( 729 sourceCredentials, 730 IMPERSONATED_CLIENT_EMAIL, 731 ImmutableList.of("delegate@example.com"), 732 IMMUTABLE_SCOPES_LIST, 733 VALID_LIFETIME, 734 mockTransportFactory); 735 736 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 737 738 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 739 mockTransportFactory.transport.setSignedBlob(expectedSignature); 740 741 assertArrayEquals(expectedSignature, targetCredentials.sign(expectedSignature)); 742 743 MockLowLevelHttpRequest request = mockTransportFactory.transport.getRequest(); 744 assertEquals("Bearer source-token", request.getFirstHeaderValue("Authorization")); 745 } 746 747 @Test sign_accessDenied_throws()748 public void sign_accessDenied_throws() { 749 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 750 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 751 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 752 ImpersonatedCredentials targetCredentials = 753 ImpersonatedCredentials.create( 754 sourceCredentials, 755 IMPERSONATED_CLIENT_EMAIL, 756 null, 757 IMMUTABLE_SCOPES_LIST, 758 VALID_LIFETIME, 759 mockTransportFactory); 760 761 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 762 763 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 764 mockTransportFactory.transport.setSignedBlob(expectedSignature); 765 mockTransportFactory.transport.setErrorResponseCodeAndMessage( 766 HttpStatusCodes.STATUS_CODE_FORBIDDEN, "Sign Error"); 767 768 try { 769 byte[] bytes = {0xD, 0xE, 0xA, 0xD}; 770 targetCredentials.sign(bytes); 771 fail("Signing should have failed"); 772 } catch (SigningException e) { 773 assertEquals("Failed to sign the provided bytes", e.getMessage()); 774 assertNotNull(e.getCause()); 775 assertTrue(e.getCause().getMessage().contains("403")); 776 } 777 } 778 779 @Test sign_serverError_throws()780 public void sign_serverError_throws() { 781 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 782 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 783 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 784 ImpersonatedCredentials targetCredentials = 785 ImpersonatedCredentials.create( 786 sourceCredentials, 787 IMPERSONATED_CLIENT_EMAIL, 788 null, 789 IMMUTABLE_SCOPES_LIST, 790 VALID_LIFETIME, 791 mockTransportFactory); 792 793 byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; 794 795 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 796 mockTransportFactory.transport.setSignedBlob(expectedSignature); 797 mockTransportFactory.transport.setErrorResponseCodeAndMessage( 798 HttpStatusCodes.STATUS_CODE_SERVER_ERROR, "Sign Error"); 799 800 try { 801 byte[] bytes = {0xD, 0xE, 0xA, 0xD}; 802 targetCredentials.sign(bytes); 803 fail("Signing should have failed"); 804 } catch (SigningException e) { 805 assertEquals("Failed to sign the provided bytes", e.getMessage()); 806 assertNotNull(e.getCause()); 807 assertTrue(e.getCause().getMessage().contains("500")); 808 } 809 } 810 811 @Test idTokenWithAudience_sameAs()812 public void idTokenWithAudience_sameAs() throws IOException { 813 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 814 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 815 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 816 817 ImpersonatedCredentials targetCredentials = 818 ImpersonatedCredentials.create( 819 sourceCredentials, 820 IMPERSONATED_CLIENT_EMAIL, 821 null, 822 IMMUTABLE_SCOPES_LIST, 823 VALID_LIFETIME, 824 mockTransportFactory); 825 826 mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); 827 828 String targetAudience = "https://foo.bar"; 829 IdTokenCredentials tokenCredential = 830 IdTokenCredentials.newBuilder() 831 .setIdTokenProvider(targetCredentials) 832 .setTargetAudience(targetAudience) 833 .build(); 834 tokenCredential.refresh(); 835 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); 836 assertEquals(STANDARD_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); 837 assertEquals( 838 targetAudience, 839 (String) tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudience()); 840 } 841 842 @Test idTokenWithAudience_withEmail()843 public void idTokenWithAudience_withEmail() throws IOException { 844 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 845 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 846 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 847 848 ImpersonatedCredentials targetCredentials = 849 ImpersonatedCredentials.create( 850 sourceCredentials, 851 IMPERSONATED_CLIENT_EMAIL, 852 null, 853 IMMUTABLE_SCOPES_LIST, 854 VALID_LIFETIME, 855 mockTransportFactory); 856 857 mockTransportFactory.transport.setIdToken(TOKEN_WITH_EMAIL); 858 859 String targetAudience = "https://foo.bar"; 860 IdTokenCredentials tokenCredential = 861 IdTokenCredentials.newBuilder() 862 .setIdTokenProvider(targetCredentials) 863 .setTargetAudience(targetAudience) 864 .setOptions(Arrays.asList(IdTokenProvider.Option.INCLUDE_EMAIL)) 865 .build(); 866 tokenCredential.refresh(); 867 assertEquals(TOKEN_WITH_EMAIL, tokenCredential.getAccessToken().getTokenValue()); 868 Payload p = tokenCredential.getIdToken().getJsonWebSignature().getPayload(); 869 assertTrue(p.containsKey("email")); 870 } 871 872 @Test idToken_withServerError()873 public void idToken_withServerError() { 874 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 875 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 876 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 877 878 ImpersonatedCredentials targetCredentials = 879 ImpersonatedCredentials.create( 880 sourceCredentials, 881 IMPERSONATED_CLIENT_EMAIL, 882 null, 883 IMMUTABLE_SCOPES_LIST, 884 VALID_LIFETIME, 885 mockTransportFactory); 886 887 mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); 888 mockTransportFactory.transport.setErrorResponseCodeAndMessage( 889 HttpStatusCodes.STATUS_CODE_SERVER_ERROR, "Internal Server Error"); 890 891 String targetAudience = "https://foo.bar"; 892 IdTokenCredentials tokenCredential = 893 IdTokenCredentials.newBuilder() 894 .setIdTokenProvider(targetCredentials) 895 .setTargetAudience(targetAudience) 896 .build(); 897 try { 898 tokenCredential.refresh(); 899 fail("Should not be able to use credential without exception."); 900 } catch (IOException e) { 901 assertTrue(e.getMessage().contains("Error code 500 trying to getIDToken")); 902 } 903 } 904 905 @Test idToken_withOtherError()906 public void idToken_withOtherError() { 907 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 908 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 909 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 910 911 ImpersonatedCredentials targetCredentials = 912 ImpersonatedCredentials.create( 913 sourceCredentials, 914 IMPERSONATED_CLIENT_EMAIL, 915 null, 916 IMMUTABLE_SCOPES_LIST, 917 VALID_LIFETIME, 918 mockTransportFactory); 919 920 mockTransportFactory.transport.setIdToken(STANDARD_ID_TOKEN); 921 mockTransportFactory.transport.setErrorResponseCodeAndMessage( 922 HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY, "Redirect"); 923 924 String targetAudience = "https://foo.bar"; 925 IdTokenCredentials tokenCredential = 926 IdTokenCredentials.newBuilder() 927 .setIdTokenProvider(targetCredentials) 928 .setTargetAudience(targetAudience) 929 .build(); 930 try { 931 tokenCredential.refresh(); 932 fail("Should not be able to use credential without exception."); 933 } catch (IOException e) { 934 assertTrue(e.getMessage().contains("Unexpected Error code 301 trying to getIDToken")); 935 } 936 } 937 938 @Test hashCode_equals()939 public void hashCode_equals() throws IOException { 940 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 941 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 942 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 943 944 ImpersonatedCredentials credentials = 945 ImpersonatedCredentials.create( 946 sourceCredentials, 947 IMPERSONATED_CLIENT_EMAIL, 948 null, 949 IMMUTABLE_SCOPES_LIST, 950 VALID_LIFETIME, 951 mockTransportFactory); 952 953 ImpersonatedCredentials otherCredentials = 954 ImpersonatedCredentials.create( 955 sourceCredentials, 956 IMPERSONATED_CLIENT_EMAIL, 957 null, 958 IMMUTABLE_SCOPES_LIST, 959 VALID_LIFETIME, 960 mockTransportFactory); 961 962 assertEquals(credentials.hashCode(), otherCredentials.hashCode()); 963 } 964 965 @Test serialize()966 public void serialize() throws IOException, ClassNotFoundException { 967 968 mockTransportFactory.transport.setTargetPrincipal(IMPERSONATED_CLIENT_EMAIL); 969 mockTransportFactory.transport.setAccessToken(ACCESS_TOKEN); 970 mockTransportFactory.transport.setExpireTime(getDefaultExpireTime()); 971 972 ImpersonatedCredentials targetCredentials = 973 ImpersonatedCredentials.create( 974 sourceCredentials, 975 IMPERSONATED_CLIENT_EMAIL, 976 null, 977 IMMUTABLE_SCOPES_LIST, 978 VALID_LIFETIME, 979 mockTransportFactory); 980 GoogleCredentials deserializedCredentials = serializeAndDeserialize(targetCredentials); 981 assertEquals(targetCredentials, deserializedCredentials); 982 assertEquals(targetCredentials.hashCode(), deserializedCredentials.hashCode()); 983 assertEquals(targetCredentials.toString(), deserializedCredentials.toString()); 984 assertSame(deserializedCredentials.clock, Clock.SYSTEM); 985 } 986 getDefaultExpireTime()987 public static String getDefaultExpireTime() { 988 Calendar c = Calendar.getInstance(); 989 c.add(Calendar.SECOND, VALID_LIFETIME); 990 return getFormattedTime(c.getTime()); 991 } 992 993 /** 994 * Given a {@link Date}, it will return a string of the date formatted like 995 * <b>yyyy-MM-dd'T'HH:mm:ss'Z'</b> 996 */ getFormattedTime(final Date date)997 private static String getFormattedTime(final Date date) { 998 // Set timezone to GMT since that's the TZ used in the response from the service impersonation 999 // token exchange 1000 final DateFormat formatter = new SimpleDateFormat(RFC3339); 1001 formatter.setTimeZone(TimeZone.getTimeZone("GMT")); 1002 return formatter.format(date); 1003 } 1004 generateErrorJson( int errorCode, String errorMessage, String errorDomain, String errorReason)1005 private String generateErrorJson( 1006 int errorCode, String errorMessage, String errorDomain, String errorReason) 1007 throws IOException { 1008 1009 JsonFactory factory = new GsonFactory(); 1010 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 1011 JsonGenerator generator = factory.createJsonGenerator(bout, Charset.defaultCharset()); 1012 generator.enablePrettyPrint(); 1013 1014 generator.writeStartObject(); 1015 generator.writeFieldName("error"); 1016 1017 generator.writeStartObject(); 1018 generator.writeFieldName("code"); 1019 generator.writeNumber(errorCode); 1020 generator.writeFieldName("message"); 1021 generator.writeString(errorMessage); 1022 1023 generator.writeFieldName("errors"); 1024 generator.writeStartArray(); 1025 generator.writeStartObject(); 1026 generator.writeFieldName("message"); 1027 generator.writeString(errorMessage); 1028 generator.writeFieldName("domain"); 1029 generator.writeString(errorDomain); 1030 generator.writeFieldName("reason"); 1031 generator.writeString(errorReason); 1032 generator.writeEndObject(); 1033 generator.writeEndArray(); 1034 1035 generator.writeFieldName("status"); 1036 generator.writeString("PERMISSION_DENIED"); 1037 1038 generator.writeEndObject(); 1039 generator.writeEndObject(); 1040 generator.close(); 1041 return bout.toString(); 1042 } 1043 buildImpersonationCredentialsJson( String impersonationUrl, List<String> delegates, String quotaProjectId, String sourceClientId, String sourceClientSecret, String sourceRefreshToken)1044 static GenericJson buildImpersonationCredentialsJson( 1045 String impersonationUrl, 1046 List<String> delegates, 1047 String quotaProjectId, 1048 String sourceClientId, 1049 String sourceClientSecret, 1050 String sourceRefreshToken) { 1051 GenericJson sourceJson = new GenericJson(); 1052 1053 sourceJson.put("client_id", sourceClientId); 1054 sourceJson.put("client_secret", sourceClientSecret); 1055 sourceJson.put("refresh_token", sourceRefreshToken); 1056 sourceJson.put("type", "authorized_user"); 1057 GenericJson json = new GenericJson(); 1058 1059 json.put("service_account_impersonation_url", impersonationUrl); 1060 json.put("delegates", delegates); 1061 if (quotaProjectId != null) { 1062 json.put("quota_project_id", quotaProjectId); 1063 } 1064 json.put("source_credentials", sourceJson); 1065 json.put("type", "impersonated_service_account"); 1066 return json; 1067 } 1068 buildImpersonationCredentialsJson( String impersonationUrl, List<String> delegates, String quotaProjectId)1069 static GenericJson buildImpersonationCredentialsJson( 1070 String impersonationUrl, List<String> delegates, String quotaProjectId) { 1071 GenericJson sourceJson = new GenericJson(); 1072 sourceJson.put("type", "service_account"); 1073 sourceJson.put("project_id", PROJECT_ID); 1074 sourceJson.put("private_key_id", SA_PRIVATE_KEY_ID); 1075 sourceJson.put("private_key", SA_PRIVATE_KEY_PKCS8); 1076 sourceJson.put("client_email", SA_CLIENT_EMAIL); 1077 sourceJson.put("client_id", "10848832332323213"); 1078 sourceJson.put("auth_uri", "https://oauth2.googleapis.com/o/oauth2/auth"); 1079 sourceJson.put("token_uri", "https://oauth2.googleapis.com/token"); 1080 sourceJson.put("auth_provider_x509_cert_url", "https://www.googleapis.com/oauth2/v1/certs"); 1081 sourceJson.put( 1082 "client_x509_cert_url", 1083 "https://www.googleapis.com/robot/v1/metadata/x509/chaoren-test-sc%40cloudsdktest.iam.gserviceaccount.com"); 1084 1085 GenericJson json = new GenericJson(); 1086 json.put("source_credentials", sourceJson); 1087 json.put("service_account_impersonation_url", impersonationUrl); 1088 json.put("delegates", delegates); 1089 if (quotaProjectId != null) { 1090 json.put("quota_project_id", quotaProjectId); 1091 } 1092 json.put("type", "impersonated_service_account"); 1093 return json; 1094 } 1095 buildInvalidCredentialsJson()1096 static GenericJson buildInvalidCredentialsJson() { 1097 GenericJson json = new GenericJson(); 1098 json.put("service_account_impersonation_url", "mock_url"); 1099 return json; 1100 } 1101 writeImpersonationCredentialsStream( String impersonationUrl, List<String> delegates, String quotaProjectId)1102 static InputStream writeImpersonationCredentialsStream( 1103 String impersonationUrl, List<String> delegates, String quotaProjectId) throws IOException { 1104 GenericJson json = 1105 buildImpersonationCredentialsJson(impersonationUrl, delegates, quotaProjectId); 1106 return TestUtils.jsonToInputStream(json); 1107 } 1108 } 1109