/**
*
* 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.geronimo.openejb.deployment;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.EJBRoleRefPermission;
import org.apache.geronimo.common.DeploymentException;
import org.apache.geronimo.security.jacc.ComponentPermissions;
import org.apache.openejb.jee.AssemblyDescriptor;
import org.apache.openejb.jee.ExcludeList;
import org.apache.openejb.jee.Method;
import org.apache.openejb.jee.MethodPermission;
import org.apache.openejb.jee.SecurityRoleRef;
public class SecurityBuilder {
/**
* Fill the container moduleBuilder with the security information that it needs
* to create the proper interceptors. A <code>SecurityConfiguration</code>
* is also filled with permissions that need to be used to fill the JACC
* policy configuration.
*
* @param defaultRole default role for otherwise unassigned permissions
* @param notAssigned the set of all possible permissions. These will be
* culled so that all that are left are those that have
* not been assigned roles.
* @param assemblyDescriptor the assembly descriptor
* @param ejbName the name of the EJB
* @param securityRoleRefs the EJB's role references
* @param componentPermissions the holder for the ejb's permissions
* @throws DeploymentException if any constraints are violated
*/
public void addComponentPermissions(String defaultRole,
Permissions notAssigned,
AssemblyDescriptor assemblyDescriptor,
String ejbName,
List<SecurityRoleRef> securityRoleRefs,
ComponentPermissions componentPermissions) throws DeploymentException {
PermissionCollection uncheckedPermissions = componentPermissions.getUncheckedPermissions();
PermissionCollection excludedPermissions = componentPermissions.getExcludedPermissions();
Map rolePermissions = componentPermissions.getRolePermissions();
//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.
if (assemblyDescriptor != null) {
/**
* JACC v1.0 section 3.1.5.1
*/
for (MethodPermission methodPermission : assemblyDescriptor.getMethodPermission()) {
List<String> roleNames = methodPermission.getRoleName();
boolean unchecked = methodPermission.getUnchecked() != null;
for (Method method : methodPermission.getMethod()) {
if (!ejbName.equals(method.getEjbName())) {
continue;
}
// method name
String methodName = method.getMethodName();
if ("*".equals(methodName)) {
// jacc uses null instead of *
methodName = null;
}
// method interface
String methodIntf = method.getMethodIntf().toString();
// method parameters
String[] methodParams;
if (method.getMethodParams() != null) {
List<String> paramList = method.getMethodParams().getMethodParam();
methodParams = paramList.toArray(new String[paramList.size()]);
} else {
methodParams = null;
}
// create the permission object
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 {
for (String roleName : roleNames) {
Permissions permissions = (Permissions) rolePermissions.get(roleName);
if (permissions == null) {
permissions = new Permissions();
rolePermissions.put(roleName, permissions);
}
permissions.add(permission);
}
}
}
}
/**
* JACC v1.0 section 3.1.5.2
*/
ExcludeList excludeList = assemblyDescriptor.getExcludeList();
if (excludeList != null) {
for (Method method : excludeList.getMethod()) {
if (!ejbName.equals(method.getEjbName())) {
continue;
}
// method name
String methodName = method.getMethodName();
// method interface
String methodIntf = method.getMethodIntf().toString();
// method parameters
String[] methodParams;
if (method.getMethodParams() != null) {
List<String> paramList = method.getMethodParams().getMethodParam();
methodParams = paramList.toArray(new String[paramList.size()]);
} else {
methodParams = null;
}
// create the permission object
EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
excludedPermissions.add(permission);
notAssigned = cullPermissions(notAssigned, permission);
}
}
/**
* JACC v1.0 section 3.1.5.3
*/
for (SecurityRoleRef securityRoleRef : securityRoleRefs) {
if (securityRoleRef.getRoleLink() == null) {
throw new DeploymentException("Missing role-link");
}
String roleLink = securityRoleRef.getRoleLink();
PermissionCollection roleLinks = (PermissionCollection) rolePermissions.get(roleLink);
if (roleLinks == null) {
roleLinks = new Permissions();
rolePermissions.put(roleLink, roleLinks);
}
roleLinks.add(new EJBRoleRefPermission(ejbName, securityRoleRef.getRoleName()));
}
}
/**
* 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 = (PermissionCollection) rolePermissions.get(defaultRole);
if (permissions == null) {
permissions = new Permissions();
rolePermissions.put(defaultRole, permissions);
}
}
Enumeration e = notAssigned.elements();
while (e.hasMoreElements()) {
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 interfaceClass the class name of the interface to be used to generate the permissions
* @param classLoader the class loader to be used in obtaining the interface class
* @throws org.apache.geronimo.common.DeploymentException in case a class could not be found
*/
public void addToPermissions(Permissions permissions,
String ejbName,
String methodInterface,
String interfaceClass,
ClassLoader classLoader) throws DeploymentException {
if (interfaceClass == null) {
return;
}
try {
Class clazz = Class.forName(interfaceClass, false, classLoader);
for (java.lang.reflect.Method method : clazz.getMethods()) {
permissions.add(new EJBMethodPermission(ejbName, methodInterface, method));
}
} catch (ClassNotFoundException e) {
throw new DeploymentException(e);
}
}
/**
* 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 Permissions cullPermissions(Permissions toBeChecked, Permission permission) {
Permissions result = new Permissions();
for (Enumeration e = toBeChecked.elements(); e.hasMoreElements();) {
Permission test = (Permission) e.nextElement();
if (!permission.implies(test)) {
result.add(test);
}
}
return result;
}
}