/* * 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<MethodPermissionInfo>(); 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<String>(); 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 * <p/> * 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. * <p/> * 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; } }