001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.geronimo.openejb.deployment;
019
020 import java.security.Permission;
021 import java.security.PermissionCollection;
022 import java.security.Permissions;
023 import java.util.Enumeration;
024 import java.util.List;
025 import java.util.Map;
026
027 import javax.ejb.TimedObject;
028 import javax.ejb.Timer;
029 import javax.security.jacc.EJBMethodPermission;
030 import javax.security.jacc.EJBRoleRefPermission;
031
032 import org.apache.geronimo.common.DeploymentException;
033 import org.apache.geronimo.security.jacc.ComponentPermissions;
034 import org.apache.openejb.jee.AssemblyDescriptor;
035 import org.apache.openejb.jee.ExcludeList;
036 import org.apache.openejb.jee.MessageDrivenBean;
037 import org.apache.openejb.jee.Method;
038 import org.apache.openejb.jee.MethodPermission;
039 import org.apache.openejb.jee.NamedMethod;
040 import org.apache.openejb.jee.RemoteBean;
041 import org.apache.openejb.jee.SecurityRoleRef;
042 import org.apache.openejb.jee.SessionBean;
043
044 public class SecurityBuilder {
045 /**
046 * Fill the container moduleBuilder with the security information that it needs
047 * to create the proper interceptors. A <code>SecurityConfiguration</code>
048 * is also filled with permissions that need to be used to fill the JACC
049 * policy configuration.
050 *
051 * @param defaultRole default role for otherwise unassigned permissions
052 * @param notAssigned the set of all possible permissions. These will be
053 * culled so that all that are left are those that have
054 * not been assigned roles.
055 * @param assemblyDescriptor the assembly descriptor
056 * @param ejbName the name of the EJB
057 * @param securityRoleRefs the EJB's role references
058 * @param componentPermissions the holder for the ejb's permissions
059 * @throws DeploymentException if any constraints are violated
060 */
061 public void addComponentPermissions(String defaultRole,
062 PermissionCollection notAssigned,
063 AssemblyDescriptor assemblyDescriptor,
064 String ejbName,
065 List<SecurityRoleRef> securityRoleRefs,
066 ComponentPermissions componentPermissions) throws DeploymentException {
067
068 PermissionCollection uncheckedPermissions = componentPermissions.getUncheckedPermissions();
069 PermissionCollection excludedPermissions = componentPermissions.getExcludedPermissions();
070 Map<String, PermissionCollection> rolePermissions = componentPermissions.getRolePermissions();
071
072 //this can occur in an ear when one ejb module has security and one doesn't. In this case we still need
073 //to make the non-secure one completely unchecked.
074 if (assemblyDescriptor != null) {
075 /**
076 * JACC v1.0 section 3.1.5.1
077 */
078 for (MethodPermission methodPermission : assemblyDescriptor.getMethodPermission()) {
079 List<String> roleNames = methodPermission.getRoleName();
080 boolean unchecked = methodPermission.getUnchecked();
081
082 for (Method method : methodPermission.getMethod()) {
083 if (!ejbName.equals(method.getEjbName())) {
084 continue;
085 }
086
087 // method name
088 String methodName = method.getMethodName();
089 if ("*".equals(methodName)) {
090 // jacc uses null instead of *
091 methodName = null;
092 }
093 // method interface
094 String methodIntf = method.getMethodIntf() == null ? null : method.getMethodIntf().toString();
095
096 // method parameters
097 String[] methodParams;
098 if (method.getMethodParams() != null) {
099 List<String> paramList = method.getMethodParams().getMethodParam();
100 methodParams = paramList.toArray(new String[paramList.size()]);
101 } else {
102 methodParams = null;
103 }
104
105 // create the permission object
106 EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
107 notAssigned = cullPermissions(notAssigned, permission);
108
109 // if this is unchecked, mark it as unchecked; otherwise assign the roles
110 if (unchecked) {
111 uncheckedPermissions.add(permission);
112 } else {
113 for (String roleName : roleNames) {
114 Permissions permissions = (Permissions) rolePermissions.get(roleName);
115 if (permissions == null) {
116 permissions = new Permissions();
117 rolePermissions.put(roleName, permissions);
118 }
119 permissions.add(permission);
120 }
121 }
122 }
123
124 }
125
126 /**
127 * JACC v1.0 section 3.1.5.2
128 */
129 ExcludeList excludeList = assemblyDescriptor.getExcludeList();
130 if (excludeList != null) {
131 for (Method method : excludeList.getMethod()) {
132 if (!ejbName.equals(method.getEjbName())) {
133 continue;
134 }
135
136 // method name
137 String methodName = method.getMethodName();
138 // method interface
139 String methodIntf = method.getMethodIntf() == null ? null : method.getMethodIntf().toString();
140
141 // method parameters
142 String[] methodParams;
143 if (method.getMethodParams() != null) {
144 List<String> paramList = method.getMethodParams().getMethodParam();
145 methodParams = paramList.toArray(new String[paramList.size()]);
146 } else {
147 methodParams = null;
148 }
149
150 // create the permission object
151 EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
152
153 excludedPermissions.add(permission);
154 notAssigned = cullPermissions(notAssigned, permission);
155 }
156 }
157
158 /**
159 * JACC v1.0 section 3.1.5.3
160 */
161 for (SecurityRoleRef securityRoleRef : securityRoleRefs) {
162
163 String roleLink = securityRoleRef.getRoleLink() == null ? securityRoleRef.getRoleName() : securityRoleRef.getRoleLink();
164
165 PermissionCollection roleLinks = rolePermissions.get(roleLink);
166 if (roleLinks == null) {
167 roleLinks = new Permissions();
168 rolePermissions.put(roleLink, roleLinks);
169
170 }
171 roleLinks.add(new EJBRoleRefPermission(ejbName, securityRoleRef.getRoleName()));
172 }
173 }
174
175 /**
176 * EJB v2.1 section 21.3.2
177 * <p/>
178 * It is possible that some methods are not assigned to any security
179 * roles nor contained in the <code>exclude-list</code> element. In
180 * this case, it is the responsibility of the Deployer to assign method
181 * permissions for all of the unspecified methods, either by assigning
182 * them to security roles, or by marking them as <code>unchecked</code>.
183 */
184 PermissionCollection permissions;
185 if (defaultRole == null) {
186 permissions = uncheckedPermissions;
187 } else {
188 permissions = rolePermissions.get(defaultRole);
189 if (permissions == null) {
190 permissions = new Permissions();
191 rolePermissions.put(defaultRole, permissions);
192 }
193 }
194
195 Enumeration e = notAssigned.elements();
196 while (e.hasMoreElements()) {
197 Permission p = (Permission) e.nextElement();
198 permissions.add(p);
199 }
200
201 }
202
203 /**
204 * Generate all the possible permissions for a bean's interface.
205 * <p/>
206 * Method permissions are defined in the deployment descriptor as a binary
207 * relation from the set of security roles to the set of methods of the
208 * home, component, and/or web service endpoint interfaces of session and
209 * entity beans, including all their superinterfaces (including the methods
210 * of the <code>EJBHome</code> and <code>EJBObject</code> interfaces and/or
211 * <code>EJBLocalHome</code> and <code>EJBLocalObject</code> interfaces).
212 *
213 * @param permissions the permission set to be extended
214 * @param ejbName the name of the EJB
215 * @param methodInterface the EJB method interface
216 * @param interfaceClass the class name of the interface to be used to generate the permissions
217 * @param classLoader the class loader to be used in obtaining the interface class
218 * @throws org.apache.geronimo.common.DeploymentException
219 * in case a class could not be found
220 */
221 public void addToPermissions(PermissionCollection permissions,
222 String ejbName,
223 String methodInterface,
224 String interfaceClass,
225 ClassLoader classLoader) throws DeploymentException {
226
227 if (interfaceClass == null) {
228 return;
229 }
230
231 try {
232 Class clazz = Class.forName(interfaceClass, false, classLoader);
233 for (java.lang.reflect.Method method : clazz.getMethods()) {
234 permissions.add(new EJBMethodPermission(ejbName, methodInterface, method));
235 }
236 } catch (ClassNotFoundException e) {
237 throw new DeploymentException(e);
238 }
239
240 }
241
242 /**
243 * Removes permissions from <code>toBeChecked</code> that are implied by
244 * <code>permission</code>.
245 *
246 * @param toBeChecked the permissions that are to be checked and possibly culled
247 * @param permission the permission that is to be used for culling
248 * @return the culled set of permissions that are not implied by <code>permission</code>
249 */
250 private Permissions cullPermissions(PermissionCollection toBeChecked, Permission permission) {
251 Permissions result = new Permissions();
252
253 for (Enumeration e = toBeChecked.elements(); e.hasMoreElements();) {
254 Permission test = (Permission) e.nextElement();
255 if (!permission.implies(test)) {
256 result.add(test);
257 }
258 }
259
260 return result;
261 }
262
263 public void addEjbTimeout(RemoteBean remoteBean, EjbModule ejbModule, PermissionCollection permissions) throws DeploymentException {
264 NamedMethod timeout = null;
265 if (remoteBean instanceof SessionBean) {
266 timeout = ((SessionBean) remoteBean).getTimeoutMethod();
267 } else if (remoteBean instanceof MessageDrivenBean) {
268 timeout = ((MessageDrivenBean) remoteBean).getTimeoutMethod();
269 }
270 if (timeout != null) {
271 permissions.add(new EJBMethodPermission(remoteBean.getEjbName(), timeout.getMethodName(), null, new String[]{Timer.class.getName()}));
272 } else {
273 try {
274 Class ejbClass = ejbModule.getClassLoader().loadClass(remoteBean.getEjbClass());
275 if (TimedObject.class.isAssignableFrom(ejbClass)) {
276 permissions.add(new EJBMethodPermission(remoteBean.getEjbName(), "ejbTimeout", null, new String[]{Timer.class.getName()}));
277 }
278 } catch (ClassNotFoundException e) {
279 throw new DeploymentException("Could not figure out timer method", e);
280 }
281 }
282 }
283
284 }