/* * Copyright 2022 Google LLC * * 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 LLC 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 com.google.auth.Credentials.GOOGLE_DEFAULT_UNIVERSE; import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL; import static org.junit.Assert.*; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.GenericJson; import com.google.auth.TestUtils; import com.google.auth.http.HttpTransportFactory; import com.google.auth.oauth2.ExecutableHandler.ExecutableOptions; import java.io.IOException; import java.io.InputStream; import java.io.NotSerializableException; import java.math.BigDecimal; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import org.junit.Test; /** Tests for {@link PluggableAuthCredentials}. */ public class PluggableAuthCredentialsTest extends BaseSerializationTest { // The default timeout for waiting for the executable to finish (30 seconds). private static final int DEFAULT_EXECUTABLE_TIMEOUT_MS = 30 * 1000; // The minimum timeout for waiting for the executable to finish (5 seconds). private static final int MINIMUM_EXECUTABLE_TIMEOUT_MS = 5 * 1000; // The maximum timeout for waiting for the executable to finish (120 seconds). private static final int MAXIMUM_EXECUTABLE_TIMEOUT_MS = 120 * 1000; private static final String STS_URL = "https://sts.googleapis.com"; private static final PluggableAuthCredentials CREDENTIAL = PluggableAuthCredentials.newBuilder() .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(buildCredentialSource()) .build(); static class MockExternalAccountCredentialsTransportFactory implements HttpTransportFactory { MockExternalAccountCredentialsTransport transport = new MockExternalAccountCredentialsTransport(); @Override public HttpTransport create() { return transport; } } @Test public void retrieveSubjectToken_shouldDelegateToHandler() throws IOException { PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .build(); String subjectToken = credential.retrieveSubjectToken(); assertEquals(subjectToken, "pluggableAuthToken"); } @Test public void retrieveSubjectToken_shouldPassAllOptionsToHandler() throws IOException { String command = "/path/to/executable"; String timeout = "5000"; String outputFile = "/path/to/output/file"; final ExecutableOptions[] providedOptions = {null}; ExecutableHandler executableHandler = options -> { providedOptions[0] = options; return "pluggableAuthToken"; }; PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(executableHandler) .setCredentialSource(buildCredentialSource(command, timeout, outputFile)) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .build(); String subjectToken = credential.retrieveSubjectToken(); assertEquals(subjectToken, "pluggableAuthToken"); // Validate that the correct options were passed to the executable handler. ExecutableOptions options = providedOptions[0]; assertEquals(options.getExecutableCommand(), command); assertEquals(options.getExecutableTimeoutMs(), Integer.parseInt(timeout)); assertEquals(options.getOutputFilePath(), outputFile); Map envMap = options.getEnvironmentMap(); assertEquals(envMap.size(), 5); assertEquals(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE"), credential.getAudience()); assertEquals( envMap.get("GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE"), credential.getSubjectTokenType()); assertEquals(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE"), "0"); assertEquals( envMap.get("GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL"), credential.getServiceAccountEmail()); assertEquals(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE"), outputFile); } @Test public void retrieveSubjectToken_shouldPassMinimalOptionsToHandler() throws IOException { String command = "/path/to/executable"; final ExecutableOptions[] providedOptions = {null}; ExecutableHandler executableHandler = options -> { providedOptions[0] = options; return "pluggableAuthToken"; }; PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(executableHandler) .setCredentialSource( buildCredentialSource(command, /* timeoutMs= */ null, /* outputFile= */ null)) .build(); String subjectToken = credential.retrieveSubjectToken(); assertEquals(subjectToken, "pluggableAuthToken"); // Validate that the correct options were passed to the executable handler. ExecutableOptions options = providedOptions[0]; assertEquals(options.getExecutableCommand(), command); assertEquals(options.getExecutableTimeoutMs(), DEFAULT_EXECUTABLE_TIMEOUT_MS); assertNull(options.getOutputFilePath()); Map envMap = options.getEnvironmentMap(); assertEquals(envMap.size(), 3); assertEquals(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE"), credential.getAudience()); assertEquals( envMap.get("GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE"), credential.getSubjectTokenType()); assertEquals(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE"), "0"); assertNull(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL")); assertNull(envMap.get("GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE")); } @Test public void refreshAccessToken_withoutServiceAccountImpersonation() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setTokenUrl(transportFactory.transport.getStsUrl()) .setHttpTransportFactory(transportFactory) .build(); AccessToken accessToken = credential.refreshAccessToken(); assertEquals(transportFactory.transport.getAccessToken(), accessToken.getTokenValue()); // Validate that the correct subject token was passed to STS. Map query = TestUtils.parseQuery(transportFactory.transport.getRequests().get(0).getContentAsString()); assertEquals(query.get("subject_token"), "pluggableAuthToken"); // Validate metrics header is set correctly on the sts request. Map> headers = transportFactory.transport.getRequests().get(0).getHeaders(); ExternalAccountCredentialsTest.validateMetricsHeader(headers, "executable", false, false); } @Test public void refreshAccessToken_withServiceAccountImpersonation() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") .setSubjectTokenType("subjectTokenType") .setTokenInfoUrl("tokenInfoUrl") .setTokenUrl(transportFactory.transport.getStsUrl()) .setCredentialSource(buildCredentialSource()) .setServiceAccountImpersonationUrl( transportFactory.transport.getServiceAccountImpersonationUrl()) .setHttpTransportFactory(transportFactory) .build(); credential = PluggableAuthCredentials.newBuilder(credential) .setExecutableHandler(options -> "pluggableAuthToken") .build(); AccessToken accessToken = credential.refreshAccessToken(); assertEquals( transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); // Validate that the correct subject token was passed to STS. Map query = TestUtils.parseQuery(transportFactory.transport.getRequests().get(0).getContentAsString()); assertEquals(query.get("subject_token"), "pluggableAuthToken"); // Validate metrics header is set correctly on the sts request. Map> headers = transportFactory.transport.getRequests().get(0).getHeaders(); ExternalAccountCredentialsTest.validateMetricsHeader(headers, "executable", true, false); } @Test public void refreshAccessToken_withServiceAccountImpersonationOptions() throws IOException { MockExternalAccountCredentialsTransportFactory transportFactory = new MockExternalAccountCredentialsTransportFactory(); transportFactory.transport.setExpireTime(TestUtils.getDefaultExpireTime()); PluggableAuthCredentials credential = PluggableAuthCredentials.newBuilder() .setAudience( "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/pool/providers/provider") .setSubjectTokenType("subjectTokenType") .setTokenInfoUrl("tokenInfoUrl") .setTokenUrl(transportFactory.transport.getStsUrl()) .setCredentialSource(buildCredentialSource()) .setServiceAccountImpersonationUrl( transportFactory.transport.getServiceAccountImpersonationUrl()) .setServiceAccountImpersonationOptions( ExternalAccountCredentialsTest.buildServiceAccountImpersonationOptions(2800)) .setHttpTransportFactory(transportFactory) .build(); credential = PluggableAuthCredentials.newBuilder(credential) .setExecutableHandler(options -> "pluggableAuthToken") .build(); AccessToken accessToken = credential.refreshAccessToken(); assertEquals( transportFactory.transport.getServiceAccountAccessToken(), accessToken.getTokenValue()); // Validate that default lifetime was set correctly on the request. GenericJson query = OAuth2Utils.JSON_FACTORY .createJsonParser(transportFactory.transport.getLastRequest().getContentAsString()) .parseAndClose(GenericJson.class); assertEquals("2800s", query.get("lifetime")); // Validate metrics header is set correctly on the sts request. Map> headers = transportFactory.transport.getRequests().get(0).getHeaders(); ExternalAccountCredentialsTest.validateMetricsHeader(headers, "executable", true, true); } @Test public void pluggableAuthCredentialSource_allFields() { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); executable.put("command", "/path/to/executable"); executable.put("timeout_millis", "10000"); executable.put("output_file", "/path/to/output/file"); PluggableAuthCredentialSource credentialSource = new PluggableAuthCredentialSource(source); assertEquals(credentialSource.getCommand(), "/path/to/executable"); assertEquals(credentialSource.getTimeoutMs(), 10000); assertEquals(credentialSource.getOutputFilePath(), "/path/to/output/file"); } @Test public void pluggableAuthCredentialSource_noTimeoutProvided_setToDefault() { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); executable.put("command", "command"); PluggableAuthCredentialSource credentialSource = new PluggableAuthCredentialSource(source); assertEquals(credentialSource.getCommand(), "command"); assertEquals(credentialSource.getTimeoutMs(), DEFAULT_EXECUTABLE_TIMEOUT_MS); assertNull(credentialSource.getOutputFilePath()); } @Test public void pluggableAuthCredentialSource_timeoutProvidedOutOfRange_throws() { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); executable.put("command", "command"); int[] possibleOutOfRangeValues = new int[] {0, 4 * 1000, 121 * 1000}; for (int value : possibleOutOfRangeValues) { executable.put("timeout_millis", value); try { new PluggableAuthCredentialSource(source); fail("Should not be able to continue without exception."); } catch (IllegalArgumentException exception) { assertEquals( String.format( "The executable timeout must be between %s and %s milliseconds.", MINIMUM_EXECUTABLE_TIMEOUT_MS, MAXIMUM_EXECUTABLE_TIMEOUT_MS), exception.getMessage()); } } } @Test public void pluggableAuthCredentialSource_validTimeoutProvided() { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); executable.put("command", "command"); Object[] possibleValues = new Object[] {"10000", 10000, BigDecimal.valueOf(10000L)}; for (Object value : possibleValues) { executable.put("timeout_millis", value); PluggableAuthCredentialSource credentialSource = new PluggableAuthCredentialSource(source); assertEquals(credentialSource.getCommand(), "command"); assertEquals(credentialSource.getTimeoutMs(), 10000); assertNull(credentialSource.getOutputFilePath()); } } @Test public void pluggableAuthCredentialSource_missingExecutableField_throws() { try { new PluggableAuthCredentialSource(new HashMap<>()); fail("Should not be able to continue without exception."); } catch (IllegalArgumentException exception) { assertEquals( "Invalid credential source for PluggableAuth credentials.", exception.getMessage()); } } @Test public void pluggableAuthCredentialSource_missingExecutableCommandField_throws() { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); try { new PluggableAuthCredentialSource(source); fail("Should not be able to continue without exception."); } catch (IllegalArgumentException exception) { assertEquals( "The PluggableAuthCredentialSource is missing the required 'command' field.", exception.getMessage()); } } @Test public void builder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(source) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setScopes(scopes) .setUniverseDomain("universeDomain") .build(); assertEquals(handler, credentials.getExecutableHandler()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); assertEquals(STS_URL, credentials.getTokenUrl()); assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); assertEquals( SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); assertEquals(source, credentials.getCredentialSource()); assertEquals("quotaProjectId", credentials.getQuotaProjectId()); assertEquals("clientId", credentials.getClientId()); assertEquals("clientSecret", credentials.getClientSecret()); assertEquals(scopes, credentials.getScopes()); assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); assertEquals("universeDomain", credentials.getUniverseDomain()); } @Test public void builder_missingUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(source) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setScopes(scopes) .build(); assertEquals(handler, credentials.getExecutableHandler()); assertEquals("audience", credentials.getAudience()); assertEquals("subjectTokenType", credentials.getSubjectTokenType()); assertEquals(STS_URL, credentials.getTokenUrl()); assertEquals("tokenInfoUrl", credentials.getTokenInfoUrl()); assertEquals( SERVICE_ACCOUNT_IMPERSONATION_URL, credentials.getServiceAccountImpersonationUrl()); assertEquals(source, credentials.getCredentialSource()); assertEquals("quotaProjectId", credentials.getQuotaProjectId()); assertEquals("clientId", credentials.getClientId()); assertEquals("clientSecret", credentials.getClientSecret()); assertEquals(scopes, credentials.getScopes()); assertEquals(SystemEnvironmentProvider.getInstance(), credentials.getEnvironmentProvider()); assertEquals(GOOGLE_DEFAULT_UNIVERSE, credentials.getUniverseDomain()); } @Test public void newBuilder_allFields() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(source) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setScopes(scopes) .setUniverseDomain("universeDomain") .build(); PluggableAuthCredentials newBuilderCreds = PluggableAuthCredentials.newBuilder(credentials).build(); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); assertEquals( credentials.getServiceAccountImpersonationUrl(), newBuilderCreds.getServiceAccountImpersonationUrl()); assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); assertEquals(credentials.getUniverseDomain(), newBuilderCreds.getUniverseDomain()); } @Test public void newBuilder_noUniverseDomain_defaults() throws IOException { List scopes = Arrays.asList("scope1", "scope2"); PluggableAuthCredentialSource source = buildCredentialSource(); ExecutableHandler handler = options -> "Token"; PluggableAuthCredentials credentials = PluggableAuthCredentials.newBuilder() .setExecutableHandler(handler) .setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY) .setAudience("audience") .setSubjectTokenType("subjectTokenType") .setTokenUrl(STS_URL) .setTokenInfoUrl("tokenInfoUrl") .setCredentialSource(source) .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setScopes(scopes) .build(); PluggableAuthCredentials newBuilderCreds = PluggableAuthCredentials.newBuilder(credentials).build(); assertEquals(credentials.getAudience(), newBuilderCreds.getAudience()); assertEquals(credentials.getSubjectTokenType(), newBuilderCreds.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newBuilderCreds.getTokenUrl()); assertEquals(credentials.getTokenInfoUrl(), newBuilderCreds.getTokenInfoUrl()); assertEquals( credentials.getServiceAccountImpersonationUrl(), newBuilderCreds.getServiceAccountImpersonationUrl()); assertEquals(credentials.getCredentialSource(), newBuilderCreds.getCredentialSource()); assertEquals(credentials.getQuotaProjectId(), newBuilderCreds.getQuotaProjectId()); assertEquals(credentials.getClientId(), newBuilderCreds.getClientId()); assertEquals(credentials.getClientSecret(), newBuilderCreds.getClientSecret()); assertEquals(credentials.getScopes(), newBuilderCreds.getScopes()); assertEquals(credentials.getEnvironmentProvider(), newBuilderCreds.getEnvironmentProvider()); assertEquals(GOOGLE_DEFAULT_UNIVERSE, newBuilderCreds.getUniverseDomain()); } @Test public void createdScoped_clonedCredentialWithAddedScopes() throws IOException { PluggableAuthCredentials credentials = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setUniverseDomain("universeDomain") .build(); List newScopes = Arrays.asList("scope1", "scope2"); PluggableAuthCredentials newCredentials = credentials.createScoped(newScopes); assertEquals(credentials.getAudience(), newCredentials.getAudience()); assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType()); assertEquals(credentials.getTokenUrl(), newCredentials.getTokenUrl()); assertEquals(credentials.getTokenInfoUrl(), newCredentials.getTokenInfoUrl()); assertEquals( credentials.getServiceAccountImpersonationUrl(), newCredentials.getServiceAccountImpersonationUrl()); assertEquals(credentials.getCredentialSource(), newCredentials.getCredentialSource()); assertEquals(newScopes, newCredentials.getScopes()); assertEquals(credentials.getQuotaProjectId(), newCredentials.getQuotaProjectId()); assertEquals(credentials.getClientId(), newCredentials.getClientId()); assertEquals(credentials.getClientSecret(), newCredentials.getClientSecret()); assertEquals(credentials.getExecutableHandler(), newCredentials.getExecutableHandler()); assertEquals(credentials.getUniverseDomain(), newCredentials.getUniverseDomain()); assertEquals("universeDomain", newCredentials.getUniverseDomain()); } @Test public void serialize() throws IOException, ClassNotFoundException { PluggableAuthCredentials testCredentials = PluggableAuthCredentials.newBuilder(CREDENTIAL) .setExecutableHandler(options -> "pluggableAuthToken") .setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL) .setQuotaProjectId("quotaProjectId") .setClientId("clientId") .setClientSecret("clientSecret") .setUniverseDomain("universeDomain") .build(); // PluggableAuthCredentials are not serializable assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials)); } private static PluggableAuthCredentialSource buildCredentialSource() { return buildCredentialSource("command", null, null); } private static PluggableAuthCredentialSource buildCredentialSource( String command, @Nullable String timeoutMs, @Nullable String outputFile) { Map source = new HashMap<>(); Map executable = new HashMap<>(); source.put("executable", executable); executable.put("command", command); if (timeoutMs != null) { executable.put("timeout_millis", timeoutMs); } if (outputFile != null) { executable.put("output_file", outputFile); } return new PluggableAuthCredentialSource(source); } static InputStream writeCredentialsStream(String tokenUrl) throws IOException { GenericJson json = new GenericJson(); json.put("audience", "audience"); json.put("subject_token_type", "subjectTokenType"); json.put("token_url", tokenUrl); json.put("token_info_url", "tokenInfoUrl"); json.put("type", ExternalAccountCredentials.EXTERNAL_ACCOUNT_FILE_TYPE); GenericJson credentialSource = new GenericJson(); GenericJson executable = new GenericJson(); executable.put("command", "/path/to/executable"); credentialSource.put("executable", executable); json.put("credential_source", credentialSource); return TestUtils.jsonToInputStream(json); } }