This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch jdk24 in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 87eb7235acfcdcab2e6e1fb94b60b9b45823297b Author: Richard Zowalla <r...@apache.org> AuthorDate: Tue Jun 24 10:37:06 2025 +0200 JDK 24 Support? --- .../core/security/AbstractSecurityService.java | 42 +++++++++-- .../apache/openejb/core/security/PolicyJDK24.java | 46 +++++++++++ .../core/security/jacc/BasicJaccProvider.java | 3 +- .../openejb/client/JaasIdentityResolver.java | 5 +- .../apache/openejb/client/util/JDK24Subject.java | 88 ++++++++++++++++++++++ .../java/org/apache/openejb/client/MainTest.java | 5 +- 6 files changed, 178 insertions(+), 11 deletions(-) diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java index cb56a5f9b7..18bff3fc9a 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/security/AbstractSecurityService.java @@ -50,6 +50,7 @@ import java.security.Policy; import java.security.Principal; import java.security.PrivilegedAction; import java.security.ProtectionDomain; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashSet; @@ -62,6 +63,8 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import static java.util.Arrays.asList; +import static org.apache.openejb.InterfaceType.BUSINESS_LOCALBEAN_HOME; +import static org.apache.openejb.InterfaceType.LOCALBEAN; /** * This security service chooses a UUID as its token as this can be serialized @@ -379,6 +382,7 @@ public abstract class AbstractSecurityService implements DestroyableResource, Se if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) { name = null; } + final Identity currentIdentity = clientIdentity.get(); final SecurityContext securityContext; if (currentIdentity == null) { @@ -386,11 +390,18 @@ public abstract class AbstractSecurityService implements DestroyableResource, Se } else { securityContext = new SecurityContext(currentIdentity.getSubject()); } - securityContext.getAccessControlContext().checkPermission(new EJBMethodPermission(ejbName, name, method)); + return getPolicy().implies(newProtectionDomain(securityContext.subject.getPrincipals()), new EJBMethodPermission(ejbName, name, method)); } catch (final AccessControlException e) { return false; } - return true; + } + + private ProtectionDomain newProtectionDomain(Set<Principal> principalSet) { + return new ProtectionDomain( + new CodeSource(null, (Certificate[]) null), + null, + null, + principalSet == null ? null : principalSet.toArray(Principal[]::new)); } protected static String autoJaccProvider() { @@ -422,11 +433,11 @@ public abstract class AbstractSecurityService implements DestroyableResource, Se // check the system provided provider first - if for some reason it isn't loaded, load it final String systemPolicyProvider = SystemInstance.get().getOptions().getProperties().getProperty("jakarta.security.jacc.policy.provider"); - if (systemPolicyProvider != null && Policy.getPolicy() == null) { + if (systemPolicyProvider != null && getPolicy() == null) { installPolicy(systemPolicyProvider); } - if (! JaccProvider.Policy.class.getName().equals(Policy.getPolicy().getClass().getName())) { + if (! JaccProvider.Policy.class.getName().equals(getPolicy().getClass().getName())) { // this should delegate to the policy installed above installPolicy(JaccProvider.Policy.class.getName()); } @@ -436,15 +447,34 @@ public abstract class AbstractSecurityService implements DestroyableResource, Se try { final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); final Class policyClass = Class.forName(policyProvider, true, classLoader); - final Policy policy = (Policy) policyClass.newInstance(); + final Policy policy = (Policy) policyClass.getDeclaredConstructor().newInstance(); policy.refresh(); - Policy.setPolicy(policy); + setPolicy(policy); } catch (final Exception e) { throw new IllegalStateException("Could not install JACC Policy Provider: " + policyProvider, e); } } + public static Policy getPolicy() { + Policy policy = PolicyJDK24.getPolicy(); + if (policy == null) { + policy = Policy.getPolicy(); + } + + return policy; + } + + public static void setPolicy(Policy policy) { + try { + Policy.setPolicy(policy); + } catch (UnsupportedOperationException e) { + //we are running JDK 24 or later, so no system-wide policy possible. + PolicyJDK24.setPolicy(policy); + } + } + + protected Subject createSubject(final String name, final String groupName) { if (name == null) { return null; diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java b/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java new file mode 100644 index 0000000000..6db9ccf4cb --- /dev/null +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/security/PolicyJDK24.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.core.security; + +import java.security.Policy; + +/** + * A utility class to manage the Java Security Policy in a thread-safe manner. + * This class provides methods to get and set the security policy, ensuring + * that changes are synchronized across threads. + */ +public class PolicyJDK24 { + + private static volatile Policy policy; + + /** + * @return the policy + */ + public static synchronized Policy getPolicy() { + return policy; + } + + /** + * @param policy the policy to set + */ + public static synchronized void setPolicy(Policy policy) { + PolicyJDK24.policy = policy; + } + + + +} diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java index 3db5c4af63..6a2f77811b 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/core/security/jacc/BasicJaccProvider.java @@ -18,6 +18,7 @@ package org.apache.openejb.core.security.jacc; import org.apache.openejb.core.security.JaccProvider; +import org.apache.openejb.core.security.PolicyJDK24; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.spi.SecurityService; @@ -66,7 +67,7 @@ public class BasicJaccProvider extends JaccProvider { private final java.security.Policy systemPolicy; public BasicJaccProvider() { - systemPolicy = Policy.getPolicy(); + systemPolicy = PolicyJDK24.getPolicy(); } public PolicyConfiguration getPolicyConfiguration(final String contextID, final boolean remove) throws PolicyContextException { diff --git a/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java b/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java index 2193d03aaa..26fcf5cfd4 100644 --- a/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java +++ b/server/openejb-client/src/main/java/org/apache/openejb/client/JaasIdentityResolver.java @@ -17,15 +17,16 @@ */ package org.apache.openejb.client; +import org.apache.openejb.client.util.JDK24Subject; + import javax.security.auth.Subject; -import java.security.AccessController; import java.util.Set; public class JaasIdentityResolver implements IdentityResolver { @Override public Object getIdentity() { - final Subject subject = Subject.getSubject(AccessController.getContext()); + final Subject subject = JDK24Subject.currentSubject(); if (subject == null) { return null; } diff --git a/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java b/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java new file mode 100644 index 0000000000..3b35ff8d87 --- /dev/null +++ b/server/openejb-client/src/main/java/org/apache/openejb/client/util/JDK24Subject.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.client.util; + +import javax.security.auth.Subject; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class JDK24Subject { + + private static final MethodHandle CURRENT = lookupCurrent(); + + /** + * Maps to Subject.current() is available, otherwise maps to Subject.getSubject() + * @return the current subject + */ + public static Subject currentSubject() { + try { + return (Subject) CURRENT.invoke(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + private static MethodHandle lookupCurrent() { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + // Subject.getSubject(AccessControlContext) is deprecated for removal and replaced by + // Subject.current(). + // Lookup first the new API, since for Java versions where both exists, the + // new API delegates to the old API (for example Java 18, 19 and 20). + // Otherwise (Java 17), lookup the old API. + return lookup.findStatic(Subject.class, "current", + MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + final MethodHandle getContext = lookupGetContext(); + final MethodHandle getSubject = lookupGetSubject(); + return MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupGetSubject() { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + final Class<?> contextClazz = + ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + return lookup.findStatic(Subject.class, "getSubject", + MethodType.methodType(Subject.class, contextClazz)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupGetContext() { + try { + // Use reflection to work with Java versions that have and don't have AccessController. + final Class<?> controllerClazz = + ClassLoader.getSystemClassLoader().loadClass("java.security.AccessController"); + final Class<?> contextClazz = + ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + return lookup.findStatic(controllerClazz, "getContext", + MethodType.methodType(contextClazz)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } +} diff --git a/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java b/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java index ea10f2e667..380407a481 100644 --- a/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java +++ b/server/openejb-client/src/test/java/org/apache/openejb/client/MainTest.java @@ -18,6 +18,7 @@ package org.apache.openejb.client; import junit.framework.TestCase; +import org.apache.openejb.client.util.JDK24Subject; import javax.naming.Binding; import javax.naming.Context; @@ -87,7 +88,7 @@ public class MainTest extends TestCase { public static class SecureMain { public static void main(String[] args) { - Subject subject = Subject.getSubject(AccessController.getContext()); + Subject subject = JDK24Subject.currentSubject(); // verify subject assertEquals("Should have one principal", 1, subject.getPrincipals().size()); @@ -109,7 +110,7 @@ public class MainTest extends TestCase { public static class NormalMain { public static void main(String[] args) { - Subject subject = Subject.getSubject(AccessController.getContext()); + Subject subject = JDK24Subject.currentSubject(); assertNull("subject is not null", subject);