Skip to content

Commit 7d3458f

Browse files
committed
Merge remote-tracking branch 'origin/main' into b/488439640
2 parents 1d02562 + b2f10ae commit 7d3458f

52 files changed

Lines changed: 2275 additions & 2694 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.kokoro/build.sh

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,28 @@ graalvm)
8080
# Run Unit and Integration Tests with Native Image
8181
bash .kokoro/populate-secrets.sh
8282
export GOOGLE_APPLICATION_CREDENTIALS="${KOKORO_GFILE_DIR}/secret_manager/java-it-service-account"
83-
mvn -B ${INTEGRATION_TEST_ARGS} -ntp -Pnative -Pnative-test -Pslf4j2x test -pl 'oauth2_http'
83+
84+
# GraalVM Native Image ignores JAVA_TOOL_OPTIONS at runtime. If the CI environment
85+
# injects a custom truststore (e.g., for a proxy) via JAVA_TOOL_OPTIONS, we need to
86+
# extract it and pass it explicitly to the native executable.
87+
TRUST_STORE=""
88+
if [[ "${JAVA_TOOL_OPTIONS}" =~ -Djavax.net.ssl.trustStore=([^ ]+) ]]; then
89+
TRUST_STORE="${BASH_REMATCH[1]}"
90+
fi
91+
92+
# We use 'package' instead of 'test' to build the native image without automatically
93+
# running it via the Maven plugin. This allows us to run it manually with the
94+
# extracted truststore argument in the next step, avoiding complex Maven XML overrides.
95+
mvn -B ${INTEGRATION_TEST_ARGS} -ntp -Pnative -Pnative-test -Pslf4j2x package -pl 'oauth2_http'
96+
97+
# Run the native tests manually with the truststore
98+
CMD="./oauth2_http/target/native-tests --xml-output-dir ./oauth2_http/target/native-test-reports"
99+
if [ -n "$TRUST_STORE" ]; then
100+
CMD="$CMD -Djavax.net.ssl.trustStore=$TRUST_STORE"
101+
fi
102+
103+
echo "Executing: $CMD"
104+
$CMD
84105
RETURN_CODE=$?
85106
;;
86107
samples)

appengine/javatests/com/google/auth/appengine/AppEngineCredentialsTest.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
import static org.junit.jupiter.api.Assertions.assertNotEquals;
3737
import static org.junit.jupiter.api.Assertions.assertNotNull;
3838
import static org.junit.jupiter.api.Assertions.assertNotSame;
39+
import static org.junit.jupiter.api.Assertions.assertThrows;
3940
import static org.junit.jupiter.api.Assertions.assertTrue;
40-
import static org.junit.jupiter.api.Assertions.fail;
4141

4242
import com.google.auth.Credentials;
4343
import com.google.auth.oauth2.AccessToken;
@@ -135,11 +135,7 @@ void createScoped_clonesWithScopes() throws IOException {
135135
.setAppIdentityService(appIdentity)
136136
.build();
137137
assertTrue(credentials.createScopedRequired());
138-
try {
139-
credentials.getRequestMetadata(CALL_URI);
140-
fail("Should not be able to use credential without scopes.");
141-
} catch (Exception expected) {
142-
}
138+
assertThrows(IOException.class, () -> credentials.getRequestMetadata(CALL_URI));
143139
assertEquals(0, appIdentity.getGetAccessTokenCallCount());
144140

145141
GoogleCredentials scopedCredentials = credentials.createScoped(SCOPES);

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ public class IdTokenCredentials extends OAuth2Credentials {
105105

106106
private static final long serialVersionUID = -2133257318957588431L;
107107

108-
private IdTokenProvider idTokenProvider;
108+
private final IdTokenProvider idTokenProvider;
109+
private final List<IdTokenProvider.Option> options;
109110
private String targetAudience;
110-
private List<IdTokenProvider.Option> options;
111111

112112
private IdTokenCredentials(Builder builder) {
113113
this.idTokenProvider = Preconditions.checkNotNull(builder.getIdTokenProvider());
@@ -131,7 +131,7 @@ public IdToken getIdToken() {
131131

132132
@Override
133133
public int hashCode() {
134-
return Objects.hash(options, targetAudience);
134+
return Objects.hash(idTokenProvider, options, targetAudience);
135135
}
136136

137137
@Override
@@ -146,7 +146,8 @@ public boolean equals(Object obj) {
146146
}
147147
IdTokenCredentials other = (IdTokenCredentials) obj;
148148
return Objects.equals(this.idTokenProvider, other.idTokenProvider)
149-
&& Objects.equals(this.targetAudience, other.targetAudience);
149+
&& Objects.equals(this.targetAudience, other.targetAudience)
150+
&& Objects.equals(this.options, other.options);
150151
}
151152

152153
@Override
@@ -169,16 +170,29 @@ public static class Builder extends OAuth2Credentials.Builder {
169170

170171
protected Builder() {}
171172

173+
/**
174+
* Sets the provider for the ID token.
175+
*
176+
* @param idTokenProvider the provider for the ID token, cannot be null
177+
* @return the builder object
178+
*/
172179
@CanIgnoreReturnValue
173180
public Builder setIdTokenProvider(IdTokenProvider idTokenProvider) {
174-
this.idTokenProvider = idTokenProvider;
181+
this.idTokenProvider = Preconditions.checkNotNull(idTokenProvider);
175182
return this;
176183
}
177184

178185
public IdTokenProvider getIdTokenProvider() {
179186
return this.idTokenProvider;
180187
}
181188

189+
/**
190+
* Sets the target audience for the ID token.
191+
*
192+
* @param targetAudience the target audience, cannot be null for non-UserCredentials. If set for
193+
* UserCredentials, the value will be ignored.
194+
* @return the builder object
195+
*/
182196
@CanIgnoreReturnValue
183197
public Builder setTargetAudience(String targetAudience) {
184198
this.targetAudience = targetAudience;
@@ -189,6 +203,12 @@ public String getTargetAudience() {
189203
return this.targetAudience;
190204
}
191205

206+
/**
207+
* Sets the options for the ID token.
208+
*
209+
* @param options list of options, can be null or empty if no options are needed.
210+
* @return the builder object
211+
*/
192212
@CanIgnoreReturnValue
193213
public Builder setOptions(List<IdTokenProvider.Option> options) {
194214
this.options = options;

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

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.google.api.client.json.JsonObjectParser;
4646
import com.google.api.client.util.GenericData;
4747
import com.google.api.core.InternalApi;
48+
import com.google.api.core.ObsoleteApi;
4849
import com.google.auth.CredentialTypeForMetrics;
4950
import com.google.auth.ServiceAccountSigner;
5051
import com.google.auth.http.HttpCredentialsAdapter;
@@ -59,9 +60,9 @@
5960
import java.io.IOException;
6061
import java.io.InputStream;
6162
import java.io.ObjectInputStream;
62-
import java.text.DateFormat;
63-
import java.text.ParseException;
64-
import java.text.SimpleDateFormat;
63+
import java.time.DateTimeException;
64+
import java.time.Instant;
65+
import java.time.format.DateTimeFormatter;
6566
import java.util.ArrayList;
6667
import java.util.Calendar;
6768
import java.util.Collection;
@@ -101,7 +102,6 @@ public class ImpersonatedCredentials extends GoogleCredentials
101102
implements ServiceAccountSigner, IdTokenProvider {
102103

103104
private static final long serialVersionUID = -2133257318957488431L;
104-
private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ssX";
105105
private static final int TWELVE_HOURS_IN_SECONDS = 43200;
106106
private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600;
107107
private GoogleCredentials sourceCredentials;
@@ -510,12 +510,16 @@ public CredentialTypeForMetrics getMetricsCredentialType() {
510510
}
511511

512512
/**
513-
* Clones the impersonated credentials with a new calendar.
513+
* This method is marked obsolete. There is no alternative to setting a custom calendar for the
514+
* Credential.
515+
*
516+
* <p>Clones the impersonated credentials with a new calendar.
514517
*
515518
* @param calendar the calendar that will be used by the new ImpersonatedCredentials instance when
516519
* parsing the received expiration time of the refreshed access token
517520
* @return the cloned impersonated credentials with the given custom calendar
518521
*/
522+
@ObsoleteApi("This method is obsolete and will be removed in a future release.")
519523
public ImpersonatedCredentials createWithCustomCalendar(Calendar calendar) {
520524
return toBuilder()
521525
.setScopes(this.scopes)
@@ -660,14 +664,23 @@ public AccessToken refreshAccessToken() throws IOException {
660664
String expireTime =
661665
OAuth2Utils.validateString(responseData, "expireTime", "Expected to find an expireTime");
662666

663-
DateFormat format = new SimpleDateFormat(RFC3339);
664-
format.setCalendar(calendar);
667+
Instant expirationInstant;
665668
try {
666-
Date date = format.parse(expireTime);
667-
return new AccessToken(accessToken, date);
668-
} catch (ParseException pe) {
669-
throw new IOException("Error parsing expireTime: " + pe.getMessage());
669+
if (calendar != null) {
670+
// For backward compatibility, if a custom calendar is set, use its timezone
671+
// and convert it to an Instant
672+
expirationInstant =
673+
Instant.from(
674+
DateTimeFormatter.ISO_INSTANT
675+
.withZone(calendar.getTimeZone().toZoneId())
676+
.parse(expireTime));
677+
} else {
678+
expirationInstant = Instant.parse(expireTime);
679+
}
680+
} catch (DateTimeException e) {
681+
throw new IOException("Error parsing expireTime: " + expireTime, e);
670682
}
683+
return new AccessToken(accessToken, Date.from(expirationInstant));
671684
}
672685

673686
/**
@@ -883,7 +896,17 @@ public Builder setIamEndpointOverride(String iamEndpointOverride) {
883896
return this;
884897
}
885898

899+
/**
900+
* This method is marked obsolete. There is no alternative to setting a custom calendar for the
901+
* Credential.
902+
*
903+
* <p>Sets the calendar to be used for parsing the expiration time.
904+
*
905+
* @param calendar the calendar to use
906+
* @return the builder
907+
*/
886908
@CanIgnoreReturnValue
909+
@ObsoleteApi("This method is obsolete and will be removed in a future release.")
887910
public Builder setCalendar(Calendar calendar) {
888911
this.calendar = calendar;
889912
return this;
@@ -903,6 +926,15 @@ public Builder setReadTimeout(int readTimeout) {
903926
return this;
904927
}
905928

929+
/**
930+
* This method is marked obsolete. There is no alternative to getting a custom calendar for the
931+
* Credential.
932+
*
933+
* <p>Returns the calendar to be used for parsing the expiration time.
934+
*
935+
* @return the calendar
936+
*/
937+
@ObsoleteApi("This method is obsolete and will be removed in a future release.")
906938
public Calendar getCalendar() {
907939
return this.calendar;
908940
}

oauth2_http/javatests/com/google/auth/TestUtils.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
import java.io.ByteArrayInputStream;
4747
import java.io.IOException;
4848
import java.io.InputStream;
49-
import java.io.UnsupportedEncodingException;
5049
import java.net.URI;
5150
import java.net.URLDecoder;
51+
import java.nio.charset.StandardCharsets;
5252
import java.text.SimpleDateFormat;
5353
import java.util.Calendar;
5454
import java.util.Date;
@@ -90,15 +90,11 @@ private static boolean hasBearerToken(Map<String, List<String>> metadata, String
9090
public static InputStream jsonToInputStream(GenericJson json) throws IOException {
9191
json.setFactory(JSON_FACTORY);
9292
String text = json.toPrettyString();
93-
return new ByteArrayInputStream(text.getBytes("UTF-8"));
93+
return new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
9494
}
9595

9696
public static InputStream stringToInputStream(String text) {
97-
try {
98-
return new ByteArrayInputStream(text.getBytes("UTF-8"));
99-
} catch (UnsupportedEncodingException e) {
100-
throw new RuntimeException("Unexpected encoding exception", e);
101-
}
97+
return new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
10298
}
10399

104100
/**

oauth2_http/javatests/com/google/auth/http/HttpCredentialsAdapterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ void initialize_populatesOAuth2Credentials() throws IOException {
7979

8080
HttpHeaders requestHeaders = request.getHeaders();
8181
String authorizationHeader = requestHeaders.getAuthorization();
82-
assertEquals(authorizationHeader, expectedAuthorization);
82+
assertEquals(expectedAuthorization, authorizationHeader);
8383
}
8484

8585
@Test

oauth2_http/javatests/com/google/auth/mtls/SecureConnectProviderTest.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.io.IOException;
3939
import java.io.InputStream;
4040
import java.io.OutputStream;
41-
import java.security.GeneralSecurityException;
4241
import java.util.List;
4342
import org.junit.jupiter.api.Test;
4443

@@ -70,7 +69,7 @@ public InputStream getErrorStream() {
7069
}
7170

7271
@Override
73-
public int waitFor() throws InterruptedException {
72+
public int waitFor() {
7473
return 0;
7574
}
7675

@@ -83,7 +82,9 @@ public int exitValue() {
8382
}
8483

8584
@Override
86-
public void destroy() {}
85+
public void destroy() {
86+
// Nothing was initialized and nothing needs to be destroyed
87+
}
8788
}
8889

8990
static class TestProcessProvider implements SecureConnectProvider.ProcessProvider {
@@ -102,19 +103,22 @@ public Process createProcess(InputStream metadata) throws IOException {
102103

103104
@Test
104105
void testGetKeyStoreNonZeroExitCode() {
105-
InputStream metadata =
106+
try (InputStream metadata =
106107
this.getClass()
107108
.getClassLoader()
108-
.getResourceAsStream("com/google/api/gax/rpc/mtls/mtlsCertAndKey.pem");
109-
IOException actual =
110-
assertThrows(
111-
IOException.class,
112-
() -> SecureConnectProvider.getKeyStore(metadata, new TestProcessProvider(1)));
113-
assertTrue(
114-
actual
115-
.getMessage()
116-
.contains("SecureConnect: Cert provider command failed with exit code: 1"),
117-
"expected to fail with nonzero exit code");
109+
.getResourceAsStream("com/google/api/gax/rpc/mtls/mtlsCertAndKey.pem")) {
110+
IOException actual =
111+
assertThrows(
112+
IOException.class,
113+
() -> SecureConnectProvider.getKeyStore(metadata, new TestProcessProvider(1)));
114+
assertTrue(
115+
actual
116+
.getMessage()
117+
.contains("SecureConnect: Cert provider command failed with exit code: 1"),
118+
"expected to fail with nonzero exit code");
119+
} catch (IOException e) {
120+
throw new RuntimeException(e);
121+
}
118122
}
119123

120124
@Test
@@ -147,8 +151,7 @@ void testRunCertificateProviderCommandTimeout() {
147151
}
148152

149153
@Test
150-
void testGetKeyStore_FileNotFoundException()
151-
throws IOException, GeneralSecurityException, InterruptedException {
154+
void testGetKeyStore_FileNotFoundException() {
152155
SecureConnectProvider provider =
153156
new SecureConnectProvider(new TestProcessProvider(0), "/invalid/metadata/path.json");
154157

0 commit comments

Comments
 (0)