Skip to content

Commit 356d7dc

Browse files
committed
Added separate test class for mTLS-utils. Added missing cert and malformed cert tests to X509Provider.
1 parent 6153618 commit 356d7dc

8 files changed

Lines changed: 328 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: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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 Path tempDir;
48+
49+
@Test
50+
void getCertificatePath_succeeds() throws IOException {
51+
Path configFile = tempDir.resolve("config.json");
52+
Files.write(
53+
configFile,
54+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
55+
.getBytes());
56+
57+
EnvironmentProvider envProvider =
58+
new EnvironmentProvider() {
59+
@Override
60+
public String getEnv(String name) {
61+
return null;
62+
}
63+
};
64+
PropertyProvider propProvider =
65+
new PropertyProvider() {
66+
@Override
67+
public String getProperty(String name, String def) {
68+
return def;
69+
}
70+
};
71+
72+
String certPath =
73+
MtlsUtils.getCertificatePath(envProvider, propProvider, configFile.toString());
74+
75+
assertEquals("cert.pem", certPath);
76+
}
77+
78+
@Test
79+
void getCertificatePath_missingCertPath_throws() throws IOException {
80+
Path configFile = tempDir.resolve("config.json");
81+
Files.write(
82+
configFile, "{\"cert_configs\":{\"workload\":{\"key_path\":\"key.pem\"}}}".getBytes());
83+
84+
EnvironmentProvider envProvider =
85+
new EnvironmentProvider() {
86+
@Override
87+
public String getEnv(String name) {
88+
return null;
89+
}
90+
};
91+
PropertyProvider propProvider =
92+
new PropertyProvider() {
93+
@Override
94+
public String getProperty(String name, String def) {
95+
return def;
96+
}
97+
};
98+
99+
assertThrows(
100+
IllegalArgumentException.class,
101+
() -> MtlsUtils.getCertificatePath(envProvider, propProvider, configFile.toString()));
102+
}
103+
104+
@Test
105+
void getWorkloadCertificateConfiguration_overridePath() throws IOException {
106+
Path configFile = tempDir.resolve("custom_config.json");
107+
Files.write(
108+
configFile,
109+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
110+
.getBytes());
111+
112+
EnvironmentProvider envProvider =
113+
new EnvironmentProvider() {
114+
@Override
115+
public String getEnv(String name) {
116+
return null;
117+
}
118+
};
119+
PropertyProvider propProvider =
120+
new PropertyProvider() {
121+
@Override
122+
public String getProperty(String name, String def) {
123+
return def;
124+
}
125+
};
126+
127+
WorkloadCertificateConfiguration config =
128+
MtlsUtils.getWorkloadCertificateConfiguration(
129+
envProvider, propProvider, configFile.toString());
130+
131+
assertNotNull(config);
132+
assertEquals("cert.pem", config.getCertPath());
133+
assertEquals("key.pem", config.getPrivateKeyPath());
134+
}
135+
136+
@Test
137+
void getWorkloadCertificateConfiguration_envVar() throws IOException {
138+
Path configFile = tempDir.resolve("env_config.json");
139+
Files.write(
140+
configFile,
141+
"{\"cert_configs\":{\"workload\":{\"cert_path\":\"cert.pem\",\"key_path\":\"key.pem\"}}}"
142+
.getBytes());
143+
144+
EnvironmentProvider envProvider =
145+
new EnvironmentProvider() {
146+
@Override
147+
public String getEnv(String name) {
148+
return "GOOGLE_API_CERTIFICATE_CONFIG".equals(name) ? configFile.toString() : null;
149+
}
150+
};
151+
PropertyProvider propProvider =
152+
new PropertyProvider() {
153+
@Override
154+
public String getProperty(String name, String def) {
155+
return def;
156+
}
157+
};
158+
159+
WorkloadCertificateConfiguration config =
160+
MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null);
161+
162+
assertNotNull(config);
163+
assertEquals("cert.pem", config.getCertPath());
164+
}
165+
166+
@Test
167+
void getWellKnownCertificateConfigFile_windows() throws IOException {
168+
EnvironmentProvider envProvider =
169+
new EnvironmentProvider() {
170+
@Override
171+
public String getEnv(String name) {
172+
return "APPDATA".equals(name) ? tempDir.toString() : null;
173+
}
174+
};
175+
PropertyProvider propProvider =
176+
new PropertyProvider() {
177+
@Override
178+
public String getProperty(String name, String def) {
179+
return "os.name".equals(name) ? "Windows 10" : def;
180+
}
181+
};
182+
183+
CertificateSourceUnavailableException exception =
184+
assertThrows(
185+
CertificateSourceUnavailableException.class,
186+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
187+
188+
String expectedPath =
189+
new File(tempDir.toFile(), "gcloud/certificate_config.json").getAbsolutePath();
190+
assertTrue(exception.getMessage().contains(expectedPath));
191+
}
192+
193+
@Test
194+
void getWellKnownCertificateConfigFile_linux() throws IOException {
195+
EnvironmentProvider envProvider =
196+
new EnvironmentProvider() {
197+
@Override
198+
public String getEnv(String name) {
199+
return null;
200+
}
201+
};
202+
PropertyProvider propProvider =
203+
new PropertyProvider() {
204+
@Override
205+
public String getProperty(String name, String def) {
206+
if ("os.name".equals(name)) return "Linux";
207+
if ("user.home".equals(name)) return tempDir.toString();
208+
return def;
209+
}
210+
};
211+
212+
CertificateSourceUnavailableException exception =
213+
assertThrows(
214+
CertificateSourceUnavailableException.class,
215+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
216+
217+
String expectedPath =
218+
new File(tempDir.toFile(), ".config/gcloud/certificate_config.json").getAbsolutePath();
219+
assertTrue(exception.getMessage().contains(expectedPath));
220+
}
221+
222+
@Test
223+
void getWellKnownCertificateConfigFile_windows_missingAppData_throws() {
224+
EnvironmentProvider envProvider =
225+
new EnvironmentProvider() {
226+
@Override
227+
public String getEnv(String name) {
228+
return null;
229+
}
230+
};
231+
PropertyProvider propProvider =
232+
new PropertyProvider() {
233+
@Override
234+
public String getProperty(String name, String def) {
235+
return "os.name".equals(name) ? "Windows 10" : def;
236+
}
237+
};
238+
239+
CertificateSourceUnavailableException exception =
240+
assertThrows(
241+
CertificateSourceUnavailableException.class,
242+
() -> MtlsUtils.getWorkloadCertificateConfiguration(envProvider, propProvider, null));
243+
244+
assertEquals("APPDATA environment variable is not set on Windows.", exception.getMessage());
245+
}
246+
}

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,43 @@ 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
}

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)