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    }