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.util.Collections; 29 import java.util.LinkedList; 30 import java.util.List; 31 import java.util.Arrays; 32 import java.io.File; 33 import java.io.OutputStream; 34 import java.lang.module.ModuleDescriptor; 35 import java.lang.module.ModuleDescriptor.Builder; 36 import java.util.stream.Stream; 37 import jdk.test.lib.process.ProcessTools; 38 import jdk.test.lib.process.OutputAnalyzer; 39 import jdk.test.lib.util.JarUtils; 40 import jdk.test.lib.util.ModuleInfoWriter; 41 42 /* 43 * @test 44 * @bug 8078813 8183310 45 * @summary Test custom JAAS login module with all possible modular option. 46 * @modules java.base/jdk.internal.module 47 * @library /test/lib 48 * @build jdk.test.lib.util.JarUtils 49 * @build TestLoginModule JaasClient 50 * @run main JaasModularClientTest false 51 * @run main JaasModularClientTest true 52 */ 53 public class JaasModularClientTest { 54 55 private static final Path SRC = Paths.get(System.getProperty("test.src")); 56 private static final Path TEST_CLASSES 57 = Paths.get(System.getProperty("test.classes")); 58 private static final Path ARTIFACT_DIR = Paths.get("jars"); 59 private static final String PS = File.pathSeparator; 60 private static final String L_TYPE = "login.TestLoginModule"; 61 private static final String C_TYPE = "client.JaasClient"; 62 63 /** 64 * Here is the naming convention followed. 65 * l.jar - Unnamed login module jar. 66 * ml.jar - Modular login module jar. 67 * msl.jar - Modular login module jar provides login module service 68 * through module-info 69 * c.jar - Unnamed client jar. 70 * mc.jar - Modular client jar. 71 * mcs.jar - Modular client jar uses login module service through 72 * module-info. 73 * amc.jar - Modular client used for automatic login module jar. 74 * amcs.jar - Modular client used for automatic login module jar and uses 75 * login module service through module-info. 76 */ 77 private static final Path L_JAR = artifact("l.jar"); 78 private static final Path ML_JAR = artifact("ml.jar"); 79 private static final Path MSL_JAR = artifact("msl.jar"); 80 private static final Path C_JAR = artifact("c.jar"); 81 private static final Path MC_JAR = artifact("mc.jar"); 82 private static final Path MCS_JAR = artifact("mcs.jar"); 83 private static final Path AMC_JAR = artifact("amc.jar"); 84 private static final Path AMCS_JAR = artifact("amcs.jar"); 85 86 private final String unnL; 87 private final String modL; 88 private final String unnC; 89 private final String modC; 90 private final String autoMC; 91 // Common set of VM arguments used in all test cases 92 private final List<String> commonArgs; 93 94 public JaasModularClientTest(boolean service) { 95 96 System.out.printf("%n*** Login Module defined as service in " 97 + "module-info: %s ***%n%n", service); 98 List<String> argList = new LinkedList<>(); 99 argList.add("-Djava.security.auth.login.config=" 100 + toAbsPath(SRC.resolve("jaas.conf"))); 101 commonArgs = Collections.unmodifiableList(argList); 102 103 // Based on Testcase, select unnamed/modular jar files to use. 104 unnL = toAbsPath(L_JAR); 105 modL = toAbsPath(service ? MSL_JAR : ML_JAR); 106 unnC = toAbsPath(C_JAR); 107 modC = toAbsPath(service ? MCS_JAR : MC_JAR); 108 autoMC = toAbsPath(service ? AMCS_JAR : AMC_JAR); 109 } 110 111 /* 112 * Test cases are based on the following logic, 113 * for (definedAs : {"Service in module-info", "Class Type"}) { 114 * for (clientType : {"NAMED", "AUTOMATIC", "UNNAMED"}) { 115 * for (loginModuleType : {"NAMED", "AUTOMATIC", "UNNAMED"}) { 116 * Create and run java command for each possible case 117 * } 118 * } 119 * } 120 */ 121 public static void main(String[] args) throws Exception { 122 123 // Generates unnamed and modular jars. 124 setUp(); 125 boolean service = Boolean.valueOf(args[0]); 126 JaasModularClientTest test = new JaasModularClientTest(service); 127 test.process(); 128 } 129 130 private void process() throws Exception { 131 132 // Case: NAMED-NAMED, NAMED-AUTOMATIC, NAMED-UNNAMED 133 System.out.println("Case: Modular Client and Modular Login module."); 134 execute(String.format("--module-path %s%s%s -m mc/%s", 135 modC, PS, modL, C_TYPE)); 136 System.out.println("Case: Modular Client and automatic Login module."); 137 execute(String.format("--module-path %s%s%s --add-modules=l -m mc/%s", 138 autoMC, PS, unnL, C_TYPE)); 139 System.out.println("Case: Modular Client and unnamed Login module."); 140 execute(String.format("--module-path %s -cp %s -m mc/%s", autoMC, 141 unnL, C_TYPE)); 142 143 // Case: AUTOMATIC-NAMED, AUTOMATIC-AUTOMATIC, AUTOMATIC-UNNAMED 144 System.out.println("Case: Automatic Client and modular Login module."); 145 execute(String.format("--module-path %s%s%s --add-modules=ml -m c/%s", 146 unnC, PS, modL, C_TYPE)); 147 System.out.println("Case: Automatic Client and automatic Login module"); 148 execute(String.format("--module-path %s%s%s --add-modules=l -m c/%s", 149 unnC, PS, unnL, C_TYPE)); 150 System.out.println("Case: Automatic Client and unnamed Login module."); 151 execute(String.format("--module-path %s -cp %s -m c/%s", unnC, 152 unnL, C_TYPE)); 153 154 // Case: UNNAMED-NAMED, UNNAMED-AUTOMATIC, UNNAMED-UNNAMED 155 System.out.println("Case: Unnamed Client and modular Login module."); 156 execute(String.format("-cp %s --module-path %s --add-modules=ml %s", 157 unnC, modL, C_TYPE)); 158 System.out.println("Case: Unnamed Client and automatic Login module."); 159 execute(String.format("-cp %s --module-path %s --add-modules=l %s", 160 unnC, unnL, C_TYPE)); 161 System.out.println("Case: Unnamed Client and unnamed Login module."); 162 execute(String.format("-cp %s%s%s %s", unnC, PS, unnL, C_TYPE)); 163 164 // Case: unnamed jars in --module-path and modular jars in -cp. 165 System.out.println( 166 "Case: Unnamed Client and Login module from modulepath."); 167 execute(String.format("--module-path %s%s%s --add-modules=l -m c/%s", 168 unnC, PS, unnL, C_TYPE)); 169 System.out.println( 170 "Case: Modular Client and Login module in classpath."); 171 execute(String.format("-cp %s%s%s %s", modC, PS, modL, C_TYPE)); 172 } 173 174 /** 175 * Execute with command arguments and process the result. 176 */ 177 private void execute(String args) throws Exception { 178 179 String[] safeArgs = Stream.concat(commonArgs.stream(), 180 Stream.of(args.split("\\s+"))).filter(s -> { 181 if (s.contains(" ")) { 182 throw new RuntimeException("No spaces in args"); 183 } 184 return !s.isEmpty(); 185 }).toArray(String[]::new); 186 OutputAnalyzer out = ProcessTools.executeTestJava(safeArgs); 187 // Handle response. 188 if (out.getExitValue() != 0) { 189 System.out.printf("OUTPUT: %s", out.getOutput()); 190 throw new RuntimeException("FAIL: Unknown failure occured."); 191 } else { 192 System.out.println("Passed."); 193 } 194 } 195 196 /** 197 * Creates Unnamed/modular jar files for TestClient and TestClassLoader. 198 */ 199 private static void setUp() throws Exception { 200 201 if (ARTIFACT_DIR.toFile().exists()) { 202 System.out.println("Skipping setup: Artifacts already exists."); 203 return; 204 } 205 // Generate unnamed login module jar file. 206 JarUtils.createJarFile(L_JAR, TEST_CLASSES, 207 "login/TestLoginModule.class"); 208 // Generate unnamed client jar. 209 JarUtils.createJarFile(C_JAR, TEST_CLASSES, "client/JaasClient.class", 210 "client/JaasClient$MyCallbackHandler.class"); 211 212 Builder mBuilder = ModuleDescriptor.newModule("ml") 213 .requires("jdk.security.auth"); 214 // Modular jar exports package to let the login module type accessible. 215 generateJar(L_JAR, ML_JAR, mBuilder.exports("login").build()); 216 217 mBuilder = ModuleDescriptor.newModule("ml") 218 .requires("jdk.security.auth") 219 .provides("javax.security.auth.spi.LoginModule", 220 Arrays.asList(L_TYPE)); 221 // Modular login module as Service in module-info does not need to 222 // export service package. 223 generateJar(L_JAR, MSL_JAR, mBuilder.build()); 224 225 mBuilder = ModuleDescriptor.newModule("mc").exports("client") 226 .requires("jdk.security.auth"); 227 // Generate modular client jar to use automatic login module jar. 228 generateJar(C_JAR, AMC_JAR, mBuilder.build()); 229 // Generate modular client jar to use modular login module jar. 230 generateJar(C_JAR, MC_JAR, mBuilder.requires("ml").build()); 231 232 mBuilder = ModuleDescriptor.newModule("mc").exports("client") 233 .requires("jdk.security.auth") 234 .uses("javax.security.auth.spi.LoginModule"); 235 // Generate modular client jar to use automatic login module service. 236 generateJar(C_JAR, AMCS_JAR, mBuilder.build()); 237 // Generate modular client jar using modular login module service. 238 generateJar(C_JAR, MCS_JAR, mBuilder.requires("ml").build()); 239 } 240 241 /** 242 * Update Unnamed jars and include module descriptor files. 243 */ 244 private static void generateJar(Path sjar, Path djar, 245 ModuleDescriptor mDesc) throws Exception { 246 247 Files.copy(sjar, djar, StandardCopyOption.REPLACE_EXISTING); 248 Path dir = Files.createTempDirectory("tmp"); 249 if (mDesc != null) { 250 Path mi = dir.resolve("module-info.class"); 251 try (OutputStream out = Files.newOutputStream(mi)) { 252 ModuleInfoWriter.write(mDesc, out); 253 } 254 System.out.format("Added 'module-info.class' in '%s'%n", djar); 255 } 256 JarUtils.updateJarFile(djar, dir); 257 } 258 259 /** 260 * Look for file path in generated jars. 261 */ 262 private static Path artifact(String file) { 263 return ARTIFACT_DIR.resolve(file); 264 } 265 266 /** 267 * Convert to absolute file path. 268 */ 269 private static String toAbsPath(Path path) { 270 return path.toFile().getAbsolutePath(); 271 } 272 }