jdk-24/test/jdk/java/lang/reflect/sealed_classes/TestSecurityManagerChecks.java
Aleksey Shipilev d547a707bf 8286474: Drop --enable-preview from Sealed Classes related tests
Reviewed-by: alanb, jpai, mchung, lancea
2022-05-11 05:27:56 +00:00

276 lines
11 KiB
Java

/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8246778
* @summary Test that security checks occur for getPermittedSubclasses
* @library /test/lib
* @modules java.compiler
* @build jdk.test.lib.compiler.CompilerUtils jdk.test.lib.compiler.ModuleInfoMaker TestSecurityManagerChecks
* @run main/othervm -Djava.security.manager=allow TestSecurityManagerChecks named
* @run main/othervm -Djava.security.manager=allow TestSecurityManagerChecks unnamed
*/
import java.io.IOException;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import jdk.test.lib.compiler.*;
public class TestSecurityManagerChecks {
private static final ClassLoader OBJECT_CL = null;
public static void main(String[] args) throws Throwable {
if ("named".equals(args[0])) {
runNamedModuleTest();
} else {
runUnnamedModuleTest();
}
}
private static void runNamedModuleTest() throws Throwable {
Path classes = compileNamedModuleTest();
URL[] testClassPath = getTestClassPath();
//need to use a different ClassLoader to run the test, so that the checks are performed:
ClassLoader testCL = new URLClassLoader(testClassPath, OBJECT_CL);
testCL.loadClass("TestSecurityManagerChecks")
.getDeclaredMethod("doRunNamedModuleTest", Path.class)
.invoke(null, classes);
}
public static void doRunNamedModuleTest(Path classes) throws Throwable {
Configuration testConfig = ModuleLayer.boot()
.configuration()
.resolve(ModuleFinder.of(),
ModuleFinder.of(classes),
List.of("test"));
ModuleLayer testLayer = ModuleLayer.boot()
.defineModulesWithOneLoader(testConfig,
OBJECT_CL);
// First get hold of the target classes before we enable security
Class<?> sealed = Class.forName(testLayer.findModule("test").get(), "test.Base");
//try without a SecurityManager:
checkPermittedSubclasses(sealed, "test.a.ImplA1",
"test.a.ImplA2",
"test.b.ImplB");
String[] denyPackageAccess = new String[1];
int[] checkPackageAccessCallCount = new int[1];
//try with a SecurityManager:
SecurityManager sm = new SecurityManager() {
@Override
public void checkPackageAccess(String pkg) {
if (pkg.startsWith("test.")) {
checkPackageAccessCallCount[0]++;
}
if (Objects.equals(denyPackageAccess[0], pkg)) {
throw new SecurityException();
}
}
};
System.setSecurityManager(sm);
denyPackageAccess[0] = "test";
//passes - does not return a class from package "test":
checkPermittedSubclasses(sealed, "test.a.ImplA1",
"test.a.ImplA2",
"test.b.ImplB");
if (checkPackageAccessCallCount[0] != 2) {
throw new AssertionError("Unexpected call count: " +
checkPackageAccessCallCount[0]);
}
denyPackageAccess[0] = "test.a";
try {
sealed.getPermittedSubclasses();
throw new Error("getPermittedSubclasses incorrectly succeeded for " +
sealed.getName());
} catch (SecurityException e) {
System.out.println("OK - getPermittedSubclasses for " + sealed.getName() +
" got expected exception: " + e);
}
}
private static Path compileNamedModuleTest() throws IOException {
Path base = Paths.get(".", "named");
Path src = base.resolve("src");
Path classes = base.resolve("classes");
ModuleInfoMaker maker = new ModuleInfoMaker(src);
maker.writeJavaFiles("test",
"module test {}",
"package test; public sealed interface Base permits test.a.ImplA1, test.a.ImplA2, test.b.ImplB, test.c.ImplC {}",
"package test.a; public final class ImplA1 implements test.Base {}",
"package test.a; public final class ImplA2 implements test.Base {}",
"package test.b; public final class ImplB implements test.Base {}",
"package test.c; public final class ImplC implements test.Base {}"
);
if (!CompilerUtils.compile(src, classes.resolve("test"), "--enable-preview", "-source", System.getProperty("java.specification.version"))) {
throw new AssertionError("Compilation didn't succeed!");
}
Files.delete(classes.resolve("test").resolve("test").resolve("c").resolve("ImplC.class"));
return classes;
}
private static void runUnnamedModuleTest() throws Throwable {
Path classes = compileUnnamedModuleTest();
URL[] testClassPath = getTestClassPath();
//need to use a different ClassLoader to run the test, so that the checks are performed:
ClassLoader testCL = new URLClassLoader(testClassPath, OBJECT_CL);
testCL.loadClass("TestSecurityManagerChecks")
.getDeclaredMethod("doRunUnnamedModuleTest", Path.class)
.invoke(null, classes);
}
public static void doRunUnnamedModuleTest(Path classes) throws Throwable {
ClassLoader unnamedModuleCL =
new URLClassLoader(new URL[] {classes.toUri().toURL()}, OBJECT_CL);
// First get hold of the target classes before we enable security
Class<?> sealed = unnamedModuleCL.loadClass("test.Base");
//try without a SecurityManager:
checkPermittedSubclasses(sealed, "test.ImplA1",
"test.ImplA2",
"test.ImplB");
String[] denyPackageAccess = new String[1];
int[] checkPackageAccessCallCount = new int[1];
//try with a SecurityManager:
SecurityManager sm = new SecurityManager() {
@Override
public void checkPackageAccess(String pkg) {
if (pkg.equals("test")) {
checkPackageAccessCallCount[0]++;
}
if (Objects.equals(denyPackageAccess[0], pkg)) {
throw new SecurityException();
}
}
};
System.setSecurityManager(sm);
denyPackageAccess[0] = "test.unknown";
//passes - does not return a class from package "test.unknown":
checkPermittedSubclasses(sealed, "test.ImplA1",
"test.ImplA2",
"test.ImplB");
if (checkPackageAccessCallCount[0] != 1) {
throw new AssertionError("Unexpected call count: " +
checkPackageAccessCallCount[0]);
}
denyPackageAccess[0] = "test";
try {
sealed.getPermittedSubclasses();
throw new Error("getPermittedSubclasses incorrectly succeeded for " +
sealed.getName());
} catch (SecurityException e) {
System.out.println("OK - getPermittedSubclasses for " + sealed.getName() +
" got expected exception: " + e);
}
}
private static Path compileUnnamedModuleTest() throws IOException {
Path base = Paths.get(".", "unnamed");
Path src = base.resolve("src");
Path classes = base.resolve("classes");
ModuleInfoMaker maker = new ModuleInfoMaker(src);
maker.writeJavaFiles("test",
"module test {}",
"package test; public sealed interface Base permits ImplA1, ImplA2, ImplB, ImplC {}",
"package test; public final class ImplA1 implements test.Base {}",
"package test; public final class ImplA2 implements test.Base {}",
"package test; public final class ImplB implements test.Base {}",
"package test; public final class ImplC implements test.Base {}"
);
Files.delete(src.resolve("test").resolve("module-info.java"));
if (!CompilerUtils.compile(src.resolve("test"), classes, "--enable-preview", "-source", System.getProperty("java.specification.version"))) {
throw new AssertionError("Compilation didn't succeed!");
}
Files.delete(classes.resolve("test").resolve("ImplC.class"));
return classes;
}
private static void checkPermittedSubclasses(Class<?> c, String... expected) {
Class<?>[] subclasses = c.getPermittedSubclasses();
List<String> subclassesNames = Arrays.stream(subclasses)
.map(Class::getName)
.collect(Collectors.toList());
if (!subclassesNames.equals(Arrays.asList(expected))) {
throw new AssertionError("Incorrect permitted subclasses: " +
subclassesNames);
}
}
private static URL[] getTestClassPath() {
return Arrays.stream(System.getProperty("test.class.path")
.split(System.getProperty("path.separator")))
.map(TestSecurityManagerChecks::path2URL)
.toArray(s -> new URL[s]);
}
private static URL path2URL(String p) {
try {
return Path.of(p).toUri().toURL();
} catch (MalformedURLException ex) {
throw new AssertionError(ex);
}
}
}