1 /*
2 * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.nio.file.StandardCopyOption;
28 import java.security.Security;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Arrays;
35 import java.util.stream.Stream;
36 import java.io.File;
37 import java.io.IOException;
38 import java.io.OutputStream;
39 import java.lang.module.ModuleDescriptor;
40 import java.lang.module.ModuleDescriptor.Builder;
41 import jdk.test.lib.process.ProcessTools;
42 import jdk.test.lib.util.JarUtils;
43 import jdk.test.lib.util.ModuleInfoWriter;
44
45 /*
46 * @test
47 * @bug 8130360 8183310
48 * @summary Test security provider in different combination of modular option
49 * defined with(out) service description.
50 * @modules java.base/jdk.internal.module
51 * @library /test/lib
52 * @build jdk.test.lib.util.JarUtils
53 * TestProvider TestClient
54 * @run main SecurityProviderModularTest CL true
55 * @run main SecurityProviderModularTest CL false
56 * @run main SecurityProviderModularTest SL true
57 * @run main SecurityProviderModularTest SL false
58 * @run main SecurityProviderModularTest SPN true
59 * @run main SecurityProviderModularTest SPN false
60 * @run main SecurityProviderModularTest SPT true
61 * @run main SecurityProviderModularTest SPT false
62 */
63 public class SecurityProviderModularTest {
64
65 private static final Path TEST_CLASSES
66 = Paths.get(System.getProperty("test.classes"));
67 private static final Path ARTIFACT_DIR = Paths.get("jars");
68 private static final Path SEC_FILE = Paths.get("java.extn.security");
69 private static final String PS = File.pathSeparator;
70 private static final String P_TYPE = "p.TestProvider";
71 private static final String C_TYPE = "c.TestClient";
72
73 /**
74 * Here is the naming convention followed.
75 * Test runtime arguments,
76 * CL - Provider class loaded through ClassLoader
77 * SL - Provider class to be discovered by ServiceLoader
78 * SPN - Provider name defined through "java.extn.security" file which
79 * referred through system property "java.security.properties".
80 * SPT - Provider type defined through "java.extn.security" file which
81 * referred through system property "java.security.properties".
82 *
83 * For each jar file name,
84 * p.jar - Unnamed provider jar.
85 * pd.jar - Unnamed provider jar with META-INF provider descriptor.
86 * mp.jar - Modular provider jar.
87 * mpd.jar - Modular provider jar with META-INF provider descriptor.
88 * msp.jar - Modular provider jar provides service through module-info.java
89 * mspd.jar - Modular provider jar with META-INF provider descriptor and
90 * provides service through module-info.java.
91 * c.jar - Unnamed client jar.
92 * mc.jar - Modular client jar.
93 * mcs.jar - Modular client jar uses service through module-info.java.
94 * amc.jar - Modular client used for automatic provider jar.
95 * amcs.jar - Modular client used for automatic provider jar uses service
96 * through module-info.java.
97 */
98 private static final Path P_JAR = artifact("p.jar");
99 private static final Path PD_JAR = artifact("pd.jar");
100 private static final Path MP_JAR = artifact("mp.jar");
101 private static final Path MPD_JAR = artifact("mpd.jar");
102 private static final Path MSP_JAR = artifact("msp.jar");
103 private static final Path MSPD_JAR = artifact("mspd.jar");
104 private static final Path C_JAR = artifact("c.jar");
105 private static final Path MC_JAR = artifact("mc.jar");
106 private static final Path MCS_JAR = artifact("mcs.jar");
107 private static final Path AMC_JAR = artifact("amc.jar");
108 private static final Path AMCS_JAR = artifact("amcs.jar");
109 private static final Map<String, String> MSG_MAP = new HashMap<>();
110
111 static {
112 /*
113 * This mapping help process finding expected message based
114 * on the key passed as argument while executing java command.
115 */
116 MSG_MAP.put("NoAccess", "cannot access class p.TestProvider");
117 MSG_MAP.put("Success", "Client: found provider TestProvider");
118 MSG_MAP.put("NoProvider", "Provider TestProvider not found");
119 }
120
121 private final String addUNArg;
122 private final String addNMArg;
123 private final String cArg;
124 private final String unnP;
125 private final String modP;
126 private final String unnC;
127 private final String modC;
128 private final String autoMC;
129 private final String expModRes;
130 private final String expAModRes;
131 // Common set of VM arguments used in all test cases
132 private final List<String> commonArgs;
133
134 public SecurityProviderModularTest(String use, boolean metaDesc) {
135
136 List<String> argList = new LinkedList<>();
137 argList.add("-Duser.language=en");
138 argList.add("-Duser.region=US");
139 final boolean useSL = "SL".equals(use) || "SPN".equals(use);
140 final boolean useCL = "CL".equals(use);
141 final boolean useSPT = "SPT".equals(use);
142 final boolean useSP = use.startsWith("SP");
143 /* Use Security property file when the provider expected to
144 * loaded through Security property file. */
145 if (useSP) {
146 /* Create a java.security file to specify the new provider.
147 * java.security file extension can be provided using
148 * "-Djava.security.properties" VM argument at runtime.*/
149 createJavaSecurityFileExtn("SPN".equals(use));
150 argList.add("-Djava.security.properties=" + toAbsPath(SEC_FILE));
151 }
152 commonArgs = Collections.unmodifiableList(argList);
153 cArg = (useCL) ? P_TYPE : "TestProvider";
154 addUNArg = (useSL) ? "" : ("--add-modules="
155 + ((metaDesc) ? "pd" : "p"));
156 addNMArg = (useSL) ? "" : "--add-modules=mp";
157
158 // Based on Testcase, select unnamed/modular jar files to use.
159 unnP = toAbsPath((metaDesc) ? PD_JAR : P_JAR);
160 modP = toAbsPath(useSL ? (metaDesc ? MSPD_JAR : MSP_JAR)
161 : (metaDesc ? MPD_JAR : MP_JAR));
162 unnC = toAbsPath(C_JAR);
163 modC = toAbsPath(useSL ? MCS_JAR : MC_JAR);
164 autoMC = toAbsPath(useSL ? AMCS_JAR : AMC_JAR);
165
166 expModRes = "Success";
167 expAModRes = (useSPT | useCL) ? "Success"
168 : (metaDesc) ? "Success" : "NoProvider";
169 String loadByMsg = useSP ? "SecurityPropertyFile"
170 : ((useCL) ? "ClassLoader" : "ServiceLoader");
171 System.out.printf("%n*** Providers loaded through %s and includes"
172 + " META Descriptor: %s ***%n%n", loadByMsg, metaDesc);
173 }
174
175 /*
176 * Test cases are based on the following logic,
177 * for (ProviderLoadedThrough : {"ServiceLoader", "ClassLoader",
178 * "SecurityPropertyFile"}) {
179 * for (definedWith : {"METAINFService", "WithoutMETAINFService"}) {
180 * for (clientType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
181 * for (providerType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
182 * Create and run java command for each possible case
183 * }
184 * }
185 * }
186 * }
187 */
188 public static void main(String[] args) throws Exception {
189
190 // Generates unnamed and modular jars.
191 setUp();
192 boolean metaDesc = Boolean.valueOf(args[1]);
193 SecurityProviderModularTest test
194 = new SecurityProviderModularTest(args[0], metaDesc);
195 test.process(args[0]);
196 }
197
198 private void process(String use) throws Exception {
199
200 // Case: NAMED-NAMED, NAMED-AUTOMATIC, NAMED-UNNAMED
201 System.out.printf("Case: Modular Client and Modular Provider");
202 execute(String.format("--module-path %s%s%s -m mc/%s %s %s",
203 modC, PS, modP, C_TYPE, use, cArg), expModRes);
204 System.out.printf("Case: Modular Client and automatic Provider");
205 execute(String.format("--module-path %s%s%s %s -m mc/%s %s %s", autoMC,
206 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
207 System.out.printf("Case: Modular Client and unnamed Provider");
208 execute(String.format("--module-path %s -cp %s -m mc/%s %s %s", autoMC,
209 unnP, C_TYPE, use, cArg), expAModRes);
210
211 // Case: AUTOMATIC-NAMED, AUTOMATIC-AUTOMATIC, AUTOMATIC-UNNAMED
212 System.out.printf("Case: Automatic Client and modular Provider");
213 execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
214 PS, modP, addNMArg, C_TYPE, use, cArg), expModRes);
215 System.out.printf("Case: Automatic Client and automatic Provider");
216 execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
217 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
218 System.out.printf("Case: Automatic Client and unnamed Provider");
219 execute(String.format("--module-path %s -cp %s -m c/%s %s %s", unnC,
220 unnP, C_TYPE, use, cArg), expAModRes);
221
222 // Case: UNNAMED-NAMED, UNNAMED-AUTOMATIC, UNNAMED-UNNAMED
223 System.out.printf("Case: Unnamed Client and modular Provider");
224 execute(String.format("-cp %s --module-path %s %s %s %s %s", unnC,
225 modP, addNMArg, C_TYPE, use, cArg), expModRes);
226 System.out.printf("Case: Unnamed Client and automatic Provider");
227 execute(String.format("-cp %s --module-path %s %s %s %s %s", unnC,
228 unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
229 System.out.printf("Case: Unnamed Client and unnamed Provider");
230 execute(String.format("-cp %s%s%s %s %s %s", unnC, PS, unnP, C_TYPE,
231 use, cArg), expAModRes);
232
233 // Case: unnamed jars in --module-path and modular jars in -cp.
234 System.out.printf(
235 "Case: Unnamed Client and Unnamed Provider in modulepath");
236 execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
237 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
238 System.out.printf(
239 "Case: Modular Client and Modular Provider in classpath");
240 execute(String.format("-cp %s%s%s %s %s %s", modC, PS, modP, C_TYPE,
241 use, cArg), expAModRes);
242 }
243
244 /**
245 * Execute with command arguments and process the result.
246 */
247 private void execute(String args, String msgKey) throws Exception {
248
249 String[] safeArgs = Stream.concat(commonArgs.stream(),
250 Stream.of(args.split("\\s+"))).filter(s -> {
251 if (s.contains(" ")) {
252 throw new RuntimeException("No spaces in args");
253 }
254 return !s.isEmpty();
255 }).toArray(String[]::new);
256 String out = ProcessTools.executeTestJava(safeArgs).getOutput();
257 // Handle response.
258 if ((msgKey != null && out.contains(MSG_MAP.get(msgKey)))) {
259 System.out.printf("PASS: Expected Result: %s.%n",
260 MSG_MAP.get(msgKey));
261 } else if (out.contains("Exception") || out.contains("Error")) {
262 System.out.printf("OUTPUT: %s", out);
263 throw new RuntimeException("FAIL: Unknown Exception occured. "
264 + "Expected: " + MSG_MAP.get(msgKey));
265 } else {
266 System.out.printf("OUTPUT: %s", out);
267 throw new RuntimeException("FAIL: Unknown Test case found");
268 }
269 }
270
271 /**
272 * Creates Unnamed/modular jar files for TestClient and TestClassLoader.
273 */
274 private static void setUp() throws Exception {
275
276 if (ARTIFACT_DIR.toFile().exists()) {
277 System.out.println("Skipping setup: Artifacts already exists.");
278 return;
279 }
280 // Generate unnamed provider jar file.
281 JarUtils.createJarFile(P_JAR, TEST_CLASSES, "p/TestProvider.class");
282 // Generate unnamed client jar file.
283 JarUtils.createJarFile(C_JAR, TEST_CLASSES, "c/TestClient.class");
284 // Generate unnamed provider jar files with META-INF descriptor.
285 generateJar(P_JAR, PD_JAR, null, true);
286
287 Builder mBuilder = ModuleDescriptor.newModule("mp").exports("p");
288 // Modular provider defined as META-INF service.
289 generateJar(P_JAR, MPD_JAR, mBuilder.build(), true);
290 // Modular jar exports package to let the provider type accessible.
291 generateJar(P_JAR, MP_JAR, mBuilder.build(), false);
292
293 mBuilder = ModuleDescriptor.newModule("mp")
294 .provides("java.security.Provider", Arrays.asList(P_TYPE));
295 // Modular provider Service in module-info does not need to export
296 // its package.
297 generateJar(P_JAR, MSP_JAR, mBuilder.build(), false);
298 // Modular provider Service in module-info also have META-INF descriptor
299 generateJar(P_JAR, MSPD_JAR, mBuilder.build(), true);
300
301 mBuilder = ModuleDescriptor.newModule("mc").exports("c");
302 // Generate modular client jar file to use automatic provider jar.
303 generateJar(C_JAR, AMC_JAR, mBuilder.build(), false);
304 // Generate modular client jar file to use modular provider jar.
305 generateJar(C_JAR, MC_JAR, mBuilder.requires("mp").build(), false);
306
307 mBuilder = ModuleDescriptor.newModule("mc").exports("c")
308 .uses("java.security.Provider");
309 // Generate modular client jar file to use automatic provider service.
310 generateJar(C_JAR, AMCS_JAR, mBuilder.build(), false);
311 // Generate modular client jar file using modular provider service.
312 generateJar(C_JAR, MCS_JAR, mBuilder.requires("mp").build(), false);
313 }
314
315 /**
316 * Update Unnamed jars and include descriptor files.
317 */
318 private static void generateJar(Path sjar, Path djar,
319 ModuleDescriptor mDesc, boolean metaDesc) throws Exception {
320
321 Files.copy(sjar, djar, StandardCopyOption.REPLACE_EXISTING);
322 Path dir = Files.createTempDirectory("tmp");
323 if (metaDesc) {
324 write(dir.resolve(Paths.get("META-INF", "services",
325 "java.security.Provider")), P_TYPE);
326 }
327 if (mDesc != null) {
328 Path mi = dir.resolve("module-info.class");
329 try (OutputStream out = Files.newOutputStream(mi)) {
330 ModuleInfoWriter.write(mDesc, out);
331 }
332 System.out.format("Added 'module-info.class' in '%s'%n", djar);
333 }
334 JarUtils.updateJarFile(djar, dir);
335 }
336
337 /**
338 * Look for file path in generated jars.
339 */
340 private static Path artifact(String file) {
341 return ARTIFACT_DIR.resolve(file);
342 }
343
344 /**
345 * Convert to absolute file path.
346 */
347 private static String toAbsPath(Path path) {
348 return path.toFile().getAbsolutePath();
349 }
350
351 /**
352 * Create the parent directories if missing to ensure the path exist.
353 */
354 private static Path ensurePath(Path at) throws IOException {
355 Path parent = at.getParent();
356 if (parent != null && !parent.toFile().exists()) {
357 ensurePath(parent);
358 }
359 return Files.createDirectories(parent);
360 }
361
362 /**
363 * Generates service descriptor inside META-INF folder.
364 */
365 private static void write(Path at, String content) throws IOException {
366 ensurePath(at);
367 Files.write(at, content.getBytes("UTF-8"));
368 }
369
370 /**
371 * Create new provider entry through java.security file extension.
372 * New provider entry will be the last entry inside the JRE.
373 */
374 private static void createJavaSecurityFileExtn(boolean useName) {
375 int insertAt = Security.getProviders().length + 1;
376 String provider = (useName ? "TestProvider" : P_TYPE);
377 try {
378 Files.write(SEC_FILE, String.format("security.provider.%s=%s",
379 insertAt, provider).getBytes("UTF-8"));
380 } catch (IOException e) {
381 throw new RuntimeException(e);
382 }
383 System.out.printf("Security property file created at: %s with value:"
384 + " %s%n", SEC_FILE, provider);
385 }
386 }
--- EOF ---