Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit b2ff241

Browse files
committed
Added separate test class for mTLS-utils. Added missing cert and malformed cert tests to X509Provider.
1 parent 39707b0 commit b2ff241

8 files changed

Lines changed: 313 additions & 8 deletions

File tree

oauth2_http/java/com/google/auth/mtls/MtlsUtils.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,20 @@ static WorkloadCertificateConfiguration getWorkloadCertificateConfiguration(
115115
}
116116

117117
private static File getWellKnownCertificateConfigFile(
118-
EnvironmentProvider envProvider, PropertyProvider propProvider) {
118+
EnvironmentProvider envProvider, PropertyProvider propProvider) throws IOException {
119119
File cloudConfigPath;
120120
String envPath = envProvider.getEnv("CLOUDSDK_CONFIG");
121121
if (envPath != null) {
122122
cloudConfigPath = new File(envPath);
123123
} else {
124124
String osName = propProvider.getProperty("os.name", "").toLowerCase(Locale.US);
125125
if (osName.indexOf("windows") >= 0) {
126-
File appDataPath = new File(envProvider.getEnv("APPDATA"));
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);
127132
cloudConfigPath = new File(appDataPath, CLOUDSDK_CONFIG_DIRECTORY);
128133
} else {
129134
File configPath = new File(propProvider.getProperty("user.home", ""), ".config");

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
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+
131
package com.google.auth.oauth2;
232

333
import com.google.api.core.InternalApi;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,6 @@ private IdentityPoolSubjectTokenSupplier createCertificateSubjectTokenSupplier(
176176

177177
private X509Provider getX509Provider(
178178
Builder builder, IdentityPoolCredentialSource credentialSource) {
179-
final IdentityPoolCredentialSource.CertificateConfig certConfig =
180-
credentialSource.getCertificateConfig();
181-
182179
// Use the provided X509Provider if available, otherwise initialize a default one.
183180
X509Provider x509Provider = builder.x509Provider;
184181
if (x509Provider == null) {
@@ -193,6 +190,9 @@ private X509Provider getX509Provider(
193190
private static String getExplicitCertConfigPath(IdentityPoolCredentialSource credentialSource) {
194191
IdentityPoolCredentialSource.CertificateConfig certConfig =
195192
credentialSource.getCertificateConfig();
193+
if (certConfig == null) {
194+
return null;
195+
}
196196
return certConfig.useDefaultCertificateConfig()
197197
? null
198198
: certConfig.getCertificateConfigLocation();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Google LLC
2+
* Copyright 2026 Google LLC
33
*
44
* Redistribution and use in source and binary forms, with or without
55
* modification, are permitted provided that the following conditions are

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Google LLC
2+
* Copyright 2026 Google LLC
33
*
44
* Redistribution and use in source and binary forms, with or without
55
* modification, are permitted provided that the following conditions are
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Copyright 2026, Google Inc. All rights reserved.
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+
*
15+
* * Neither the name of Google Inc. nor the names of its
16+
* contributors may be used to endorse or promote products derived from
17+
* this software without specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
*/
31+
32+
package com.google.auth.mtls;
33+
34+
import static org.junit.jupiter.api.Assertions.*;
35+
36+
import com.google.auth.oauth2.EnvironmentProvider;
37+
import com.google.auth.oauth2.PropertyProvider;
38+
import java.io.File;
39+
import java.io.IOException;
40+
import java.nio.file.Files;
41+
import java.nio.file.Path;
42+
import org.junit.jupiter.api.Test;
43+
import org.junit.jupiter.api.io.TempDir;
44+
45+
class MtlsUtilsTest {
46+
47+
@TempDir
48+
Path tempDir;
49+
50+
@Test
51+
void getCertificatePath_succeeds() throws IOException {
52+
Path configFile = tempDir.resolve("config.json");
53+
Files.write(
54+
configFile,
55+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
56+
.getBytes());
57+
58+
EnvironmentProvider envProvider = new EnvironmentProvider() {
59+
@Override
60+
public String getEnv(String name) {
61+
return null;
62+
}
63+
};
64+
PropertyProvider propProvider = new PropertyProvider() {
65+
@Override
66+
public String getProperty(String name, String def) {
67+
return def;
68+
}
69+
};
70+
71+
String certPath = MtlsUtils.getCertificatePath(envProvider, propProvider, configFile.toString());
72+
73+
assertEquals("cert.pem", certPath);
74+
}
75+
76+
@Test
77+
void getCertificatePath_missingCertPath_throws() throws IOException {
78+
Path configFile = tempDir.resolve("config.json");
79+
Files.write(
80+
configFile,
81+
"{\"cert_configs\":{\"workload\":{\"key_path\":\"key.pem\"}}}"
82+
.getBytes());
83+
84+
EnvironmentProvider envProvider = new EnvironmentProvider() {
85+
@Override
86+
public String getEnv(String name) {
87+
return null;
88+
}
89+
};
90+
PropertyProvider propProvider = new PropertyProvider() {
91+
@Override
92+
public String getProperty(String name, String def) {
93+
return def;
94+
}
95+
};
96+
97+
assertThrows(
98+
IllegalArgumentException.class,
99+
() -> MtlsUtils.getCertificatePath(envProvider, propProvider, configFile.toString()));
100+
}
101+
102+
@Test
103+
void getWorkloadCertificateConfiguration_overridePath() throws IOException {
104+
Path configFile = tempDir.resolve("custom_config.json");
105+
Files.write(
106+
configFile,
107+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
108+
.getBytes());
109+
110+
EnvironmentProvider envProvider = new EnvironmentProvider() {
111+
@Override
112+
public String getEnv(String name) {
113+
return null;
114+
}
115+
};
116+
PropertyProvider propProvider = new PropertyProvider() {
117+
@Override
118+
public String getProperty(String name, String def) {
119+
return def;
120+
}
121+
};
122+
123+
WorkloadCertificateConfiguration config = MtlsUtils.getWorkloadCertificateConfiguration(
124+
envProvider, propProvider, configFile.toString());
125+
126+
assertNotNull(config);
127+
assertEquals("cert.pem", config.getCertPath());
128+
assertEquals("key.pem", config.getPrivateKeyPath());
129+
}
130+
131+
@Test
132+
void getWorkloadCertificateConfiguration_envVar() throws IOException {
133+
Path configFile = tempDir.resolve("env_config.json");
134+
Files.write(
135+
configFile,
136+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
137+
.getBytes());
138+
139+
EnvironmentProvider envProvider = new EnvironmentProvider() {
140+
@Override
141+
public String getEnv(String name) {
142+
return "GOOGLE_API_CERTIFICATE_CONFIG".equals(name) ? configFile.toString() : null;
143+
}
144+
};
145+
PropertyProvider propProvider = new PropertyProvider() {
146+
@Override
147+
public String getProperty(String name, String def) {
148+
return def;
149+
}
150+
};
151+
152+
WorkloadCertificateConfiguration config = MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider,
153+
null);
154+
155+
assertNotNull(config);
156+
assertEquals("cert.pem", config.getCertPath());
157+
}
158+
159+
@Test
160+
void getWellKnownCertificateConfigFile_windows() throws IOException {
161+
EnvironmentProvider envProvider = new EnvironmentProvider() {
162+
@Override
163+
public String getEnv(String name) {
164+
return "APPDATA".equals(name) ? tempDir.toString() : null;
165+
}
166+
};
167+
PropertyProvider propProvider = new PropertyProvider() {
168+
@Override
169+
public String getProperty(String name, String def) {
170+
return "os.name".equals(name) ? "Windows 10" : def;
171+
}
172+
};
173+
174+
CertificateSourceUnavailableException exception = assertThrows(
175+
CertificateSourceUnavailableException.class,
176+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
177+
178+
String expectedPath = new File(tempDir.toFile(), "gcloud/certificate_config.json").getAbsolutePath();
179+
assertTrue(exception.getMessage().contains(expectedPath));
180+
}
181+
182+
@Test
183+
void getWellKnownCertificateConfigFile_linux() throws IOException {
184+
EnvironmentProvider envProvider = new EnvironmentProvider() {
185+
@Override
186+
public String getEnv(String name) {
187+
return null;
188+
}
189+
};
190+
PropertyProvider propProvider = new PropertyProvider() {
191+
@Override
192+
public String getProperty(String name, String def) {
193+
if ("os.name".equals(name))
194+
return "Linux";
195+
if ("user.home".equals(name))
196+
return tempDir.toString();
197+
return def;
198+
}
199+
};
200+
201+
CertificateSourceUnavailableException exception = assertThrows(
202+
CertificateSourceUnavailableException.class,
203+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
204+
205+
String expectedPath = new File(tempDir.toFile(), ".config/gcloud/certificate_config.json").getAbsolutePath();
206+
assertTrue(exception.getMessage().contains(expectedPath));
207+
}
208+
209+
@Test
210+
void getWellKnownCertificateConfigFile_windows_missingAppData_throws() {
211+
EnvironmentProvider envProvider = new EnvironmentProvider() {
212+
@Override
213+
public String getEnv(String name) {
214+
return null;
215+
}
216+
};
217+
PropertyProvider propProvider = new PropertyProvider() {
218+
@Override
219+
public String getProperty(String name, String def) {
220+
return "os.name".equals(name) ? "Windows 10" : def;
221+
}
222+
};
223+
224+
CertificateSourceUnavailableException exception = assertThrows(
225+
CertificateSourceUnavailableException.class,
226+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
227+
228+
assertEquals("APPDATA environment variable is not set on Windows.", exception.getMessage());
229+
}
230+
}

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,44 @@ void x509Provider_succeeds_withWindowsPath()
169169
assertEquals(1, store.size());
170170
assertNotNull(store.getCertificateAlias(expectedCert));
171171
}
172+
173+
@Test
174+
void x509Provider_certFileDoesntExist_throws() throws IOException {
175+
Path tempConfig = Files.createTempFile("config", ".json");
176+
tempConfig.toFile().deleteOnExit();
177+
Path nonExistentCert = tempConfig.getParent().resolve("non_existent_cert.pem");
178+
179+
Files.write(
180+
tempConfig,
181+
("{\"cert_configs\":{\"workload\":{\"cert_path\":\""
182+
+ nonExistentCert.toString()
183+
+ "\",\"key_path\":\"key.pem\"}}}")
184+
.getBytes());
185+
186+
X509Provider testProvider = new X509Provider(tempConfig.toString());
187+
188+
assertThrows(IOException.class, testProvider::getKeyStore);
189+
}
190+
191+
@Test
192+
void x509Provider_malformedCert_throws() throws IOException {
193+
Path tempConfig = Files.createTempFile("config", ".json");
194+
tempConfig.toFile().deleteOnExit();
195+
Path malformedCert = Files.createTempFile("badcert", ".pem");
196+
malformedCert.toFile().deleteOnExit();
197+
198+
Files.write(malformedCert, "This is not a valid certificate".getBytes());
199+
200+
Files.write(
201+
tempConfig,
202+
("{\"cert_configs\":{\"workload\":{\"cert_path\":\""
203+
+ malformedCert.toString()
204+
+ "\",\"key_path\":\"key.pem\"}}}")
205+
.getBytes());
206+
207+
X509Provider testProvider = new X509Provider(tempConfig.toString());
208+
209+
assertThrows(Exception.class, testProvider::getKeyStore);
210+
}
172211
}
212+

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 Google LLC
2+
* Copyright 2026 Google LLC
33
*
44
* Redistribution and use in source and binary forms, with or without
55
* modification, are permitted provided that the following conditions are

0 commit comments

Comments
 (0)