/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.security; import java.lang.reflect.Method; import java.util.HashMap; import javax.ejb.EJBContext; /** * An abstract implementation of SecurityProxy that wraps a non-SecurityProxy * object. Subclasses of this class are used to create a SecurityProxy given * a security delegate that implements methods in the EJB home or remote * interface for security checks. This allows custom security classes to be * written without using a JBoss specific interface. It also allows the security * delegate to follow a natural proxy pattern implementation. * * @author Scott.Stark@jboss.org * @version $Revision: 85945 $ */ public abstract class AbstractSecurityProxy implements SecurityProxy { /** The HashMap<Method, Method> from the EJB interface methods to the * corresponding delegate method */ private HashMap methodMap; /** The optional setContext delegate method */ private Method setContextMethod; /** The optional setContext delegate method */ private Method setBeanMethod; /** The optional setContext delegate method */ protected Object delegate; /** Flag which sets whether the method mapping will be performed in a strict * fashion. The proxy delegate must provide an implementation of all methods. * If set to 'true', a security exception will be thrown during * initialisation if a method is found for which the delegate doesn't have * a matching method. This defaults to false and is obtained via reflection * on the proxy delegate's 'boolean isStrict()' method. */ protected boolean strict = false; AbstractSecurityProxy(Object delegate) { this.delegate = delegate; methodMap = new HashMap(); } /** * Subclasses implement this method to actually invoke the given home * method on the proxy delegate. * * @param m, the delegate method that was mapped from the ejb home method. * @param args, the method invocation arguments. * @param delegate, the proxy delegate object associated with the * AbstractSecurityProxy * * @see invokeHome(Method, Object[]) */ protected abstract void invokeHomeOnDelegate(Method m, Object[] args, Object delegate) throws Exception; /** * Subclasses implement this method to actually invoke the given remote * method on the proxy delegate. * * @param m, the delegate method that was mapped from the ejb remote method. * @param args, the method invocation arguments. * @param delegate, the proxy delegate object associated with the AbstractSecurityProxy * * @see invoke(Method, Object[], Object) */ protected abstract void invokeOnDelegate(Method m, Object[] args, Object delegate) throws Exception; /** * * This version invokes init(beanHome, beanRemote, null, null, securityMgr) * * @see #init(Class, Class, Class, Class, Object) * @param beanHome, the class for the EJB home interface * @param beanRemote, the class for the EJB remote interface * @param securityMgr, The security manager instance assigned to the container. * It is not used by this class. */ public void init(Class beanHome, Class beanRemote, Object securityMgr) throws InstantiationException { init(beanHome, beanRemote, null, null, securityMgr); } /** This method is called by the container SecurityInterceptor to intialize * the proxy with the EJB home and remote interface classes that the * container is housing. This method creates a mapping from the home and * remote classes to the proxy delegate instance. The mapping is based on * method name and paramter types. In addition, the proxy delegate is * inspected for a setEJBContext(EJBContext) and a setBean(Object) method * so that the active EJBContext and EJB instance can be passed to the * delegate prior to method invocations. * * @param beanHome The EJB remote home interface class * @param beanRemote The EJB remote interface class * @param beanLocalHome The EJB local home interface class * @param beanLocal The EJB local interface class * @param securityMgr The security manager from the security domain * @throws InstantiationException */ public void init(Class beanHome, Class beanRemote, Class beanLocalHome, Class beanLocal, Object securityMgr) throws InstantiationException { // Get any methods from the bean home interface mapHomeMethods(beanHome); // Get any methods from the bean local home interface mapHomeMethods(beanLocalHome); // Get any methods from the bean remote interface mapRemoteMethods(beanRemote); // Get any methods from the bean local interface mapRemoteMethods(beanLocal); // Get the setEJBContext(EJBContext) method try { Class[] parameterTypes = {EJBContext.class}; setContextMethod = delegate.getClass().getMethod("setEJBContext", parameterTypes); } catch(Exception ignore) { } // Get the setBean(Object) method try { Class[] parameterTypes = {Object.class}; setBeanMethod = delegate.getClass().getMethod("setBean", parameterTypes); } catch(Exception ignore) { } // Check for a boolean isStrict() strict flag accessor try { Class[] parameterTypes = {}; Object[] args = {}; Method isStrict = delegate.getClass().getMethod("isStrict", parameterTypes); Boolean flag = (Boolean) isStrict.invoke(delegate, args); strict = flag.booleanValue(); } catch(Exception ignore) { } } /** Called by the SecurityProxyInterceptor prior to a method invocation * to set the context for the call. * * @param ctx the bean's EJBContext */ public void setEJBContext(EJBContext ctx) { if(setContextMethod != null) { Object[] args = {ctx}; try { setContextMethod.invoke(delegate, args); } catch(Exception e) { e.printStackTrace(); } } } /** Called by the SecurityProxyInterceptor to allow the proxy delegate to * perform a security check of the indicated home interface method. * * @param m, the EJB home interface method * @param args, the method arguments */ public void invokeHome(final Method m, Object[] args) throws Exception { Method delegateMethod = (Method)methodMap.get(m); if( delegateMethod != null ) invokeHomeOnDelegate(delegateMethod, args, delegate); } /** * Called by the SecurityProxyInterceptor to allow the proxy delegate to perform * a security check of the indicated remote interface method. * @param m, the EJB remote interface method * @param args, the method arguments * @param bean, the EJB bean instance */ public void invoke(final Method m, final Object[] args, final Object bean) throws Exception { Method delegateMethod = (Method)methodMap.get(m); if( delegateMethod != null ) { if( setBeanMethod != null ) { Object[] bargs = {bean}; try { setBeanMethod.invoke(delegate, bargs); } catch(Exception e) { e.printStackTrace(); throw new SecurityException("Failed to set bean on proxy" + e.getMessage()); } } invokeOnDelegate(delegateMethod, args, delegate); } } /** Performs a mapping from the methods declared in the beanHome class to * the proxy delegate class. This allows the methods to be either named * the same as the home interface method "create(...)" or as the bean * class method "ejbCreate(...)". This handles both local home and * remote home interface methods. */ protected void mapHomeMethods(Class beanHome) { if( beanHome == null ) return; Class delegateClass = delegate.getClass(); Method[] methods = beanHome.getMethods(); for(int m = 0; m < methods.length; m++) { // Check for ejbCreate... methods Method hm = methods[m]; Class[] parameterTypes = hm.getParameterTypes(); String name = hm.getName(); name = "ejb" + Character.toUpperCase(name.charAt(0)) + name.substring(1); try { Method match = delegateClass.getMethod(name, parameterTypes); methodMap.put(hm, match); } catch(NoSuchMethodException e) { // Try for the home interface name without the ejb prefix name = hm.getName(); try { Method match = delegateClass.getMethod(name, parameterTypes); methodMap.put(hm, match); } catch(NoSuchMethodException e2) { if( strict ) { String msg = "Missing home method:" + hm + " in delegate"; throw new SecurityException(msg); } } } } } /** Performs a mapping from the methods declared in the beanRemote class to * the proxy delegate class. This handles both local and remote interface * methods. */ protected void mapRemoteMethods(Class beanRemote) { if( beanRemote == null ) return; Class delegateClass = delegate.getClass(); Method[] methods = beanRemote.getMethods(); for(int m = 0; m < methods.length; m++) { Method rm = methods[m]; Class[] parameterTypes = rm.getParameterTypes(); String name = rm.getName(); try { Method match = delegateClass.getMethod(name, parameterTypes); methodMap.put(rm, match); } catch(NoSuchMethodException e) { if( strict ) { String msg = "Missing method:" + rm + " in delegate"; throw new SecurityException(msg); } } } } }