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