Skip to content

Commit b389d03

Browse files
authored
Merge branch 'main' into upgrade-grpc-gcp
2 parents 53db19a + facb2f6 commit b389d03

File tree

188 files changed

+31489
-3405
lines changed

Some content is hidden

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

188 files changed

+31489
-3405
lines changed

gapic-libraries-bom/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@
133133
<type>pom</type>
134134
<scope>import</scope>
135135
</dependency>
136+
<dependency>
137+
<groupId>com.google.cloud</groupId>
138+
<artifactId>google-cloud-appoptimize-bom</artifactId>
139+
<version>0.0.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-appoptimize:current} -->
140+
<type>pom</type>
141+
<scope>import</scope>
142+
</dependency>
136143
<dependency>
137144
<groupId>com.google.cloud</groupId>
138145
<artifactId>google-cloud-artifact-registry-bom</artifactId>

generation_config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,23 @@ libraries:
183183
rpc_documentation: https://cloud.google.com/app-hub/docs/reference/rpc
184184
GAPICs:
185185
- proto_path: google/cloud/apphub/v1
186+
- api_shortname: appoptimize
187+
name_pretty: App Optimize API
188+
product_documentation: https://docs.cloud.google.com/app-optimize/overview
189+
api_description: The App Optimize API provides developers and platform teams with
190+
tools to monitor, analyze, and improve the performance and cost-efficiency of
191+
their cloud applications.
192+
client_documentation:
193+
https://cloud.google.com/java/docs/reference/google-cloud-appoptimize/latest/overview
194+
release_level: preview
195+
distribution_name: com.google.cloud:google-cloud-appoptimize
196+
api_id: appoptimize.googleapis.com
197+
library_type: GAPIC_AUTO
198+
group_id: com.google.cloud
199+
cloud_api: true
200+
GAPICs:
201+
- proto_path: google/cloud/appoptimize/v1beta
202+
requires_billing: true
186203
- api_shortname: area120tables
187204
name_pretty: Area 120 Tables
188205
product_documentation: https://area120.google.com/

google-auth-library-java/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);

google-auth-library-java/cab-token-generator/java/com/google/auth/credentialaccessboundary/ClientSideCredentialAccessBoundaryFactory.java

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import com.google.common.annotations.VisibleForTesting;
5454
import com.google.common.base.Strings;
5555
import com.google.common.util.concurrent.AbstractFuture;
56-
import com.google.common.util.concurrent.FutureCallback;
5756
import com.google.common.util.concurrent.Futures;
5857
import com.google.common.util.concurrent.ListenableFuture;
5958
import com.google.common.util.concurrent.ListenableFutureTask;
@@ -79,7 +78,6 @@
7978
import java.util.Date;
8079
import java.util.List;
8180
import java.util.concurrent.ExecutionException;
82-
import javax.annotation.Nullable;
8381

8482
/**
8583
* A factory for generating downscoped access tokens using a client-side approach.
@@ -248,7 +246,7 @@ void refreshCredentialsIfRequired() throws IOException {
248246
}
249247
try {
250248
// Wait for the refresh task to complete.
251-
currentRefreshTask.task.get();
249+
currentRefreshTask.get();
252250
} catch (InterruptedException e) {
253251
// Restore the interrupted status and throw an exception.
254252
Thread.currentThread().interrupt();
@@ -495,31 +493,18 @@ class RefreshTask extends AbstractFuture<IntermediateCredentials> implements Run
495493
this.task = task;
496494
this.isNew = isNew;
497495

498-
// Add listener to update factory's credentials when the task completes.
496+
// Single listener to guarantee that finishRefreshTask updates the internal state BEFORE
497+
// the outer future completes and unblocks waiters.
499498
task.addListener(
500499
() -> {
501500
try {
502501
finishRefreshTask(task);
502+
RefreshTask.this.set(Futures.getDone(task));
503503
} catch (ExecutionException e) {
504504
Throwable cause = e.getCause();
505-
RefreshTask.this.setException(cause);
506-
}
507-
},
508-
MoreExecutors.directExecutor());
509-
510-
// Add callback to set the result or exception based on the outcome.
511-
Futures.addCallback(
512-
task,
513-
new FutureCallback<IntermediateCredentials>() {
514-
@Override
515-
public void onSuccess(IntermediateCredentials result) {
516-
RefreshTask.this.set(result);
517-
}
518-
519-
@Override
520-
public void onFailure(@Nullable Throwable t) {
521-
RefreshTask.this.setException(
522-
t != null ? t : new IOException("Refresh failed with null Throwable."));
505+
RefreshTask.this.setException(cause != null ? cause : e);
506+
} catch (Throwable t) {
507+
RefreshTask.this.setException(t);
523508
}
524509
},
525510
MoreExecutors.directExecutor());

google-auth-library-java/cab-token-generator/javatests/com/google/auth/credentialaccessboundary/ClientSideCredentialAccessBoundaryFactoryTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,4 +988,59 @@ void generateToken_withMalformSessionKey_failure() throws Exception {
988988

989989
assertThrows(GeneralSecurityException.class, () -> factory.generateToken(accessBoundary));
990990
}
991+
992+
@Test
993+
void generateToken_freshInstance_concurrent_noNpe() throws Exception {
994+
for (int run = 0; run < 10; run++) { // Run 10 times in a single test instance to save time
995+
GoogleCredentials sourceCredentials =
996+
getServiceAccountSourceCredentials(mockTokenServerTransportFactory);
997+
ClientSideCredentialAccessBoundaryFactory factory =
998+
ClientSideCredentialAccessBoundaryFactory.newBuilder()
999+
.setSourceCredential(sourceCredentials)
1000+
.setHttpTransportFactory(mockStsTransportFactory)
1001+
.build();
1002+
1003+
CredentialAccessBoundary.Builder cabBuilder = CredentialAccessBoundary.newBuilder();
1004+
CredentialAccessBoundary accessBoundary =
1005+
cabBuilder
1006+
.addRule(
1007+
CredentialAccessBoundary.AccessBoundaryRule.newBuilder()
1008+
.setAvailableResource("resource")
1009+
.setAvailablePermissions(ImmutableList.of("role"))
1010+
.build())
1011+
.build();
1012+
1013+
int numThreads = 5;
1014+
CountDownLatch latch = new CountDownLatch(numThreads);
1015+
java.util.concurrent.atomic.AtomicInteger npeCount =
1016+
new java.util.concurrent.atomic.AtomicInteger();
1017+
java.util.concurrent.ExecutorService executor =
1018+
java.util.concurrent.Executors.newFixedThreadPool(numThreads);
1019+
1020+
try {
1021+
for (int i = 0; i < numThreads; i++) {
1022+
executor.submit(
1023+
() -> {
1024+
try {
1025+
latch.countDown();
1026+
latch.await();
1027+
factory.generateToken(accessBoundary);
1028+
} catch (NullPointerException e) {
1029+
npeCount.incrementAndGet();
1030+
} catch (Exception e) {
1031+
// Ignore other exceptions for the sake of the race reproduction
1032+
}
1033+
});
1034+
}
1035+
} finally {
1036+
executor.shutdown();
1037+
executor.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS);
1038+
}
1039+
1040+
org.junit.jupiter.api.Assertions.assertEquals(
1041+
0,
1042+
npeCount.get(),
1043+
"Expected zero NullPointerExceptions due to the race condition, but some were thrown.");
1044+
}
1045+
}
9911046
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.auth.mtls;
32+
33+
import com.google.api.core.InternalApi;
34+
import com.google.auth.oauth2.EnvironmentProvider;
35+
import com.google.auth.oauth2.PropertyProvider;
36+
import com.google.common.base.Strings;
37+
import java.io.File;
38+
import java.io.FileInputStream;
39+
import java.io.IOException;
40+
import java.io.InputStream;
41+
import java.util.Locale;
42+
43+
/**
44+
* Utility class for mTLS related operations.
45+
*
46+
* <p>For internal use only.
47+
*/
48+
@InternalApi
49+
public class MtlsUtils {
50+
static final String CERTIFICATE_CONFIGURATION_ENV_VARIABLE = "GOOGLE_API_CERTIFICATE_CONFIG";
51+
static final String WELL_KNOWN_CERTIFICATE_CONFIG_FILE = "certificate_config.json";
52+
static final String CLOUDSDK_CONFIG_DIRECTORY = "gcloud";
53+
54+
private MtlsUtils() {
55+
// Prevent instantiation for Utility class
56+
}
57+
58+
/**
59+
* Returns the path to the client certificate file specified by the loaded workload certificate
60+
* configuration.
61+
*
62+
* @return The path to the certificate file.
63+
* @throws IOException if the certificate configuration cannot be found or loaded.
64+
*/
65+
public static String getCertificatePath(
66+
EnvironmentProvider envProvider, PropertyProvider propProvider, String certConfigPathOverride)
67+
throws IOException {
68+
String certPath =
69+
getWorkloadCertificateConfiguration(envProvider, propProvider, certConfigPathOverride)
70+
.getCertPath();
71+
if (Strings.isNullOrEmpty(certPath)) {
72+
throw new CertificateSourceUnavailableException(
73+
"Certificate configuration loaded successfully, but does not contain a 'certificate_file' path.");
74+
}
75+
return certPath;
76+
}
77+
78+
/**
79+
* Resolves and loads the workload certificate configuration.
80+
*
81+
* <p>The configuration file is resolved in the following order of precedence: 1. The provided
82+
* certConfigPathOverride (if not null). 2. The path specified by the
83+
* GOOGLE_API_CERTIFICATE_CONFIG environment variable. 3. The well-known certificate configuration
84+
* file in the gcloud config directory.
85+
*
86+
* @param envProvider the environment provider to use for resolving environment variables
87+
* @param propProvider the property provider to use for resolving system properties
88+
* @param certConfigPathOverride optional override path for the configuration file
89+
* @return the loaded WorkloadCertificateConfiguration
90+
* @throws IOException if the configuration file cannot be found, read, or parsed
91+
*/
92+
static WorkloadCertificateConfiguration getWorkloadCertificateConfiguration(
93+
EnvironmentProvider envProvider, PropertyProvider propProvider, String certConfigPathOverride)
94+
throws IOException {
95+
File certConfig;
96+
if (certConfigPathOverride != null) {
97+
certConfig = new File(certConfigPathOverride);
98+
} else {
99+
String envCredentialsPath = envProvider.getEnv(CERTIFICATE_CONFIGURATION_ENV_VARIABLE);
100+
if (!Strings.isNullOrEmpty(envCredentialsPath)) {
101+
certConfig = new File(envCredentialsPath);
102+
} else {
103+
certConfig = getWellKnownCertificateConfigFile(envProvider, propProvider);
104+
}
105+
}
106+
107+
if (!certConfig.isFile()) {
108+
throw new CertificateSourceUnavailableException(
109+
"Certificate configuration file does not exist or is not a file: "
110+
+ certConfig.getAbsolutePath());
111+
}
112+
try (InputStream certConfigStream = new FileInputStream(certConfig)) {
113+
return WorkloadCertificateConfiguration.fromCertificateConfigurationStream(certConfigStream);
114+
}
115+
}
116+
117+
private static File getWellKnownCertificateConfigFile(
118+
EnvironmentProvider envProvider, PropertyProvider propProvider) throws IOException {
119+
File cloudConfigPath;
120+
String envPath = envProvider.getEnv("CLOUDSDK_CONFIG");
121+
if (envPath != null) {
122+
cloudConfigPath = new File(envPath);
123+
} else {
124+
String osName = propProvider.getProperty("os.name", "").toLowerCase(Locale.US);
125+
if (osName.indexOf("windows") >= 0) {
126+
String appData = envProvider.getEnv("APPDATA");
127+
if (Strings.isNullOrEmpty(appData)) {
128+
throw new CertificateSourceUnavailableException(
129+
"APPDATA environment variable is not set on Windows.");
130+
}
131+
File appDataPath = new File(appData);
132+
cloudConfigPath = new File(appDataPath, CLOUDSDK_CONFIG_DIRECTORY);
133+
} else {
134+
File configPath = new File(propProvider.getProperty("user.home", ""), ".config");
135+
cloudConfigPath = new File(configPath, CLOUDSDK_CONFIG_DIRECTORY);
136+
}
137+
}
138+
return new File(cloudConfigPath, WELL_KNOWN_CERTIFICATE_CONFIG_FILE);
139+
}
140+
}

0 commit comments

Comments
 (0)