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