Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@

package org.apache.openejb.core.security;

import jakarta.security.jacc.EJBMethodPermission;
import jakarta.security.jacc.PolicyConfigurationFactory;
import jakarta.security.jacc.PolicyContext;
import jakarta.security.jacc.PolicyContextException;
import jakarta.security.jacc.PolicyContextHandler;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.openejb.BeanContext;
import org.apache.openejb.InterfaceType;
import org.apache.openejb.api.resource.DestroyableResource;
Expand All @@ -34,12 +40,6 @@

import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import jakarta.security.jacc.EJBMethodPermission;
import jakarta.security.jacc.PolicyConfigurationFactory;
import jakarta.security.jacc.PolicyContext;
import jakarta.security.jacc.PolicyContextException;
import jakarta.security.jacc.PolicyContextHandler;
import jakarta.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
Expand Down Expand Up @@ -371,24 +371,31 @@ private Principal getCallerPrincipal(final Set<Principal> principals) {

@Override
public boolean isCallerAuthorized(final Method method, final InterfaceType type) {
final ThreadContext threadContext = ThreadContext.getThreadContext();
final BeanContext beanContext = threadContext.getBeanContext();
try {
final String ejbName = beanContext.getEjbName();
String name = type == null ? null : type.getSpecName();
if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
name = null;
}
final Identity currentIdentity = clientIdentity.get();
final SecurityContext securityContext;
if (currentIdentity == null) {
securityContext = threadContext.get(SecurityContext.class);
} else {
securityContext = new SecurityContext(currentIdentity.getSubject());
if (System.getProperty("java.vm.specification.version").compareTo("21") < 0) {
final ThreadContext threadContext = ThreadContext.getThreadContext();
final BeanContext beanContext = threadContext.getBeanContext();
try {

final String ejbName = beanContext.getEjbName();
String name = type == null ? null : type.getSpecName();
if ("LocalBean".equals(name) || "LocalBeanHome".equals(name)) {
name = null;
}

final Identity currentIdentity = clientIdentity.get();
final SecurityContext securityContext;
if (currentIdentity == null) {
securityContext = threadContext.get(SecurityContext.class);
} else {
securityContext = new SecurityContext(currentIdentity.getSubject());
}

securityContext.getAccessControlContext().checkPermission(new EJBMethodPermission(ejbName, name, method));
} catch (final AccessControlException e) {
return false;
}
securityContext.getAccessControlContext().checkPermission(new EJBMethodPermission(ejbName, name, method));
} catch (final AccessControlException e) {
return false;
} else {
LOGGER.warning("Skipping JACC authorization check for method {} on type {} as TomEE running on JDK 21+ does not support method security at the moment.", method, type);
}
return true;
}
Expand Down Expand Up @@ -422,11 +429,11 @@ protected static void installJacc() {

// 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());
}
Expand All @@ -436,15 +443,34 @@ private static void installPolicy(String policyProvider) {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -87,7 +88,7 @@ public void testSecureMainFailed() throws Exception {
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());
Expand All @@ -109,7 +110,7 @@ public void testNormalMain() throws Exception {
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);

Expand Down