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