blob: f52b6de0980dd99c3ddbc3f2d9146791f2eb8c8c [file] [log] [blame]
/*
* 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.assembler.classic;
import org.apache.openejb.BeanContext;
import org.apache.openejb.InterfaceType;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.JavaSecurityManagers;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.EJBRoleRefPermission;
import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyConfigurationFactory;
import javax.security.jacc.PolicyContextException;
import java.lang.reflect.Method;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.openejb.assembler.classic.MethodInfoUtil.resolveAttributes;
/**
* @version $Rev$ $Date$
*/
public class JaccPermissionsBuilder {
static {
JavaSecurityManagers.setSystemProperty("org.apache.security.jacc.EJBMethodPermission.methodInterfaces", "BusinessLocalHome,BusinessRemoteHome,BusinessRemote,BusinessLocal");
}
public void install(final PolicyContext policyContext) throws OpenEJBException {
if (SystemInstance.get().hasProperty("openejb.geronimo")) {
return;
}
try {
final PolicyConfigurationFactory factory = PolicyConfigurationFactory.getPolicyConfigurationFactory();
final PolicyConfiguration policy = factory.getPolicyConfiguration(policyContext.getContextID(), false);
policy.addToExcludedPolicy(policyContext.getExcludedPermissions());
policy.addToUncheckedPolicy(policyContext.getUncheckedPermissions());
for (final Map.Entry<String, PermissionCollection> entry : policyContext.getRolePermissions().entrySet()) {
policy.addToRole(entry.getKey(), entry.getValue());
}
policy.commit();
} catch (final ClassNotFoundException e) {
throw new OpenEJBException("PolicyConfigurationFactory class not found", e);
} catch (final PolicyContextException e) {
throw new OpenEJBException("JACC PolicyConfiguration failed: ContextId=" + policyContext.getContextID(), e);
}
}
private static final Logger log = Logger.getInstance(LogCategory.OPENEJB_STARTUP.createChild("attributes"), JaccPermissionsBuilder.class);
public PolicyContext build(final EjbJarInfo ejbJar, final HashMap<String, BeanContext> deployments) throws OpenEJBException {
final List<MethodPermissionInfo> normalized = new ArrayList<>();
List<MethodPermissionInfo> perms = ejbJar.methodPermissions;
for (final MethodInfo info : ejbJar.excludeList) {
final MethodPermissionInfo perm = new MethodPermissionInfo();
perm.excluded = true;
perm.methods.add(info);
perms.add(perm);
}
perms = MethodInfoUtil.normalizeMethodPermissionInfos(perms);
for (final BeanContext beanContext : deployments.values()) {
final Map<Method, MethodAttributeInfo> attributes = resolveAttributes(perms, beanContext);
if (log.isDebugEnabled()) {
for (final Map.Entry<Method, MethodAttributeInfo> entry : attributes.entrySet()) {
final Method method = entry.getKey();
final MethodPermissionInfo value = (MethodPermissionInfo) entry.getValue();
log.debug("Security Attribute: " + method + " -- " + MethodInfoUtil.toString(value));
}
}
for (final Map.Entry<Method, MethodAttributeInfo> entry : attributes.entrySet()) {
final Method method = entry.getKey();
final MethodPermissionInfo a = (MethodPermissionInfo) entry.getValue();
final MethodPermissionInfo b = new MethodPermissionInfo();
b.excluded = a.excluded;
b.unchecked = a.unchecked;
b.roleNames.addAll(a.roleNames);
final MethodInfo am = a.methods.get(0);
final MethodInfo bm = new MethodInfo();
bm.ejbName = beanContext.getEjbName();
bm.ejbDeploymentId = String.valueOf(beanContext.getDeploymentID());
bm.methodIntf = am.methodIntf;
bm.className = method.getDeclaringClass().getName();
bm.methodName = method.getName();
bm.methodParams = new ArrayList<>();
for (final Class<?> type : method.getParameterTypes()) {
bm.methodParams.add(type.getName());
}
b.methods.add(bm);
normalized.add(b);
}
}
ejbJar.methodPermissions.clear();
ejbJar.methodPermissions.addAll(normalized);
ejbJar.excludeList.clear();
final PolicyContext policyContext = new PolicyContext(ejbJar.moduleUri.toString());
for (final EnterpriseBeanInfo enterpriseBean : ejbJar.enterpriseBeans) {
final BeanContext beanContext = deployments.get(enterpriseBean.ejbDeploymentId);
final PermissionCollection permissions = DelegatePermissionCollection.getPermissionCollection();
final String ejbName = enterpriseBean.ejbName;
for (final InterfaceType type : InterfaceType.values()) {
if (type == InterfaceType.UNKNOWN) {
continue;
}
for (final Class interfce : beanContext.getInterfaces(type)) {
addPossibleEjbMethodPermissions(permissions, ejbName, type.getSpecName(), interfce);
}
}
addPossibleEjbMethodPermissions(permissions, ejbName, null, beanContext.getBeanClass());
addDeclaredEjbPermissions(ejbJar, enterpriseBean, null, permissions, policyContext);
}
return policyContext;
}
private void addDeclaredEjbPermissions(final EjbJarInfo ejbJar,
final EnterpriseBeanInfo beanInfo,
final String defaultRole,
PermissionCollection notAssigned,
final PolicyContext policyContext) throws OpenEJBException {
final PermissionCollection uncheckedPermissions = policyContext.getUncheckedPermissions();
final PermissionCollection excludedPermissions = policyContext.getExcludedPermissions();
final Map<String, PermissionCollection> rolePermissions = policyContext.getRolePermissions();
final String ejbName = beanInfo.ejbName;
//this can occur in an ear when one ejb module has security and one doesn't. In this case we still need
//to make the non-secure one completely unchecked.
/**
* JACC v1.0 section 3.1.5.1
*/
for (final MethodPermissionInfo methodPermission : ejbJar.methodPermissions) {
final List<String> roleNames = methodPermission.roleNames;
final boolean unchecked = methodPermission.unchecked;
final boolean excluded = methodPermission.excluded;
for (final MethodInfo method : methodPermission.methods) {
if (!ejbName.equals(method.ejbName)) {
continue;
}
// method name
String methodName = method.methodName;
if ("*".equals(methodName)) {
// jacc uses null instead of *
methodName = null;
}
// method interface
final String methodIntf = method.methodIntf;
// method parameters
final String[] methodParams;
if (method.methodParams != null) {
final List<String> paramList = method.methodParams;
methodParams = paramList.toArray(new String[paramList.size()]);
} else {
methodParams = null;
}
// create the permission object
final EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
notAssigned = cullPermissions(notAssigned, permission);
// if this is unchecked, mark it as unchecked; otherwise assign the roles
if (unchecked) {
uncheckedPermissions.add(permission);
} else if (excluded) {
/**
* JACC v1.0 section 3.1.5.2
*/
excludedPermissions.add(permission);
} else {
for (final String roleName : roleNames) {
PermissionCollection permissions = rolePermissions.get(roleName);
if (permissions == null) {
permissions = DelegatePermissionCollection.getPermissionCollection();
rolePermissions.put(roleName, permissions);
}
permissions.add(permission);
}
}
}
}
/**
* JACC v1.0 section 3.1.5.3
*/
for (final SecurityRoleReferenceInfo securityRoleRef : beanInfo.securityRoleReferences) {
if (securityRoleRef.roleLink == null) {
throw new OpenEJBException("Missing role-link");
}
final String roleLink = securityRoleRef.roleLink;
PermissionCollection roleLinks = rolePermissions.get(roleLink);
if (roleLinks == null) {
roleLinks = DelegatePermissionCollection.getPermissionCollection();
rolePermissions.put(roleLink, roleLinks);
}
roleLinks.add(new EJBRoleRefPermission(ejbName, securityRoleRef.roleName));
}
/**
* EJB v2.1 section 21.3.2
*
* It is possible that some methods are not assigned to any security
* roles nor contained in the <code>exclude-list</code> element. In
* this case, it is the responsibility of the Deployer to assign method
* permissions for all of the unspecified methods, either by assigning
* them to security roles, or by marking them as <code>unchecked</code>.
*/
PermissionCollection permissions;
if (defaultRole == null) {
permissions = uncheckedPermissions;
} else {
permissions = rolePermissions.get(defaultRole);
if (permissions == null) {
permissions = DelegatePermissionCollection.getPermissionCollection();
rolePermissions.put(defaultRole, permissions);
}
}
final Enumeration e = notAssigned.elements();
while (e.hasMoreElements()) {
final Permission p = (Permission) e.nextElement();
permissions.add(p);
}
}
/**
* Generate all the possible permissions for a bean's interface.
*
* Method permissions are defined in the deployment descriptor as a binary
* relation from the set of security roles to the set of methods of the
* home, component, and/or web service endpoint interfaces of session and
* entity beans, including all their superinterfaces (including the methods
* of the <code>EJBHome</code> and <code>EJBObject</code> interfaces and/or
* <code>EJBLocalHome</code> and <code>EJBLocalObject</code> interfaces).
*
* @param permissions the permission set to be extended
* @param ejbName the name of the EJB
* @param methodInterface the EJB method interface
* @param clazz clazz
* @throws OpenEJBException in case a class could not be found
*/
public void addPossibleEjbMethodPermissions(final PermissionCollection permissions,
final String ejbName,
final String methodInterface,
final Class clazz) throws OpenEJBException {
if (clazz == null) {
return;
}
for (final Method method : clazz.getMethods()) {
final String methodIface = "LocalBean".equals(methodInterface) || "LocalBeanHome".equals(methodInterface) ? null : methodInterface;
permissions.add(new EJBMethodPermission(ejbName, methodIface, method));
}
}
/**
* Removes permissions from <code>toBeChecked</code> that are implied by
* <code>permission</code>.
*
* @param toBeChecked the permissions that are to be checked and possibly culled
* @param permission the permission that is to be used for culling
* @return the culled set of permissions that are not implied by <code>permission</code>
*/
private PermissionCollection cullPermissions(final PermissionCollection toBeChecked, final Permission permission) {
final PermissionCollection result = DelegatePermissionCollection.getPermissionCollection();
for (final Enumeration e = toBeChecked.elements(); e.hasMoreElements(); ) {
final Permission test = (Permission) e.nextElement();
if (!permission.implies(test)) {
result.add(test);
}
}
return result;
}
}