Skip to content

Commit 82bf871

Browse files
authored
fix: Make supporting classes of AwsCredentials serializable (#1113)
* fix: Make supporting classes of AwsCredentials serializable * Add unit test for ExternalAccountCredentials test. Discovered ServiceAccountImpersonationOptions needed be serializable as well. * Fix linting * Add serialization tests for subclasses of ExternalAccountCredentials Note that PluggableAuthCredentials uses a non-serializable ExecutableHandler. It's not clear that this can/should be a serializable credential source. * Add serialVersionUID fields to classes implementing ExternalAccountCredentials
1 parent 46720b0 commit 82bf871

10 files changed

+142
-21
lines changed

oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public class AwsCredentials extends ExternalAccountCredentials {
7373
static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token";
7474
static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds";
7575
static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300";
76+
private static final long serialVersionUID = -3670131891574618105L;
7677

7778
/**
7879
* The AWS credential source. Stores data required to retrieve the AWS credential from the AWS
@@ -81,6 +82,7 @@ public class AwsCredentials extends ExternalAccountCredentials {
8182
static class AwsCredentialSource extends CredentialSource {
8283

8384
private static final String IMDSV2_SESSION_TOKEN_URL_FIELD_NAME = "imdsv2_session_token_url";
85+
private static final long serialVersionUID = -4180558200808134436L;
8486

8587
private final String regionUrl;
8688
private final String url;

oauth2_http/java/com/google/auth/oauth2/ExternalAccountCredentials.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.common.base.MoreObjects;
4444
import java.io.IOException;
4545
import java.io.InputStream;
46+
import java.io.Serializable;
4647
import java.math.BigDecimal;
4748
import java.net.URI;
4849
import java.nio.charset.StandardCharsets;
@@ -65,8 +66,12 @@
6566
*/
6667
public abstract class ExternalAccountCredentials extends GoogleCredentials {
6768

69+
private static final long serialVersionUID = 8049126194174465023L;
70+
6871
/** Base credential source class. Dictates the retrieval method of the external credential. */
69-
abstract static class CredentialSource {
72+
abstract static class CredentialSource implements Serializable {
73+
74+
private static final long serialVersionUID = 8204657811562399944L;
7075

7176
CredentialSource(Map<String, Object> credentialSourceMap) {
7277
checkNotNull(credentialSourceMap);
@@ -636,7 +641,9 @@ private static boolean isValidUrl(String url) {
636641
* }
637642
* </pre>
638643
*/
639-
static final class ServiceAccountImpersonationOptions {
644+
static final class ServiceAccountImpersonationOptions implements Serializable {
645+
646+
private static final long serialVersionUID = 4250771921886280953L;
640647
private static final int DEFAULT_TOKEN_LIFETIME_SECONDS = 3600;
641648
private static final int MAXIMUM_TOKEN_LIFETIME_SECONDS = 43200;
642649
private static final int MINIMUM_TOKEN_LIFETIME_SECONDS = 600;

oauth2_http/java/com/google/auth/oauth2/IdentityPoolCredentials.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,16 @@
6363
*/
6464
public class IdentityPoolCredentials extends ExternalAccountCredentials {
6565

66+
private static final long serialVersionUID = 2471046175477275881L;
67+
6668
/**
6769
* The IdentityPool credential source. Dictates the retrieval method of the external credential,
6870
* which can either be through a metadata server or a local file.
6971
*/
7072
static class IdentityPoolCredentialSource extends ExternalAccountCredentials.CredentialSource {
7173

74+
private static final long serialVersionUID = -745855247050085694L;
75+
7276
enum IdentityPoolCredentialSourceType {
7377
FILE,
7478
URL

oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.google.common.base.MoreObjects;
5151
import com.google.common.collect.ImmutableMap;
5252
import java.io.IOException;
53+
import java.io.ObjectInputStream;
5354
import java.text.DateFormat;
5455
import java.text.ParseException;
5556
import java.text.SimpleDateFormat;
@@ -700,4 +701,9 @@ public ImpersonatedCredentials build() {
700701
return new ImpersonatedCredentials(this);
701702
}
702703
}
704+
705+
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
706+
input.defaultReadObject();
707+
transportFactory = newInstance(transportFactoryClassName);
708+
}
703709
}

oauth2_http/java/com/google/auth/oauth2/SystemEnvironmentProvider.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.google.auth.oauth2;
22

3+
import java.io.Serializable;
4+
35
/** Represents the default system environment provider. */
4-
class SystemEnvironmentProvider implements EnvironmentProvider {
6+
class SystemEnvironmentProvider implements EnvironmentProvider, Serializable {
57
static final SystemEnvironmentProvider INSTANCE = new SystemEnvironmentProvider();
8+
private static final long serialVersionUID = -4698164985883575244L;
69

710
private SystemEnvironmentProvider() {}
811

oauth2_http/javatests/com/google/auth/oauth2/AwsCredentialsTest.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.google.api.client.json.GenericJson;
4343
import com.google.api.client.json.JsonParser;
4444
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
45+
import com.google.api.client.util.Clock;
4546
import com.google.auth.TestUtils;
4647
import com.google.auth.oauth2.AwsCredentials.AwsCredentialSource;
4748
import com.google.auth.oauth2.ExternalAccountCredentialsTest.MockExternalAccountCredentialsTransportFactory;
@@ -62,7 +63,7 @@
6263

6364
/** Tests for {@link AwsCredentials}. */
6465
@RunWith(JUnit4.class)
65-
public class AwsCredentialsTest {
66+
public class AwsCredentialsTest extends BaseSerializationTest {
6667

6768
private static final String STS_URL = "https://sts.googleapis.com";
6869
private static final String AWS_CREDENTIALS_URL = "https://169.254.169.254";
@@ -1025,6 +1026,34 @@ public void builder() {
10251026
assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance());
10261027
}
10271028

1029+
@Test
1030+
public void serialize() throws IOException, ClassNotFoundException {
1031+
List<String> scopes = Arrays.asList("scope1", "scope2");
1032+
1033+
AwsCredentials testCredentials =
1034+
(AwsCredentials)
1035+
AwsCredentials.newBuilder()
1036+
.setHttpTransportFactory(OAuth2Utils.HTTP_TRANSPORT_FACTORY)
1037+
.setAudience("audience")
1038+
.setSubjectTokenType("subjectTokenType")
1039+
.setTokenUrl(STS_URL)
1040+
.setTokenInfoUrl("tokenInfoUrl")
1041+
.setCredentialSource(AWS_CREDENTIAL_SOURCE)
1042+
.setTokenInfoUrl("tokenInfoUrl")
1043+
.setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
1044+
.setQuotaProjectId("quotaProjectId")
1045+
.setClientId("clientId")
1046+
.setClientSecret("clientSecret")
1047+
.setScopes(scopes)
1048+
.build();
1049+
1050+
AwsCredentials deserializedCredentials = serializeAndDeserialize(testCredentials);
1051+
assertEquals(testCredentials, deserializedCredentials);
1052+
assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode());
1053+
assertEquals(testCredentials.toString(), deserializedCredentials.toString());
1054+
assertSame(deserializedCredentials.clock, Clock.SYSTEM);
1055+
}
1056+
10281057
private static void ValidateRequest(
10291058
MockLowLevelHttpRequest request, String expectedUrl, Map<String, String> expectedHeaders) {
10301059
assertEquals(expectedUrl, request.getUrl());

oauth2_http/javatests/com/google/auth/oauth2/ExternalAccountCredentialsTest.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@
3535
import static org.junit.Assert.assertEquals;
3636
import static org.junit.Assert.assertNotNull;
3737
import static org.junit.Assert.assertNull;
38+
import static org.junit.Assert.assertSame;
3839
import static org.junit.Assert.assertTrue;
3940
import static org.junit.Assert.fail;
4041

4142
import com.google.api.client.http.HttpTransport;
4243
import com.google.api.client.json.GenericJson;
44+
import com.google.api.client.util.Clock;
4345
import com.google.auth.TestUtils;
4446
import com.google.auth.http.HttpTransportFactory;
4547
import com.google.auth.oauth2.ExternalAccountCredentialsTest.TestExternalAccountCredentials.TestCredentialSource;
@@ -62,7 +64,7 @@
6264

6365
/** Tests for {@link ExternalAccountCredentials}. */
6466
@RunWith(JUnit4.class)
65-
public class ExternalAccountCredentialsTest {
67+
public class ExternalAccountCredentialsTest extends BaseSerializationTest {
6668

6769
private static final String STS_URL = "https://sts.googleapis.com";
6870

@@ -954,6 +956,37 @@ public void getRequestMetadata_withQuotaProjectId() throws IOException {
954956
assertEquals("quotaProjectId", requestMetadata.get("x-goog-user-project").get(0));
955957
}
956958

959+
@Test
960+
public void serialize() throws IOException, ClassNotFoundException {
961+
Map<String, Object> impersonationOpts =
962+
new HashMap<String, Object>() {
963+
{
964+
put("token_lifetime_seconds", 1000);
965+
}
966+
};
967+
968+
TestExternalAccountCredentials testCredentials =
969+
(TestExternalAccountCredentials)
970+
TestExternalAccountCredentials.newBuilder()
971+
.setHttpTransportFactory(transportFactory)
972+
.setAudience("audience")
973+
.setSubjectTokenType("subjectTokenType")
974+
.setTokenUrl(STS_URL)
975+
.setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
976+
.setServiceAccountImpersonationOptions(impersonationOpts)
977+
.build();
978+
979+
TestExternalAccountCredentials deserializedCredentials =
980+
serializeAndDeserialize(testCredentials);
981+
assertEquals(testCredentials, deserializedCredentials);
982+
assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode());
983+
assertEquals(testCredentials.toString(), deserializedCredentials.toString());
984+
assertEquals(
985+
testCredentials.getServiceAccountImpersonationOptions().getLifetime(),
986+
deserializedCredentials.getServiceAccountImpersonationOptions().getLifetime());
987+
assertSame(deserializedCredentials.clock, Clock.SYSTEM);
988+
}
989+
957990
@Test
958991
public void validateTokenUrl_validUrls() {
959992
List<String> validUrls =

oauth2_http/javatests/com/google/auth/oauth2/GoogleCredentialsTest.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,11 @@
3131

3232
package com.google.auth.oauth2;
3333

34-
import static org.junit.Assert.assertEquals;
35-
import static org.junit.Assert.assertFalse;
36-
import static org.junit.Assert.assertNotNull;
37-
import static org.junit.Assert.assertNull;
38-
import static org.junit.Assert.assertTrue;
39-
import static org.junit.Assert.fail;
34+
import static org.junit.Assert.*;
4035

4136
import com.google.api.client.http.HttpTransport;
4237
import com.google.api.client.testing.http.MockHttpTransport;
38+
import com.google.api.client.util.Clock;
4339
import com.google.auth.TestUtils;
4440
import com.google.auth.http.HttpTransportFactory;
4541
import com.google.auth.oauth2.ExternalAccountAuthorizedUserCredentialsTest.MockExternalAccountAuthorizedUserCredentialsTransportFactory;
@@ -62,7 +58,7 @@
6258

6359
/** Test case for {@link GoogleCredentials}. */
6460
@RunWith(JUnit4.class)
65-
public class GoogleCredentialsTest {
61+
public class GoogleCredentialsTest extends BaseSerializationTest {
6662

6763
private static final String SA_CLIENT_EMAIL =
6864
"36680232662-vrd7ji19qe3nelgchd0ah2csanun6bnr@developer.gserviceaccount.com";
@@ -589,6 +585,16 @@ public void createWithQuotaProject() {
589585
assertEquals(null, sameCredentials.getQuotaProjectId());
590586
}
591587

588+
@Test
589+
public void serialize() throws IOException, ClassNotFoundException {
590+
final GoogleCredentials testCredentials = new GoogleCredentials.Builder().build();
591+
GoogleCredentials deserializedCredentials = serializeAndDeserialize(testCredentials);
592+
assertEquals(testCredentials, deserializedCredentials);
593+
assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode());
594+
assertEquals(testCredentials.toString(), deserializedCredentials.toString());
595+
assertSame(deserializedCredentials.clock, Clock.SYSTEM);
596+
}
597+
592598
private static void testFromStreamException(InputStream stream, String expectedMessageContent) {
593599
try {
594600
GoogleCredentials.fromStream(stream, DUMMY_TRANSPORT_FACTORY);

oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,11 @@
3333

3434
import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL;
3535
import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY;
36-
import static org.junit.Assert.assertEquals;
37-
import static org.junit.Assert.assertNotNull;
38-
import static org.junit.Assert.assertTrue;
39-
import static org.junit.Assert.fail;
36+
import static org.junit.Assert.*;
4037

4138
import com.google.api.client.http.HttpTransport;
4239
import com.google.api.client.json.GenericJson;
40+
import com.google.api.client.util.Clock;
4341
import com.google.auth.TestUtils;
4442
import com.google.auth.http.HttpTransportFactory;
4543
import com.google.auth.oauth2.IdentityPoolCredentials.IdentityPoolCredentialSource;
@@ -59,7 +57,7 @@
5957

6058
/** Tests for {@link IdentityPoolCredentials}. */
6159
@RunWith(JUnit4.class)
62-
public class IdentityPoolCredentialsTest {
60+
public class IdentityPoolCredentialsTest extends BaseSerializationTest {
6361

6462
private static final String STS_URL = "https://sts.googleapis.com";
6563

@@ -732,6 +730,24 @@ public void builder_emptyWorkforceUserProjectWithWorkforceAudience() {
732730
assertTrue(credentials.isWorkforcePoolConfiguration());
733731
}
734732

733+
@Test
734+
public void serialize() throws IOException, ClassNotFoundException {
735+
IdentityPoolCredentials testCredentials =
736+
(IdentityPoolCredentials)
737+
IdentityPoolCredentials.newBuilder(FILE_SOURCED_CREDENTIAL)
738+
.setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
739+
.setQuotaProjectId("quotaProjectId")
740+
.setClientId("clientId")
741+
.setClientSecret("clientSecret")
742+
.build();
743+
744+
IdentityPoolCredentials deserializedCredentials = serializeAndDeserialize(testCredentials);
745+
assertEquals(testCredentials, deserializedCredentials);
746+
assertEquals(testCredentials.hashCode(), deserializedCredentials.hashCode());
747+
assertEquals(testCredentials.toString(), deserializedCredentials.toString());
748+
assertSame(deserializedCredentials.clock, Clock.SYSTEM);
749+
}
750+
735751
static InputStream writeIdentityPoolCredentialsStream(
736752
String tokenUrl,
737753
String url,

oauth2_http/javatests/com/google/auth/oauth2/PluggableAuthCredentialsTest.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@
3232
package com.google.auth.oauth2;
3333

3434
import static com.google.auth.oauth2.MockExternalAccountCredentialsTransport.SERVICE_ACCOUNT_IMPERSONATION_URL;
35-
import static org.junit.Assert.assertEquals;
36-
import static org.junit.Assert.assertNull;
37-
import static org.junit.Assert.fail;
35+
import static org.junit.Assert.*;
3836

3937
import com.google.api.client.http.HttpTransport;
4038
import com.google.api.client.json.GenericJson;
@@ -45,6 +43,7 @@
4543
import com.google.auth.oauth2.PluggableAuthCredentials.PluggableAuthCredentialSource;
4644
import java.io.IOException;
4745
import java.io.InputStream;
46+
import java.io.NotSerializableException;
4847
import java.math.BigDecimal;
4948
import java.util.Arrays;
5049
import java.util.HashMap;
@@ -54,7 +53,7 @@
5453
import org.junit.Test;
5554

5655
/** Tests for {@link PluggableAuthCredentials}. */
57-
public class PluggableAuthCredentialsTest {
56+
public class PluggableAuthCredentialsTest extends BaseSerializationTest {
5857
// The default timeout for waiting for the executable to finish (30 seconds).
5958
private static final int DEFAULT_EXECUTABLE_TIMEOUT_MS = 30 * 1000;
6059
// The minimum timeout for waiting for the executable to finish (5 seconds).
@@ -436,6 +435,22 @@ public void createdScoped_clonedCredentialWithAddedScopes() {
436435
assertEquals(credentials.getExecutableHandler(), newCredentials.getExecutableHandler());
437436
}
438437

438+
@Test
439+
public void serialize() throws IOException, ClassNotFoundException {
440+
PluggableAuthCredentials testCredentials =
441+
(PluggableAuthCredentials)
442+
PluggableAuthCredentials.newBuilder(CREDENTIAL)
443+
.setExecutableHandler(options -> "pluggableAuthToken")
444+
.setServiceAccountImpersonationUrl(SERVICE_ACCOUNT_IMPERSONATION_URL)
445+
.setQuotaProjectId("quotaProjectId")
446+
.setClientId("clientId")
447+
.setClientSecret("clientSecret")
448+
.build();
449+
450+
// PluggableAuthCredentials are not serializable
451+
assertThrows(NotSerializableException.class, () -> serializeAndDeserialize(testCredentials));
452+
}
453+
439454
private static CredentialSource buildCredentialSource() {
440455
return buildCredentialSource("command", null, null);
441456
}

0 commit comments

Comments
 (0)