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.security.jacc.EJBMethodPermission;
028    import javax.security.jacc.EJBRoleRefPermission;
029    
030    import org.apache.geronimo.common.DeploymentException;
031    import org.apache.geronimo.security.jacc.ComponentPermissions;
032    import org.apache.openejb.jee.AssemblyDescriptor;
033    import org.apache.openejb.jee.ExcludeList;
034    import org.apache.openejb.jee.Method;
035    import org.apache.openejb.jee.MethodPermission;
036    import org.apache.openejb.jee.SecurityRoleRef;
037    
038    public class SecurityBuilder {
039        /**
040         * Fill the container moduleBuilder with the security information that it needs
041         * to create the proper interceptors.  A <code>SecurityConfiguration</code>
042         * is also filled with permissions that need to be used to fill the JACC
043         * policy configuration.
044         *
045         * @param defaultRole default role for otherwise unassigned permissions
046         * @param notAssigned the set of all possible permissions.  These will be
047         * culled so that all that are left are those that have
048         * not been assigned roles.
049         * @param assemblyDescriptor the assembly descriptor
050         * @param ejbName the name of the EJB
051         * @param securityRoleRefs the EJB's role references
052         * @param componentPermissions the holder for the ejb's permissions
053         * @throws DeploymentException if any constraints are violated
054         */
055        public void addComponentPermissions(String defaultRole,
056                PermissionCollection notAssigned,
057                AssemblyDescriptor assemblyDescriptor,
058                String ejbName,
059                List<SecurityRoleRef> securityRoleRefs,
060                ComponentPermissions componentPermissions) throws DeploymentException {
061    
062            PermissionCollection uncheckedPermissions = componentPermissions.getUncheckedPermissions();
063            PermissionCollection excludedPermissions = componentPermissions.getExcludedPermissions();
064            Map<String, PermissionCollection> rolePermissions = componentPermissions.getRolePermissions();
065    
066            //this can occur in an ear when one ejb module has security and one doesn't.  In this case we still need
067            //to make the non-secure one completely unchecked.
068            if (assemblyDescriptor != null) {
069                /**
070                 * JACC v1.0 section 3.1.5.1
071                 */
072                for (MethodPermission methodPermission : assemblyDescriptor.getMethodPermission()) {
073                    List<String> roleNames = methodPermission.getRoleName();
074                    boolean unchecked = methodPermission.getUnchecked();
075    
076                    for (Method method : methodPermission.getMethod()) {
077                        if (!ejbName.equals(method.getEjbName())) {
078                            continue;
079                        }
080    
081                        // method name
082                        String methodName = method.getMethodName();
083                        if ("*".equals(methodName)) {
084                            // jacc uses null instead of *
085                            methodName = null;
086                        }
087                        // method interface
088                        String methodIntf = method.getMethodIntf() == null? null: method.getMethodIntf().toString();
089    
090                        // method parameters
091                        String[] methodParams;
092                        if (method.getMethodParams() != null) {
093                            List<String> paramList = method.getMethodParams().getMethodParam();
094                            methodParams = paramList.toArray(new String[paramList.size()]);
095                        } else {
096                            methodParams = null;
097                        }
098    
099                        // create the permission object
100                        EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
101                        notAssigned = cullPermissions(notAssigned, permission);
102    
103                        // if this is unchecked, mark it as unchecked; otherwise assign the roles
104                        if (unchecked) {
105                            uncheckedPermissions.add(permission);
106                        } else {
107                            for (String roleName : roleNames) {
108                                Permissions permissions = (Permissions) rolePermissions.get(roleName);
109                                if (permissions == null) {
110                                    permissions = new Permissions();
111                                    rolePermissions.put(roleName, permissions);
112                                }
113                                permissions.add(permission);
114                            }
115                        }
116                    }
117    
118                }
119    
120                /**
121                 * JACC v1.0 section 3.1.5.2
122                 */
123                ExcludeList excludeList = assemblyDescriptor.getExcludeList();
124                if (excludeList != null) {
125                    for (Method method : excludeList.getMethod()) {
126                        if (!ejbName.equals(method.getEjbName())) {
127                            continue;
128                        }
129    
130                        // method name
131                        String methodName = method.getMethodName();
132                        // method interface
133                        String methodIntf = method.getMethodIntf() == null? null: method.getMethodIntf().toString();
134    
135                        // method parameters
136                        String[] methodParams;
137                        if (method.getMethodParams() != null) {
138                            List<String> paramList = method.getMethodParams().getMethodParam();
139                            methodParams = paramList.toArray(new String[paramList.size()]);
140                        } else {
141                            methodParams = null;
142                        }
143    
144                        // create the permission object
145                        EJBMethodPermission permission = new EJBMethodPermission(ejbName, methodName, methodIntf, methodParams);
146    
147                        excludedPermissions.add(permission);
148                        notAssigned = cullPermissions(notAssigned, permission);
149                    }
150                }
151    
152                /**
153                 * JACC v1.0 section 3.1.5.3
154                 */
155                for (SecurityRoleRef securityRoleRef : securityRoleRefs) {
156    
157                    String roleLink = securityRoleRef.getRoleLink() == null? securityRoleRef.getRoleName(): securityRoleRef.getRoleLink();
158    
159                    PermissionCollection roleLinks = rolePermissions.get(roleLink);
160                    if (roleLinks == null) {
161                        roleLinks = new Permissions();
162                        rolePermissions.put(roleLink, roleLinks);
163    
164                    }
165                    roleLinks.add(new EJBRoleRefPermission(ejbName, securityRoleRef.getRoleName()));
166                }
167            }
168    
169            /**
170             * EJB v2.1 section 21.3.2
171             * <p/>
172             * It is possible that some methods are not assigned to any security
173             * roles nor contained in the <code>exclude-list</code> element. In
174             * this case, it is the responsibility of the Deployer to assign method
175             * permissions for all of the unspecified methods, either by assigning
176             * them to security roles, or by marking them as <code>unchecked</code>.
177             */
178            PermissionCollection permissions;
179            if (defaultRole == null) {
180                permissions = uncheckedPermissions;
181            } else {
182                permissions = rolePermissions.get(defaultRole);
183                if (permissions == null) {
184                    permissions = new Permissions();
185                    rolePermissions.put(defaultRole, permissions);
186                }
187            }
188    
189            Enumeration e = notAssigned.elements();
190            while (e.hasMoreElements()) {
191                Permission p = (Permission) e.nextElement();
192                permissions.add(p);
193            }
194    
195        }
196    
197        /**
198         * Generate all the possible permissions for a bean's interface.
199         * <p/>
200         * Method permissions are defined in the deployment descriptor as a binary
201         * relation from the set of security roles to the set of methods of the
202         * home, component, and/or web service endpoint interfaces of session and
203         * entity beans, including all their superinterfaces (including the methods
204         * of the <code>EJBHome</code> and <code>EJBObject</code> interfaces and/or
205         * <code>EJBLocalHome</code> and <code>EJBLocalObject</code> interfaces).
206         *
207         * @param permissions the permission set to be extended
208         * @param ejbName the name of the EJB
209         * @param methodInterface the EJB method interface
210         * @param interfaceClass the class name of the interface to be used to generate the permissions
211         * @param classLoader the class loader to be used in obtaining the interface class
212         * @throws org.apache.geronimo.common.DeploymentException in case a class could not be found
213         */
214        public void addToPermissions(PermissionCollection permissions,
215                String ejbName,
216                String methodInterface,
217                String interfaceClass,
218                ClassLoader classLoader) throws DeploymentException {
219    
220            if (interfaceClass == null) {
221                return;
222            }
223    
224            try {
225                Class clazz = Class.forName(interfaceClass, false, classLoader);
226                for (java.lang.reflect.Method method : clazz.getMethods()) {
227                    permissions.add(new EJBMethodPermission(ejbName, methodInterface, method));
228                }
229            } catch (ClassNotFoundException e) {
230                throw new DeploymentException(e);
231            }
232    
233        }
234    
235        /**
236         * Removes permissions from <code>toBeChecked</code> that are implied by
237         * <code>permission</code>.
238         *
239         * @param toBeChecked the permissions that are to be checked and possibly culled
240         * @param permission the permission that is to be used for culling
241         * @return the culled set of permissions that are not implied by <code>permission</code>
242         */
243        private Permissions cullPermissions(PermissionCollection toBeChecked, Permission permission) {
244            Permissions result = new Permissions();
245    
246            for (Enumeration e = toBeChecked.elements(); e.hasMoreElements();) {
247                Permission test = (Permission) e.nextElement();
248                if (!permission.implies(test)) {
249                    result.add(test);
250                }
251            }
252    
253            return result;
254        }
255    }