From b229f19716d574cdb0d9dd5687229ceafa7ba009 Mon Sep 17 00:00:00 2001 From: "James R. Perkins" Date: Fri, 17 Jan 2025 10:59:58 -0800 Subject: [PATCH] [431] Add an ApplicationArchiveProcessor to add the appropriate permissions needed for the TCK tests to run with the security manager enabled. Updated the RestClientDelegateBean and HeaderUtils to work when the security manager is enabled. Signed-off-by: James R. Perkins --- .../client/RestClientDelegateBean.java | 4 +- .../client/RestClientListeners.java | 13 +- .../microprofile/client/SecurityActions.java | 62 ++++++++++ .../client/header/HeaderUtils.java | 10 +- testsuite/integration-tests/pom.xml | 14 --- .../microprofile-rest-client-tck/pom.xml | 11 ++ ...tClientTckApplicationArchiveProcessor.java | 111 ++++++++++++++++++ .../client/tck/RestClientTckExtension.java | 33 ++++++ ...boss.arquillian.core.spi.LoadableExtension | 1 + testsuite/pom.xml | 11 ++ 10 files changed, 241 insertions(+), 29 deletions(-) create mode 100644 rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/SecurityActions.java create mode 100644 testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckApplicationArchiveProcessor.java create mode 100644 testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckExtension.java create mode 100644 testsuite/microprofile-rest-client-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension diff --git a/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientDelegateBean.java b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientDelegateBean.java index bd85c7e5..ead3abaa 100644 --- a/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientDelegateBean.java +++ b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientDelegateBean.java @@ -179,7 +179,7 @@ private void configureSsl(RestClientBuilder builder) { private void registerHostnameVerifier(String verifier, RestClientBuilder builder) { try { - Class verifierClass = Class.forName(verifier, true, Thread.currentThread().getContextClassLoader()); + Class verifierClass = Class.forName(verifier, true, SecurityActions.getContextClassLoader()); builder.hostnameVerifier((HostnameVerifier) verifierClass.newInstance()); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not find hostname verifier class" + verifier, e); @@ -275,7 +275,7 @@ private void registerProviders(RestClientBuilder builder, String providersAsStri private Class providerClassForName(String name) { try { - return Class.forName(name, true, Thread.currentThread().getContextClassLoader()); + return Class.forName(name, true, SecurityActions.getContextClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not find provider class: " + name); } diff --git a/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientListeners.java b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientListeners.java index 36e94545..7ebedefe 100644 --- a/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientListeners.java +++ b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/RestClientListeners.java @@ -43,7 +43,7 @@ private RestClientListeners() { .synchronizedMap(new WeakHashMap<>()); public static Collection get() { - ClassLoader loader = getClassLoader(); + ClassLoader loader = SecurityActions.getClassLoader(RestClientListeners.class); if (loader == null) { return Collections.emptyList(); } @@ -59,15 +59,4 @@ public static Collection get() { return AccessController.doPrivileged(action); }); } - - private static ClassLoader getClassLoader() { - if (System.getSecurityManager() == null) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return cl == null ? RestClientListeners.class.getClassLoader() : cl; - } - return AccessController.doPrivileged((PrivilegedAction) () -> { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return cl == null ? RestClientListeners.class.getClassLoader() : cl; - }); - } } diff --git a/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/SecurityActions.java b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/SecurityActions.java new file mode 100644 index 00000000..352fe801 --- /dev/null +++ b/rest-client-base/src/main/java/org/jboss/resteasy/microprofile/client/SecurityActions.java @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jboss.resteasy.microprofile.client; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class must never be made public. + * + * @author James R. Perkins + */ +class SecurityActions { + + /** + * Returns the current threads context class loader. + * + * @return the current threads context class loader or {@code null} if there is not one + */ + static ClassLoader getContextClassLoader() { + if (System.getSecurityManager() == null) { + return Thread.currentThread().getContextClassLoader(); + } + return AccessController.doPrivileged((PrivilegedAction) () -> Thread.currentThread() + .getContextClassLoader()); + } + + /** + * Gets the current context class loader or the class loader of the passed in class. + * + * @param clazz the class to get the class loader from if the context class loader is {@code null} + * + * @return the available class loader + */ + static ClassLoader getClassLoader(final Class clazz) { + if (System.getSecurityManager() == null) { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return cl == null ? clazz.getClassLoader() : cl; + } + return AccessController.doPrivileged((PrivilegedAction) () -> { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return cl == null ? clazz.getClassLoader() : cl; + }); + } +} diff --git a/rest-client/src/main/java/org/jboss/resteasy/microprofile/client/header/HeaderUtils.java b/rest-client/src/main/java/org/jboss/resteasy/microprofile/client/header/HeaderUtils.java index d02c48ef..18160d12 100644 --- a/rest-client/src/main/java/org/jboss/resteasy/microprofile/client/header/HeaderUtils.java +++ b/rest-client/src/main/java/org/jboss/resteasy/microprofile/client/header/HeaderUtils.java @@ -23,6 +23,8 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.List; import java.util.stream.Collectors; @@ -79,7 +81,13 @@ public static Method resolveMethod(String methodSpecifier, methodName = methodSpecifier.substring(lastDot + 1); String className = methodSpecifier.substring(0, lastDot); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final ClassLoader loader; + if (System.getSecurityManager() == null) { + loader = Thread.currentThread().getContextClassLoader(); + } else { + loader = AccessController + .doPrivileged((PrivilegedAction) () -> Thread.currentThread().getContextClassLoader()); + } try { clazz = Class.forName(className, true, loader); } catch (ClassNotFoundException e) { diff --git a/testsuite/integration-tests/pom.xml b/testsuite/integration-tests/pom.xml index 9ed16557..63879c72 100644 --- a/testsuite/integration-tests/pom.xml +++ b/testsuite/integration-tests/pom.xml @@ -237,18 +237,4 @@ - - - security.manager - - - security.manager - - - - -secmgr - - - - \ No newline at end of file diff --git a/testsuite/microprofile-rest-client-tck/pom.xml b/testsuite/microprofile-rest-client-tck/pom.xml index 5563ccb9..5bafbf4f 100644 --- a/testsuite/microprofile-rest-client-tck/pom.xml +++ b/testsuite/microprofile-rest-client-tck/pom.xml @@ -39,6 +39,17 @@ + + + org.jboss.arquillian.container + arquillian-container-test-spi + + + org.wildfly.arquillian + wildfly-testing-tools + + + org.jboss.resteasy.microprofile diff --git a/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckApplicationArchiveProcessor.java b/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckApplicationArchiveProcessor.java new file mode 100644 index 00000000..18544f54 --- /dev/null +++ b/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckApplicationArchiveProcessor.java @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.resteasy.microprofile.rest.client.tck; + +import java.io.FilePermission; +import java.lang.reflect.ReflectPermission; +import java.net.SocketPermission; +import java.util.ArrayList; +import java.util.List; +import java.util.PropertyPermission; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.container.ManifestContainer; +import org.wildfly.testing.tools.deployments.DeploymentDescriptors; + +/** + * @author James R. Perkins + */ +public class RestClientTckApplicationArchiveProcessor implements ApplicationArchiveProcessor { + private static final List ALL_FILE_TESTS = List.of( + // Creates a /tmp/ssl* keystore file + "org.eclipse.microprofile.rest.client.tck.ssl.SslContextTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslHostnameVerifierTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslMutualTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslTrustStoreTest"); + + private static final List GET_CLASS_LOADER_TESTS = List.of( + // The org.jboss.arquillian.testenricher.cdi.CDIInjectionEnricher requires getClassLoader() + "org.eclipse.microprofile.rest.client.tck.cditests.CDIProxyServerTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslHostnameVerifierTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslMutualTest", + "org.eclipse.microprofile.rest.client.tck.ssl.SslTrustStoreTest", + // Required for the Jetty thread pool + "org.eclipse.microprofile.rest.client.tck.ProxyServerTest"); + + private static final List JETTY_SERVER_TESTS = List.of( + "org.eclipse.microprofile.rest.client.tck.ProxyServerTest", + "org.eclipse.microprofile.rest.client.tck.cditests.CDIProxyServerTest"); + + @Override + public void process(final Archive applicationArchive, final TestClass testClass) { + if (applicationArchive instanceof ManifestContainer) { + final var container = (ManifestContainer) applicationArchive; + + final var permissions = new ArrayList<>(List.of( + // Required by the Arquillian ServiceLoader to allow access to the constructor + new ReflectPermission("suppressAccessChecks"), + new PropertyPermission("arquillian.*", "read"), + // Required by Jetty + new PropertyPermission("jetty.*", "read,write"), + // Required by the MP REST Client TCK + new PropertyPermission("org.eclipse.microprofile.rest.client.*", "read"), + new RuntimePermission("getenv.JETTY_AVAILABLE_PROCESSORS"), + // Required of OTel is available on the deployment class path + new RuntimePermission("getenv.OTEL_JAVAAGENT_DEBUG"), + new RuntimePermission("getenv.OTEL_INSTRUMENTATION_EXPERIMENTAL_SPAN_SUPPRESSION_STRATEGY"), + // Required by TestNG + new PropertyPermission("testng.*", "read"), + new PropertyPermission("user.dir", "read"), + new RuntimePermission("accessDeclaredMembers"), + // Required by Wiremock + new PropertyPermission("wiremock.*", "read"), + new SocketPermission("localhost", "resolve"), + new SocketPermission("localhost:*", "connect,listen,resolve"), + new SocketPermission("127.0.0.1:*", "connect,resolve"))); + + if (ALL_FILE_TESTS.contains(testClass.getName())) { + permissions.add(new FilePermission("<>", "read")); + } + if (GET_CLASS_LOADER_TESTS.contains(testClass.getName())) { + permissions.add(new RuntimePermission("getClassLoader")); + } + if (JETTY_SERVER_TESTS.contains(testClass.getName())) { + permissions.add(new RuntimePermission("modifyThread")); + } + + var currentPermissionsXml = applicationArchive.delete("META-INF/permissions.xml"); + // A WAR might be in a different location + if (currentPermissionsXml == null) { + currentPermissionsXml = applicationArchive.delete("WEB-INF/classes/META-INF/permissions.yaml"); + } + if (currentPermissionsXml != null) { + container.addAsManifestResource( + DeploymentDescriptors.appendPermissions(currentPermissionsXml.getAsset(), permissions), + "permissions.xml"); + } else { + container.addAsManifestResource(DeploymentDescriptors.createPermissionsXmlAsset(permissions), + "permissions.xml"); + } + } + } +} diff --git a/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckExtension.java b/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckExtension.java new file mode 100644 index 00000000..c7b7e6ef --- /dev/null +++ b/testsuite/microprofile-rest-client-tck/src/main/java/dev/resteasy/microprofile/rest/client/tck/RestClientTckExtension.java @@ -0,0 +1,33 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2025 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.resteasy.microprofile.rest.client.tck; + +import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor; +import org.jboss.arquillian.core.spi.LoadableExtension; + +/** + * @author James R. Perkins + */ +public class RestClientTckExtension implements LoadableExtension { + @Override + public void register(final ExtensionBuilder builder) { + builder.service(ApplicationArchiveProcessor.class, RestClientTckApplicationArchiveProcessor.class); + } +} diff --git a/testsuite/microprofile-rest-client-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/testsuite/microprofile-rest-client-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension new file mode 100644 index 00000000..8f91a628 --- /dev/null +++ b/testsuite/microprofile-rest-client-tck/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension @@ -0,0 +1 @@ +dev.resteasy.microprofile.rest.client.tck.RestClientTckExtension \ No newline at end of file diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 88c89cd6..f96632a2 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -233,6 +233,17 @@ wildfly-preview + + security.manager + + + security.manager + + + + -secmgr + + \ No newline at end of file