1 /*
   2  * Copyright (c) 2015, 2017, 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 jdk.internal.module.ModuleInfoWriter;
  37 import java.util.stream.Stream;
  38 import jdk.test.lib.process.ProcessTools;
  39 import jdk.test.lib.process.OutputAnalyzer;
  40 
  41 
  42 /*
  43  * @test
  44  * @bug 8078813 8183310
  45  * @summary Test custom JAAS login module with all possible modular option.
  46  * @library /test/lib
  47  * @modules java.base/jdk.internal.module
  48  * @build 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.executeTestJvm(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 }