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 }