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.security.Security;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.LinkedList;
  32 import java.util.List;
  33 import java.util.Map;
  34 import java.util.Arrays;
  35 import java.util.stream.Stream;
  36 import java.io.File;
  37 import java.io.IOException;
  38 import java.io.OutputStream;
  39 import java.lang.module.ModuleDescriptor;
  40 import java.lang.module.ModuleDescriptor.Builder;
  41 import jdk.internal.module.ModuleInfoWriter;
  42 import jdk.test.lib.process.ProcessTools;
  43 
  44 
  45 /*
  46  * @test
  47  * @bug 8130360 8183310
  48  * @summary Test security provider in different combination of modular option
  49  *          defined with(out) service description.
  50  * @library /lib/testlibrary /test/lib
  51  * @modules java.base/jdk.internal.module
  52  * @build JarUtils TestProvider TestClient
  53  * @run main SecurityProviderModularTest CL true
  54  * @run main SecurityProviderModularTest CL false
  55  * @run main SecurityProviderModularTest SL true
  56  * @run main SecurityProviderModularTest SL false
  57  * @run main SecurityProviderModularTest SPN true
  58  * @run main SecurityProviderModularTest SPN false
  59  * @run main SecurityProviderModularTest SPT true
  60  * @run main SecurityProviderModularTest SPT false
  61  */
  62 public class SecurityProviderModularTest {
  63 
  64     private static final Path TEST_CLASSES
  65             = Paths.get(System.getProperty("test.classes"));
  66     private static final Path ARTIFACT_DIR = Paths.get("jars");
  67     private static final Path SEC_FILE = Paths.get("java.extn.security");
  68     private static final String PS = File.pathSeparator;
  69     private static final String P_TYPE = "p.TestProvider";
  70     private static final String C_TYPE = "c.TestClient";
  71 
  72     /**
  73      * Here is the naming convention followed.
  74      * Test runtime arguments,
  75      * CL       - Provider class loaded through ClassLoader
  76      * SL       - Provider class to be discovered by ServiceLoader
  77      * SPN      - Provider name defined through "java.extn.security" file which
  78      *            referred through system property "java.security.properties".
  79      * SPT      - Provider type defined through "java.extn.security" file which
  80      *            referred through system property "java.security.properties".
  81      *
  82      * For each jar file name,
  83      * p.jar    - Unnamed provider jar.
  84      * pd.jar   - Unnamed provider jar with META-INF provider descriptor.
  85      * mp.jar   - Modular provider jar.
  86      * mpd.jar  - Modular provider jar with META-INF provider descriptor.
  87      * msp.jar  - Modular provider jar provides service through module-info.java
  88      * mspd.jar - Modular provider jar with META-INF provider descriptor and
  89      *            provides service through module-info.java.
  90      * c.jar    - Unnamed client jar.
  91      * mc.jar   - Modular client jar.
  92      * mcs.jar  - Modular client jar uses service through module-info.java.
  93      * amc.jar  - Modular client used for automatic provider jar.
  94      * amcs.jar - Modular client used for automatic provider jar uses service
  95      *            through module-info.java.
  96      */
  97     private static final Path P_JAR = artifact("p.jar");
  98     private static final Path PD_JAR = artifact("pd.jar");
  99     private static final Path MP_JAR = artifact("mp.jar");
 100     private static final Path MPD_JAR = artifact("mpd.jar");
 101     private static final Path MSP_JAR = artifact("msp.jar");
 102     private static final Path MSPD_JAR = artifact("mspd.jar");
 103     private static final Path C_JAR = artifact("c.jar");
 104     private static final Path MC_JAR = artifact("mc.jar");
 105     private static final Path MCS_JAR = artifact("mcs.jar");
 106     private static final Path AMC_JAR = artifact("amc.jar");
 107     private static final Path AMCS_JAR = artifact("amcs.jar");
 108     private static final Map<String, String> MSG_MAP = new HashMap<>();
 109 
 110     static {
 111         /*
 112          * This mapping help process finding expected message based
 113          * on the key passed as argument while executing java command.
 114          */
 115         MSG_MAP.put("NoAccess", "cannot access class p.TestProvider");
 116         MSG_MAP.put("Success", "Client: found provider TestProvider");
 117         MSG_MAP.put("NoProvider", "Provider TestProvider not found");
 118     }
 119 
 120     private final String addUNArg;
 121     private final String addNMArg;
 122     private final String cArg;
 123     private final String unnP;
 124     private final String modP;
 125     private final String unnC;
 126     private final String modC;
 127     private final String autoMC;
 128     private final String expModRes;
 129     private final String expAModRes;
 130     // Common set of VM arguments used in all test cases
 131     private final List<String> commonArgs;
 132 
 133     public SecurityProviderModularTest(String use, boolean metaDesc) {
 134 
 135         List<String> argList = new LinkedList<>();
 136         argList.add("-Duser.language=en");
 137         argList.add("-Duser.region=US");
 138         final boolean useSL = "SL".equals(use) || "SPN".equals(use);
 139         final boolean useCL = "CL".equals(use);
 140         final boolean useSPT = "SPT".equals(use);
 141         final boolean useSP = use.startsWith("SP");
 142         /* Use Security property file when the provider expected to
 143          * loaded through Security property file. */
 144         if (useSP) {
 145             /* Create a java.security file to specify the new provider.
 146              * java.security file extension can be provided using
 147              * "-Djava.security.properties" VM argument at runtime.*/
 148             createJavaSecurityFileExtn("SPN".equals(use));
 149             argList.add("-Djava.security.properties=" + toAbsPath(SEC_FILE));
 150         }
 151         commonArgs = Collections.unmodifiableList(argList);
 152         cArg = (useCL) ? P_TYPE : "TestProvider";
 153         addUNArg = (useSL) ? "" : ("--add-modules="
 154                 + ((metaDesc) ? "pd" : "p"));
 155         addNMArg = (useSL) ? "" : "--add-modules=mp";
 156 
 157         // Based on Testcase, select unnamed/modular jar files to use.
 158         unnP = toAbsPath((metaDesc) ? PD_JAR : P_JAR);
 159         modP = toAbsPath(useSL ? (metaDesc ? MSPD_JAR : MSP_JAR)
 160                 : (metaDesc ? MPD_JAR : MP_JAR));
 161         unnC = toAbsPath(C_JAR);
 162         modC = toAbsPath(useSL ? MCS_JAR : MC_JAR);
 163         autoMC = toAbsPath(useSL ? AMCS_JAR : AMC_JAR);
 164 
 165         expModRes = "Success";
 166         expAModRes = (useSPT | useCL) ? "Success"
 167                 : (metaDesc) ? "Success" : "NoProvider";
 168         String loadByMsg = useSP ? "SecurityPropertyFile"
 169                 : ((useCL) ? "ClassLoader" : "ServiceLoader");
 170         System.out.printf("%n*** Providers loaded through %s and includes"
 171                 + " META Descriptor: %s ***%n%n", loadByMsg, metaDesc);
 172     }
 173 
 174     /*
 175      * Test cases are based on the following logic,
 176      * for (ProviderLoadedThrough : {"ServiceLoader", "ClassLoader",
 177      *             "SecurityPropertyFile"}) {
 178      *     for (definedWith : {"METAINFService", "WithoutMETAINFService"}) {
 179      *         for (clientType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
 180      *             for (providerType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
 181      *                 Create and run java command for each possible case
 182      *             }
 183      *         }
 184      *     }
 185      * }
 186      */
 187     public static void main(String[] args) throws Exception {
 188 
 189         // Generates unnamed and modular jars.
 190         setUp();
 191         boolean metaDesc = Boolean.valueOf(args[1]);
 192         SecurityProviderModularTest test
 193                 = new SecurityProviderModularTest(args[0], metaDesc);
 194         test.process(args[0]);
 195     }
 196 
 197     private void process(String use) throws Exception {
 198 
 199         // Case: NAMED-NAMED, NAMED-AUTOMATIC, NAMED-UNNAMED
 200         System.out.printf("Case: Modular Client and Modular Provider");
 201         execute(String.format("--module-path %s%s%s -m mc/%s %s %s",
 202                 modC, PS, modP, C_TYPE, use, cArg), expModRes);
 203         System.out.printf("Case: Modular Client and automatic Provider");
 204         execute(String.format("--module-path %s%s%s %s -m mc/%s %s %s", autoMC,
 205                 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
 206         System.out.printf("Case: Modular Client and unnamed Provider");
 207         execute(String.format("--module-path %s -cp %s -m mc/%s %s %s", autoMC,
 208                 unnP, C_TYPE, use, cArg), expAModRes);
 209 
 210         // Case: AUTOMATIC-NAMED, AUTOMATIC-AUTOMATIC, AUTOMATIC-UNNAMED
 211         System.out.printf("Case: Automatic Client and modular Provider");
 212         execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
 213                 PS, modP, addNMArg, C_TYPE, use, cArg), expModRes);
 214         System.out.printf("Case: Automatic Client and automatic Provider");
 215         execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
 216                 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
 217         System.out.printf("Case: Automatic Client and unnamed Provider");
 218         execute(String.format("--module-path %s -cp %s -m c/%s %s %s", unnC,
 219                 unnP, C_TYPE, use, cArg), expAModRes);
 220 
 221         // Case: UNNAMED-NAMED, UNNAMED-AUTOMATIC, UNNAMED-UNNAMED
 222         System.out.printf("Case: Unnamed Client and modular Provider");
 223         execute(String.format("-cp %s --module-path %s %s %s %s %s", unnC,
 224                 modP, addNMArg, C_TYPE, use, cArg), expModRes);
 225         System.out.printf("Case: Unnamed Client and automatic Provider");
 226         execute(String.format("-cp %s --module-path %s %s %s %s %s", unnC,
 227                 unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
 228         System.out.printf("Case: Unnamed Client and unnamed Provider");
 229         execute(String.format("-cp %s%s%s %s %s %s", unnC, PS, unnP, C_TYPE,
 230                 use, cArg), expAModRes);
 231 
 232         // Case: unnamed jars in --module-path and modular jars in -cp.
 233         System.out.printf(
 234                 "Case: Unnamed Client and Unnamed Provider in modulepath");
 235         execute(String.format("--module-path %s%s%s %s -m c/%s %s %s", unnC,
 236                 PS, unnP, addUNArg, C_TYPE, use, cArg), expAModRes);
 237         System.out.printf(
 238                 "Case: Modular Client and Modular Provider in classpath");
 239         execute(String.format("-cp %s%s%s %s %s %s", modC, PS, modP, C_TYPE,
 240                 use, cArg), expAModRes);
 241     }
 242 
 243     /**
 244      * Execute with command arguments and process the result.
 245      */
 246     private void execute(String args, String msgKey) throws Exception {
 247 
 248         String[] safeArgs = Stream.concat(commonArgs.stream(),
 249                 Stream.of(args.split("\\s+"))).filter(s -> {
 250             if (s.contains(" ")) {
 251                 throw new RuntimeException("No spaces in args");
 252             }
 253             return !s.isEmpty();
 254         }).toArray(String[]::new);
 255         String out = ProcessTools.executeTestJvm(safeArgs).getOutput();
 256         // Handle response.
 257         if ((msgKey != null && out.contains(MSG_MAP.get(msgKey)))) {
 258             System.out.printf("PASS: Expected Result: %s.%n",
 259                     MSG_MAP.get(msgKey));
 260         } else if (out.contains("Exception") || out.contains("Error")) {
 261             System.out.printf("OUTPUT: %s", out);
 262             throw new RuntimeException("FAIL: Unknown Exception occured. "
 263                     + "Expected: " + MSG_MAP.get(msgKey));
 264         } else {
 265             System.out.printf("OUTPUT: %s", out);
 266             throw new RuntimeException("FAIL: Unknown Test case found");
 267         }
 268     }
 269 
 270     /**
 271      * Creates Unnamed/modular jar files for TestClient and TestClassLoader.
 272      */
 273     private static void setUp() throws Exception {
 274 
 275         if (ARTIFACT_DIR.toFile().exists()) {
 276             System.out.println("Skipping setup: Artifacts already exists.");
 277             return;
 278         }
 279         // Generate unnamed provider jar file.
 280         JarUtils.createJarFile(P_JAR, TEST_CLASSES, "p/TestProvider.class");
 281         // Generate unnamed client jar file.
 282         JarUtils.createJarFile(C_JAR, TEST_CLASSES, "c/TestClient.class");
 283         // Generate unnamed provider jar files with META-INF descriptor.
 284         generateJar(P_JAR, PD_JAR, null, true);
 285 
 286         Builder mBuilder = ModuleDescriptor.newModule("mp").exports("p");
 287         // Modular provider defined as META-INF service.
 288         generateJar(P_JAR, MPD_JAR, mBuilder.build(), true);
 289         // Modular jar exports package to let the provider type accessible.
 290         generateJar(P_JAR, MP_JAR, mBuilder.build(), false);
 291 
 292         mBuilder = ModuleDescriptor.newModule("mp")
 293                 .provides("java.security.Provider", Arrays.asList(P_TYPE));
 294         // Modular provider Service in module-info does not need to export
 295         // its package.
 296         generateJar(P_JAR, MSP_JAR, mBuilder.build(), false);
 297         // Modular provider Service in module-info also have META-INF descriptor
 298         generateJar(P_JAR, MSPD_JAR, mBuilder.build(), true);
 299 
 300         mBuilder = ModuleDescriptor.newModule("mc").exports("c");
 301         // Generate modular client jar file to use automatic provider jar.
 302         generateJar(C_JAR, AMC_JAR, mBuilder.build(), false);
 303         // Generate modular client jar file to use modular provider jar.
 304         generateJar(C_JAR, MC_JAR, mBuilder.requires("mp").build(), false);
 305 
 306         mBuilder = ModuleDescriptor.newModule("mc").exports("c")
 307                 .uses("java.security.Provider");
 308         // Generate modular client jar file to use automatic provider service.
 309         generateJar(C_JAR, AMCS_JAR, mBuilder.build(), false);
 310         // Generate modular client jar file using modular provider service.
 311         generateJar(C_JAR, MCS_JAR, mBuilder.requires("mp").build(), false);
 312     }
 313 
 314     /**
 315      * Update Unnamed jars and include descriptor files.
 316      */
 317     private static void generateJar(Path sjar, Path djar,
 318             ModuleDescriptor mDesc, boolean metaDesc) throws Exception {
 319 
 320         Files.copy(sjar, djar, StandardCopyOption.REPLACE_EXISTING);
 321         Path dir = Files.createTempDirectory("tmp");
 322         if (metaDesc) {
 323             write(dir.resolve(Paths.get("META-INF", "services",
 324                     "java.security.Provider")), P_TYPE);
 325         }
 326         if (mDesc != null) {
 327             Path mi = dir.resolve("module-info.class");
 328             try (OutputStream out = Files.newOutputStream(mi)) {
 329                 ModuleInfoWriter.write(mDesc, out);
 330             }
 331             System.out.format("Added 'module-info.class' in '%s'%n", djar);
 332         }
 333         JarUtils.updateJarFile(djar, dir);
 334     }
 335 
 336     /**
 337      * Look for file path in generated jars.
 338      */
 339     private static Path artifact(String file) {
 340         return ARTIFACT_DIR.resolve(file);
 341     }
 342 
 343     /**
 344      * Convert to absolute file path.
 345      */
 346     private static String toAbsPath(Path path) {
 347         return path.toFile().getAbsolutePath();
 348     }
 349 
 350     /**
 351      * Create the parent directories if missing to ensure the path exist.
 352      */
 353     private static Path ensurePath(Path at) throws IOException {
 354         Path parent = at.getParent();
 355         if (parent != null && !parent.toFile().exists()) {
 356             ensurePath(parent);
 357         }
 358         return Files.createDirectories(parent);
 359     }
 360 
 361     /**
 362      * Generates service descriptor inside META-INF folder.
 363      */
 364     private static void write(Path at, String content) throws IOException {
 365         ensurePath(at);
 366         Files.write(at, content.getBytes("UTF-8"));
 367     }
 368 
 369     /**
 370      * Create new provider entry through java.security file extension.
 371      * New provider entry will be the last entry inside the JRE.
 372      */
 373     private static void createJavaSecurityFileExtn(boolean useName) {
 374         int insertAt = Security.getProviders().length + 1;
 375         String provider = (useName ? "TestProvider" : P_TYPE);
 376         try {
 377             Files.write(SEC_FILE, String.format("security.provider.%s=%s",
 378                     insertAt, provider).getBytes("UTF-8"));
 379         } catch (IOException e) {
 380             throw new RuntimeException(e);
 381         }
 382         System.out.printf("Security property file created at: %s with value:"
 383                 + " %s%n", SEC_FILE, provider);
 384     }
 385 }