/* * Copyright 2015, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.google.auth.oauth2; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.api.client.http.HttpStatusCodes; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.LowLevelHttpRequest; import com.google.api.client.http.LowLevelHttpResponse; import com.google.api.client.json.webtoken.JsonWebToken.Payload; import com.google.api.client.testing.http.MockLowLevelHttpRequest; import com.google.api.client.testing.http.MockLowLevelHttpResponse; import com.google.api.client.util.ArrayMap; import com.google.api.client.util.Clock; import com.google.auth.Credentials; import com.google.auth.ServiceAccountSigner.SigningException; import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.DefaultCredentialsProviderTest.MockRequestCountingTransportFactory; import java.io.IOException; import java.net.URI; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.stream.IntStream; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Test case for {@link ComputeEngineCredentials}. */ @RunWith(JUnit4.class) public class ComputeEngineCredentialsTest extends BaseSerializationTest { private static final URI CALL_URI = URI.create("http://googleapis.com/testapi/v1/foo"); private static final String TOKEN_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"; // Id Token which includes basic default claims public static final String STANDARD_ID_TOKEN = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyO" + "TNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiL" + "CJhenAiOiIxMDIxMDE1NTA4MzQyMDA3MDg1NjgiLCJleHAiOjE1NjQ0NzUwNTEsImlhdCI6MTU2NDQ3MTQ1MSwi" + "aXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAyMTAxNTUwODM0MjAwNzA4NTY4In0" + ".redacted"; // Id Token which includes GCE extended claims public static final String FULL_ID_TOKEN = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOGI3OTIyOTNh" + "ZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL2Zvby5iYXIiLCJhe" + "nAiOiIxMTIxNzkwNjI3MjAzOTEzMDU4ODUiLCJlbWFpbCI6IjEwNzEyODQxODQ0MzYtY29tcHV0ZUBkZXZlbG9wZ" + "XIuZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NjQ1MTk0OTYsImdvb" + "2dsZSI6eyJjb21wdXRlX2VuZ2luZSI6eyJpbnN0YW5jZV9jcmVhdGlvbl90aW1lc3RhbXAiOjE1NjMyMzA5MDcsI" + "mluc3RhbmNlX2lkIjoiMzQ5Nzk3NDM5MzQ0MTE3OTI0MyIsImluc3RhbmNlX25hbWUiOiJpYW0iLCJwcm9qZWN0X" + "2lkIjoibWluZXJhbC1taW51dGlhLTgyMCIsInByb2plY3RfbnVtYmVyIjoxMDcxMjg0MTg0NDM2LCJ6b25lIjoid" + "XMtY2VudHJhbDEtYSJ9fSwiaWF0IjoxNTY0NTE1ODk2LCJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb" + "20iLCJzdWIiOiIxMTIxNzkwNjI3MjAzOTEzMDU4ODUifQ.redacted"; // Id Token which includes GCE extended claims and any VM License data (if applicable) public static final String FULL_ID_TOKEN_WITH_LICENSE = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmMzc1ODkwOG" + "I3OTIyOTNhZDk3N2EwYjk5MWQ5OGE3N2Y0ZWVlY2QiLCJ0eXAiOiJKV1QifQ.ew0KICAiYXVkIjogImh0dHBzOi8" + "vZm9vLmJhciIsDQogICJhenAiOiAiMTEyMTc5MDYyNzIwMzkxMzA1ODg1IiwNCiAgImVtYWlsIjogIjEyMzQ1Ni1" + "jb21wdXRlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwNCiAgImVtYWlsX3ZlcmlmaWVkIjogdHJ1ZSw" + "NCiAgImV4cCI6IDE1NjQ1MTk0OTYsDQogICJnb29nbGUiOiB7DQogICAgImNvbXB1dGVfZW5naW5lIjogew0KICA" + "gICAgImluc3RhbmNlX2NyZWF0aW9uX3RpbWVzdGFtcCI6IDE1NjMyMzA5MDcsDQogICAgICAiaW5zdGFuY2VfaWQ" + "iOiAiMzQ5Nzk3NDM5MzQ0MTE3OTI0MyIsDQogICAgICAiaW5zdGFuY2VfbmFtZSI6ICJpYW0iLA0KICAgICAgInB" + "yb2plY3RfaWQiOiAiZm9vLWJhci04MjAiLA0KICAgICAgInByb2plY3RfbnVtYmVyIjogMTA3MTI4NDE4NDQzNiw" + "NCiAgICAgICJ6b25lIjogInVzLWNlbnRyYWwxLWEiDQogICAgfSwNCiAgICAibGljZW5zZSI6IFsNCiAgICAgICA" + "iTElDRU5TRV8xIiwNCiAgICAgICAiTElDRU5TRV8yIg0KICAgIF0NCiAgfSwNCiAgImlhdCI6IDE1NjQ1MTU4OTY" + "sDQogICJpc3MiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwNCiAgInN1YiI6ICIxMTIxNzkwNjI3MjA" + "zOTEzMDU4ODUiDQp9.redacted"; @Test public void buildTokenUrlWithScopes_null_scopes() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setScopes(null).build(); Collection scopes = credentials.getScopes(); String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); assertEquals(TOKEN_URL, tokenUrlWithScopes); assertTrue(scopes.isEmpty()); } @Test public void buildTokenUrlWithScopes_empty_scopes() { ComputeEngineCredentials.Builder builder = ComputeEngineCredentials.newBuilder().setScopes(Collections.emptyList()); ComputeEngineCredentials credentials = builder.build(); Collection scopes = credentials.getScopes(); String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); assertEquals(TOKEN_URL, tokenUrlWithScopes); assertTrue(scopes.isEmpty()); assertTrue(builder.getScopes().isEmpty()); } @Test public void buildTokenUrlWithScopes_single_scope() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setScopes(Arrays.asList("foo")).build(); String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); Collection scopes = credentials.getScopes(); assertEquals(TOKEN_URL + "?scopes=foo", tokenUrlWithScopes); assertEquals(1, scopes.size()); assertEquals("foo", scopes.toArray()[0]); } @Test public void buildTokenUrlWithScopes_multiple_scopes() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setScopes(Arrays.asList(null, "foo", "", "bar")) .build(); Collection scopes = credentials.getScopes(); String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); assertEquals(TOKEN_URL + "?scopes=foo,bar", tokenUrlWithScopes); assertEquals(2, scopes.size()); assertEquals("foo", scopes.toArray()[0]); assertEquals("bar", scopes.toArray()[1]); } @Test public void buildTokenUrlWithScopes_defaultScopes() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().build(); credentials = (ComputeEngineCredentials) credentials.createScoped(null, Arrays.asList(null, "foo", "", "bar")); Collection scopes = credentials.getScopes(); String tokenUrlWithScopes = credentials.createTokenUrlWithScopes(); assertEquals(TOKEN_URL + "?scopes=foo,bar", tokenUrlWithScopes); assertEquals(2, scopes.size()); assertEquals("foo", scopes.toArray()[0]); assertEquals("bar", scopes.toArray()[1]); } @Test public void buildScoped_scopesPresent() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setScopes(null).build(); ComputeEngineCredentials scopedCredentials = (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); Collection scopes = scopedCredentials.getScopes(); assertEquals(1, scopes.size()); assertEquals("foo", scopes.toArray()[0]); } @Test public void buildScoped_correctMargins() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setScopes(null).build(); ComputeEngineCredentials scopedCredentials = (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); assertEquals( ComputeEngineCredentials.COMPUTE_EXPIRATION_MARGIN, scopedCredentials.getExpirationMargin()); assertEquals( ComputeEngineCredentials.COMPUTE_REFRESH_MARGIN, scopedCredentials.getRefreshMargin()); } @Test public void buildScoped_explicitUniverse() throws IOException { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setScopes(null) .setUniverseDomain("some-universe") .build(); ComputeEngineCredentials scopedCredentials = (ComputeEngineCredentials) credentials.createScoped(Arrays.asList("foo")); assertEquals("some-universe", scopedCredentials.getUniverseDomain()); assertEquals(true, scopedCredentials.isExplicitUniverseDomain()); } @Test public void createScoped_defaultScopes() { GoogleCredentials credentials = ComputeEngineCredentials.create().createScoped(null, Arrays.asList("foo")); Collection scopes = ((ComputeEngineCredentials) credentials).getScopes(); assertEquals(1, scopes.size()); assertEquals("foo", scopes.toArray()[0]); } @Test public void create_scoped_correctMargins() { GoogleCredentials credentials = ComputeEngineCredentials.create().createScoped(null, Arrays.asList("foo")); assertEquals( ComputeEngineCredentials.COMPUTE_EXPIRATION_MARGIN, credentials.getExpirationMargin()); assertEquals(ComputeEngineCredentials.COMPUTE_REFRESH_MARGIN, credentials.getRefreshMargin()); } @Test public void getRequestMetadata_hasAccessToken() throws IOException { String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport.setAccessToken(accessToken); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); Map> metadata = credentials.getRequestMetadata(CALL_URI); TestUtils.assertContainsBearerToken(metadata, accessToken); } @Test public void getRequestMetadata_missingServiceAccount_throws() { String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setRequestStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.getRequestMetadata(CALL_URI); fail("Expected error refreshing token."); } catch (IOException expected) { String message = expected.getMessage(); assertTrue(message.contains(Integer.toString(HttpStatusCodes.STATUS_CODE_NOT_FOUND))); // Message should mention scopes are missing on the VM. assertTrue(message.contains("scope")); } } @Test public void getRequestMetadata_serverError_throws() { String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setRequestStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.getRequestMetadata(CALL_URI); fail("Expected error refreshing token."); } catch (IOException expected) { String message = expected.getMessage(); assertTrue(message.contains(Integer.toString(HttpStatusCodes.STATUS_CODE_SERVER_ERROR))); assertTrue(message.contains("Unexpected")); } } @Test public void equals_true() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials explicitUniverseCredentials = ComputeEngineCredentials.newBuilder() .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) .setHttpTransportFactory(transportFactory) .build(); ComputeEngineCredentials otherCredentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, otherCredentials.getUniverseDomain()); assertFalse(explicitUniverseCredentials.equals(otherCredentials)); assertFalse(otherCredentials.equals(explicitUniverseCredentials)); ComputeEngineCredentials otherExplicitUniverseCredentials = ComputeEngineCredentials.newBuilder() .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) .setHttpTransportFactory(transportFactory) .build(); assertTrue(explicitUniverseCredentials.equals(otherExplicitUniverseCredentials)); assertTrue(otherExplicitUniverseCredentials.equals(explicitUniverseCredentials)); } @Test public void equals_false_transportFactory() throws IOException { MockHttpTransportFactory httpTransportFactory = new MockHttpTransportFactory(); MockMetadataServerTransportFactory serverTransportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(serverTransportFactory) .build(); ComputeEngineCredentials otherCredentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(httpTransportFactory).build(); assertFalse(credentials.equals(otherCredentials)); assertFalse(otherCredentials.equals(credentials)); } @Test public void toString_explicit_containsFields() throws IOException { MockMetadataServerTransportFactory serverTransportFactory = new MockMetadataServerTransportFactory(); String expectedToString = String.format( "ComputeEngineCredentials{quotaProjectId=%s, universeDomain=%s, isExplicitUniverseDomain=%s, transportFactoryClassName=%s, scopes=%s}", "some-project", "some-domain", true, MockMetadataServerTransportFactory.class.getName(), "[some scope]"); GoogleCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(serverTransportFactory) .setQuotaProjectId("some-project") .setUniverseDomain("some-domain") .build(); credentials = credentials.createScoped("some scope"); assertEquals(expectedToString, credentials.toString()); } @Test public void hashCode_equals() throws IOException { MockMetadataServerTransportFactory serverTransportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(serverTransportFactory) .build(); ComputeEngineCredentials otherCredentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(serverTransportFactory) .build(); assertEquals(credentials.hashCode(), otherCredentials.hashCode()); } @Test public void toBuilder() { ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(new MockMetadataServerTransportFactory()) .setQuotaProjectId("quota-project") .build(); ComputeEngineCredentials secondCredentials = credentials.toBuilder().build(); assertEquals(credentials, secondCredentials); } @Test public void serialize() throws IOException, ClassNotFoundException { MockMetadataServerTransportFactory serverTransportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(serverTransportFactory) .build(); GoogleCredentials deserializedCredentials = serializeAndDeserialize(credentials); assertEquals(credentials, deserializedCredentials); assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); assertEquals(credentials.toString(), deserializedCredentials.toString()); assertSame(deserializedCredentials.clock, Clock.SYSTEM); credentials = ComputeEngineCredentials.newBuilder().build(); deserializedCredentials = serializeAndDeserialize(credentials); assertEquals(credentials, deserializedCredentials); assertEquals(credentials.hashCode(), deserializedCredentials.hashCode()); assertEquals(credentials.toString(), deserializedCredentials.toString()); assertSame(deserializedCredentials.clock, Clock.SYSTEM); } @Test public void getAccount_sameAs() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); String defaultAccountEmail = "mail@mail.com"; transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); assertEquals(defaultAccountEmail, credentials.getAccount()); } @Test public void getAccount_missing_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); String defaultAccountEmail = "mail@mail.com"; transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (isGetServiceAccountsUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND) .setContent(""); } }; } return super.buildRequest(method, url); } }; transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.getAccount(); fail("Fetching default service account should have failed"); } catch (RuntimeException e) { assertEquals("Failed to get service account", e.getMessage()); assertNotNull(e.getCause()); assertTrue(e.getCause().getMessage().contains("404")); } } @Test public void getAccount_emptyContent_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); String defaultAccountEmail = "mail@mail.com"; transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (isGetServiceAccountsUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_OK); } }; } return super.buildRequest(method, url); } }; transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.getAccount(); fail("Fetching default service account should have failed"); } catch (RuntimeException e) { assertEquals("Failed to get service account", e.getMessage()); assertNotNull(e.getCause()); assertTrue(e.getCause().getMessage().contains("Empty content")); } } @Test public void sign_sameAs() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; String defaultAccountEmail = "mail@mail.com"; byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); transportFactory.transport.setSignature(expectedSignature); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); assertArrayEquals(expectedSignature, credentials.sign(expectedSignature)); } @Test public void sign_getAccountFails() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; byte[] expectedSignature = {0xD, 0xE, 0xA, 0xD}; transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setSignature(expectedSignature); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.sign(expectedSignature); fail("Should not be able to use credential without exception."); } catch (SigningException ex) { assertNotNull(ex.getMessage()); assertNotNull(ex.getCause()); } } @Test public void sign_accessDenied_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; String defaultAccountEmail = "mail@mail.com"; transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (isSignRequestUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_FORBIDDEN) .setContent(TestUtils.errorJson("Sign Error")); } }; } return super.buildRequest(method, url); } }; transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { byte[] bytes = {0xD, 0xE, 0xA, 0xD}; credentials.sign(bytes); fail("Signing should have failed"); } catch (SigningException e) { assertEquals("Failed to sign the provided bytes", e.getMessage()); assertNotNull(e.getCause()); assertTrue(e.getCause().getMessage().contains("403")); } } @Test public void sign_serverError_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); final String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; String defaultAccountEmail = "mail@mail.com"; transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (isSignRequestUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR) .setContent(TestUtils.errorJson("Sign Error")); } }; } return super.buildRequest(method, url); } }; transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { byte[] bytes = {0xD, 0xE, 0xA, 0xD}; credentials.sign(bytes); fail("Signing should have failed"); } catch (SigningException e) { assertEquals("Failed to sign the provided bytes", e.getMessage()); assertNotNull(e.getCause()); assertTrue(e.getCause().getMessage().contains("500")); } } @Test public void refresh_503_retryable_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE) .setContent(TestUtils.errorJson("Some error")); } }; } }; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.refreshAccessToken(); fail("Should have failed"); } catch (IOException e) { assertTrue(e.getCause().getMessage().contains("503")); assertTrue(e instanceof GoogleAuthException); assertTrue(((GoogleAuthException) e).isRetryable()); } } @Test public void refresh_non503_ioexception_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); final Queue responseSequence = new ArrayDeque<>(); IntStream.rangeClosed(400, 600).forEach(i -> responseSequence.add(i)); while (!responseSequence.isEmpty()) { if (responseSequence.peek() == 503) { responseSequence.poll(); continue; } transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(responseSequence.poll()) .setContent(TestUtils.errorJson("Some error")); } }; } }; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { credentials.refreshAccessToken(); fail("Should have failed"); } catch (IOException e) { assertFalse(e instanceof GoogleAuthException); } } } @Test public void getUniverseDomain_fromMetadata() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_OK) .setContent("some-universe.xyz"); } }; } }; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String universeDomain = credentials.getUniverseDomain(); assertEquals("some-universe.xyz", universeDomain); assertEquals(false, credentials.isExplicitUniverseDomain()); } @Test public void getUniverseDomain_fromMetadata_emptyBecomesDefault() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_OK) .setContent(""); } }; } }; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String universeDomain = credentials.getUniverseDomain(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); assertEquals(false, credentials.isExplicitUniverseDomain()); } @Test public void getUniverseDomain_fromMetadata_404_default() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_NOT_FOUND) .setContent("some content"); } }; } }; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String universeDomain = credentials.getUniverseDomain(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); assertEquals(false, credentials.isExplicitUniverseDomain()); } @Test public void getUniverseDomain_explicitSet_NoMdsCall() throws IOException { MockRequestCountingTransportFactory transportFactory = new MockRequestCountingTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(transportFactory) .setUniverseDomain("explicit.universe") .build(); String universeDomain = credentials.getUniverseDomain(); assertEquals("explicit.universe", universeDomain); assertEquals(true, credentials.isExplicitUniverseDomain()); assertEquals(0, transportFactory.transport.getRequestCount()); } @Test public void getUniverseDomain_explicitGduSet_NoMdsCall() throws IOException { MockRequestCountingTransportFactory transportFactory = new MockRequestCountingTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder() .setHttpTransportFactory(transportFactory) .setUniverseDomain(Credentials.GOOGLE_DEFAULT_UNIVERSE) .build(); String universeDomain = credentials.getUniverseDomain(); assertEquals(Credentials.GOOGLE_DEFAULT_UNIVERSE, universeDomain); assertEquals(true, credentials.isExplicitUniverseDomain()); assertEquals(0, transportFactory.transport.getRequestCount()); } @Test public void getUniverseDomain_fromMetadata_non404error_throws() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); MockMetadataServerTransport transport = transportFactory.transport; ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); for (int status = 400; status < 600; status++) { // 404 should not throw and tested separately if (status == 404) { continue; } try { transportFactory.transport.setRequestStatusCode(status); credentials.getUniverseDomain(); fail("Should not be able to use credential without exception."); } catch (GoogleAuthException ex) { assertTrue(ex.isRetryable()); } } } @Test public void sign_emptyContent_throws() { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); String accessToken = "1/MkSJoj1xsli0AccessToken_NKPY2"; String defaultAccountEmail = "mail@mail.com"; transportFactory.transport = new MockMetadataServerTransport() { @Override public LowLevelHttpRequest buildRequest(String method, String url) throws IOException { if (isSignRequestUrl(url)) { return new MockLowLevelHttpRequest(url) { @Override public LowLevelHttpResponse execute() throws IOException { return new MockLowLevelHttpResponse() .setStatusCode(HttpStatusCodes.STATUS_CODE_OK); } }; } return super.buildRequest(method, url); } }; transportFactory.transport.setAccessToken(accessToken); transportFactory.transport.setServiceAccountEmail(defaultAccountEmail); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); try { byte[] bytes = {0xD, 0xE, 0xA, 0xD}; credentials.sign(bytes); fail("Signing should have failed"); } catch (SigningException e) { assertEquals("Failed to sign the provided bytes", e.getMessage()); assertNotNull(e.getCause()); assertTrue(e.getCause().getMessage().contains("Empty content")); } } @Test public void idTokenWithAudience_sameAs() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); transportFactory.transport.setIdToken(STANDARD_ID_TOKEN); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder() .setIdTokenProvider(credentials) .setTargetAudience(targetAudience) .build(); tokenCredential.refresh(); assertEquals(STANDARD_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); assertEquals(STANDARD_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); assertEquals( targetAudience, (String) tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudience()); } @Test public void idTokenWithAudience_standard() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder() .setIdTokenProvider(credentials) .setTargetAudience(targetAudience) .build(); tokenCredential.refresh(); assertEquals(STANDARD_ID_TOKEN, tokenCredential.getAccessToken().getTokenValue()); assertEquals(STANDARD_ID_TOKEN, tokenCredential.getIdToken().getTokenValue()); assertNull(tokenCredential.getIdToken().getJsonWebSignature().getPayload().get("google")); } @Test @SuppressWarnings("unchecked") public void idTokenWithAudience_full() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder() .setIdTokenProvider(credentials) .setTargetAudience(targetAudience) .setOptions(Arrays.asList(IdTokenProvider.Option.FORMAT_FULL)) .build(); tokenCredential.refresh(); Payload p = tokenCredential.getIdToken().getJsonWebSignature().getPayload(); assertTrue("Full ID Token format not provided", p.containsKey("google")); ArrayMap googleClaim = (ArrayMap) p.get("google"); assertTrue(googleClaim.containsKey("compute_engine")); } @Test @SuppressWarnings("unchecked") public void idTokenWithAudience_license() throws IOException { MockMetadataServerTransportFactory transportFactory = new MockMetadataServerTransportFactory(); ComputeEngineCredentials credentials = ComputeEngineCredentials.newBuilder().setHttpTransportFactory(transportFactory).build(); String targetAudience = "https://foo.bar"; IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder() .setIdTokenProvider(credentials) .setTargetAudience(targetAudience) .setOptions( Arrays.asList( IdTokenProvider.Option.FORMAT_FULL, IdTokenProvider.Option.LICENSES_TRUE)) .build(); tokenCredential.refresh(); Payload p = tokenCredential.getIdToken().getJsonWebSignature().getPayload(); assertTrue("Full ID Token format not provided", p.containsKey("google")); ArrayMap googleClaim = (ArrayMap) p.get("google"); assertTrue(googleClaim.containsKey("license")); } static class MockMetadataServerTransportFactory implements HttpTransportFactory { MockMetadataServerTransport transport = new MockMetadataServerTransport(); @Override public HttpTransport create() { return transport; } } }