Sunday, September 23, 2007

Dynamic roles management in acegi security

The example that comes with acegi security, and the one that comes with appfuse define secure URL patterns, and the roles that can access them in the XML configuration file, which is not a very flexible solution. It is a good approach if you have predefined roles and access rights , that are never going to change (or the changes are very rare). In case you have a security module, where you define which role can access which URL patterns, this method is the worst thing you could do. The solution to this problem is to define your own data source, which loads the secure URL patterns and the corresponding roles that can access it from somewhere (database for example). A simple way to do that is by extending PathBasedFilterInvocationDefinitionMap and loading the secure URL patterns and the roles that can access them in an initialization method or in the constructor. Here's an example:

public class UrlPatternRolesDefinitionSource extends PathBasedFilterInvocationDefinitionMap {

public void init(){
Session session = sessionFactory.openSession();
try{

List urlPatternsList = urlPatternDao.listUrlPatterns(session);

for (UrlPattern pattern : urlPatternsList) {

ConfigAttributeDefinition configDefinition = new ConfigAttributeDefinition();

for(Role role: pattern.getRoles()){

ConfigAttribute config = new SecurityConfig(role.getAuthority());

configDefinition.addConfigAttribute(config);
}

addSecureUrl(pattern.getUrlPattern(), configDefinition);

}
}
catch (FindException e) {
// Handle exception
}
finally {
session.close();
}
}
}
In this example I used a Hibernate DAO I created (urlPatternDao) to retrieve the secure URL patterns which is defined and initialized with the sessionFactory somewhere else in the code (using spring's dependency injection). You can implement it anyway you like (JDBC, with any other ORM framework, or any other method you like). In this example, each URL pattern can has a set roles that can access it, so I loop over this set to create the ConfigAttributeDefinition. I prefer if you have your own implementation of the ConfigAttributeDefinition, but this is going to be discussed in part 2 of this article. Of course the Role class used in this code implements the GrantedAuthority interface. As for the XML configuration of this entry, it's shown below:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web. FilterSecurityInterceptor">

<property name="authenticationManager" ref="authenticationManager"/>

<property name="accessDecisionManager" ref="accessDecisionManager"/>

<property name="objectDefinitionSource" ref="objectDefinitionSource"/>

</bean>


<bean id="objectDefinitionSource" class= "net.jnassef.security.UrlPatternRolesDefinitionSource" init-method="init">

<property name="convertUrlToLowercaseBeforeComparison">
<value type="boolean">false</value>
</property>

<property name="urlPatternDao" ref="urlPatternDao"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
As for the first bean in the previous snippet, this definition is the default that comes with the sample acegi application and with appfuse, except to the reference to the objectDefinitionSource bean which was added by us, to allow dynamic role management. The second bean maps to the class we created, and defines the initialization method, that will load the secured URL patterns, and the roles associated with them. This definition also sets the DAO and the hibernate session factory used by the class. Of course you don't need those if you want to have any other implementation. The previous implementation seems good as a solution for dynamic role management, but if we look more closely, it's not a very good solution (or to be more accurate, it's not a complete solution). The problem that is going to face us here is that secure URL patterns are loaded only ONCE at the application start up, and any change done to the role access rights will not effective until the application is started, and this is not dynamic at all. In fact, there is no difference between this solution, and having the access rights in the XML configuration file, except that you will not need to redeploy your application, but you will need to restart it. There are several solutions to this problem, and this will be the main focus of the next part of this article. By Nassef

Using a Shared Context from EJBs

ContextSingletonBeanFactoryLocator and SingletonBeanFactoryLocator

The basic premise behind ContextSingletonBeanFactoryLocator is that there is a shared application context, which is shared based on a string key. Inside this application context is instantiated one or more other application contexts or bean factories. The internal application contexts are what the application code is interested in, while the external context is just the bag (for want of a better term) holding them. Consider that a dozen different instances of non-IoC configured, application glue code need to access a shared application context, which is defined in an XML definition on the classpath as
serviceLayer-applicationContext.xml
.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
    
<!--
  Service layer ApplicationContext definition for the application.
  Defines beans belonging to service layer.
-->
    
<beans>
    
  <bean id="myService" class="...">
    ...
  </bean>
    
  ...     bean definitions
</beans>
The glue code cannot just instantiate this context as an XmlApplicationContext; each such instantiation would get its own copy. Instead, the code relies on ContextSingletonBeanFactoryLocator, which will load and then cache an outer application context, holding the service layer application context above. Let's look at some code that uses the locator to get the service layer context, from which it gets a bean:
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfr = locator.useBeanFactory("serviceLayer-context");
BeanFactory factory = bfr.getFactory();
MyService myService = factory.getBean("myService");
bfr.release();
    
// now use myService
Let's walk through the preceding code. The call to ContextSingletonBeanFactoryLocator.getInstance() triggers the loading of an application context definition from a file that is named (by default) beanRefContext.xml. We define the contents of this file as follows: beanRefContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">
    
<!-- load a hierarchy of contexts, although there is just one here -->
<beans>
    
  <bean id="servicelayer-context"
        class="org.springframework.context.support.ClassPathXmlApplicationContext">
    <constructor-arg>
      <list>
        <value>/servicelayer-applicationContext.xml</value>
      </list>
    </constructor-arg>
  </bean>
    
</beans>
As you can see, this is just a normal application context definition. All we are doing is loading one context inside another. However, if the outer context (keyed to the name beanRefContext.xml) had already been loaded at least once, the existing instance would just have been looked up and used. The locator.useBeanFactory("serviceLayer-context") method call returns the internal application context, which is asked for by name, serviceLayer-context in this case. It is returned in the form of a BeanFactoryRef object, which is just a wrapper used to ensure that the context is properly released when it's no longer needed. The method call BeanFactory factory = bfr.getFactory() actually obtains the context from the BeanFactoryRef. The code then uses the context via a normal getBean() call to get the service bean it needs, and then releases the context by calling release() on the BeanFactoryRef. Somewhat of a complicated sequence, but necessary because what is being added here is really a level of indirection so that multiple users can share one or more application context or bean factory definitions. We call this a keyed singleton because the outer context being used as a bag is shared based on a string key. When you get the BeanFactoryLocator via
ContextSingletonBeanFactoryLocator.getInstance();
it uses the default name
classpath*:beanRefContext.xml
as the resource location for the outer context definition. So all the files called beanRefContext.xml, which are available on the classpath, will be combined as XML fragments defining the outer context. This name (
classpath*:beanRefContext.xml
) is also the key by which other code will share the same context bag. But using the form:
ContextSingletonBeanFactoryLocator.getInstance();
for example:
contextSingletonBeanFactoryLocator.getInstance("contexts.xml");
or
contextSingletonBeanFactoryLocator.getInstance("classpath*:app-contexts.xml");
allows the name of the outer context definition to be changed. This allows a module to use a unique name that it knows will not conflict with another module. Note that the outer bag context may define inside it any number of bean factories or application contexts, not just one as in the previous example, and because the full power of the normal XML definition format is available, they can be defined in a hierarchy using the right constructor for ClasspathXmlApplicationContext, if that is desired. The client code just needs to ask for the right one by name with the
locator.useBeanFactory()
method call. If the contexts are marked as lazy-init="true", then effectively they will be loaded only on demand from client code. The only difference between SingletonBeanFactoryLocator and ContextSingletonBeanFactoryLocator is that the latter loads the outer bag as an application context, while the former loads it as a bean factory, using the default definition name of classpath*:beanRefFactory.xml. In practice, it makes little difference whether the outer bag is a bean factory or full-blown application context, so you may use either locator variant. It is also possible to provide an alias for a context or bean factory, so that one locator.useBeanFactory() can resolve to the same thing as another locator.useBeanFactory() with a different ID. For more information on how this works, and to get a better overall picture of these classes, please see the JavaDocs for ContextSingletonBeanFactoryLocator and SingletonBeanFactoryLocator.

Using a Shared Context from EJBs

We're now ready to find out how ContextSingletonBeanFactoryLocator may be used to access a shared context (which can also be the same shared context used by one or more web-apps) from EJBs. This turns out to be trivial. The Spring EJB base classes already use the BeanFactoryLocator interface to load the application context or bean factory to be used by the EJB. By default, they use an implementation called ContextJndiBeanFactoryLocator, which creates, an application context based on a classpath location specified via JNDI. All that is required to use ContextSingletonBeanFactoryLocator is to override the default BeanFactoryLocator. In this example from a Session Bean, this is being done by hooking into the standard Session EJB setSessionContext() method:
// see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
public void setSessionContext(SessionContext sessionContext) {
  super.setSessionContext(sessionContext);
  setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
  setBeanFactoryLocatorKey("serviceLayer-context");
}
First, because the Spring base classes already implement this method so they may store the EJB SessionContext, super.setSessionContext() is called to maintain that functionality. Then the BeanFactoryLocator is set as an instance returned from ContextSingletonBeanFactoryLocator.getInstance(). If we didn't want to rely on the default outer bag context name of classpath*: beanRefContext.xml, we could use ContextSingletonBeanFactoryLocator.getInstance(name) instead. Finally, for the BeanFactoryLocator.useBeanFactory() method that Spring will call to get the final application context or bean factory, a key value of serviceLayer-context is specified, as in the previous examples. This name would normally be set as a String constant somewhere, so all EJBs can use the same value easily. For a Message Driven Bean, the equivalent override of the default BeanFactoryLocator needs to be done in setMessageDrivenContext().

Session Limitation with Spring security

Sometimes it's useful to restrict a user a single session. This simplifies the logic needed to guarantee certain restrictions. For example, I always want a user to have a minimum of one valid email address. With two parallel sessions and two valid emails a user could delete one email in each session and I would need to verify consistency in the database. Restrictig to one session lets me implement the restriction in the business logic. However, the exact configuration was not obvious. After some experimentation the following seemed to work. First, you need some way of detectig when sessions expire. This is largely automatic as long as you register the following in web.xml:

  <!-- used to track session events (single user session) -->
  <listener>
    <listener-class>org.acegisecurity.ui.\
      session.HttpSessionEventPublisher</listener-class>
  </listener>
I have all my authentication-related xml in web-authentication.xml (and referenced via context-param in web.xml). It includes:
  <bean id="authenticationManager"
    class="org.acegisecurity.providers.ProviderManager">
    <property name="sessionController" ref="singleSession"/>
    <property name="providers">
      ...
    </property>
  </bean>

  <bean id="sessionRegistry"
    class="org.acegisecurity.concurrent.SessionRegistryImpl"/>

  <bean id="singleSession"
    class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl">
    <property name="maximumSessions" value="1"/>
    <property name="exceptionIfMaximumExceeded" value="true"/>
    <property name="sessionRegistry" ref="sessionRegistry"/>
  </bean>
Which is all that is needed (I suspect sessionRegistry is supplied by default anyway). The way it seems to work is as follows: - authenticationManager calls the appropriate provider - if that succeeds, it calls sessionController - sessionController applies the appropriate logic, using the information in sessionRegistry - sessionRegistry is correct because of the event system (which includes the listener you registered).
By Andrew

Did You Know: Spring Object Pooling

A subject came up recently in the Spring Framework Spring-User mailing list regarding object pooling. The original mis-conception (that was triggered by the wording in the Spring documentation) was that Spring doesn't support object pooling. This isn't neccessarily true depending on the release of Spring that you are using. (Note: When I refer to release, I am not referring to the version, but rather which distribution; e.g.: spring-core, spring-web, spring-mvc, etc.) The core bean factory code of Spring does not support object pooling; instead it supports singleton objects and what they refer to as prototype objects, which simply means that Spring returns a new object everytime you request the bean with that particular id from the bean factory. (Spring Documentation Reference) For example:

    <!-- This will always be the same bean (Singleton is default -->
    <bean id="bean1" /> 

    <!-- This will be a new bean every time you request it -->
    <bean id="bean2" singleton="false"/>
While the documentation is 100% correct in saying that bean factories don't support object pooling (or more specifically saying that they only support singletons and prototypes), there is no mention of the object pooling that is available if you are using a release of Spring that contains Spring AOP support. Spring supports what's called a Target Source. The description in the documentation is somewhat complicated if you aren't familiar with Spring AOP, but long story short, Spring uses a class called ProxyFactoryBean to create an AOP wrapper for a bean. By default, you set the 'target' to the bean you want to wrap with AOP interceptors (target means the bean that is being layered with some advice). However, a target source is an interim factory for a given object type that sits between the ProxyFactoryBean and your target bean. The term 'target source' sounds confusing, but is also self-explanatory. It is a source for the proxy bean to use when retrieving 'target' objects. Here is the XML snippet for Pooling Target Sources directly from the Spring documentation:
    <bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" 
        singleton="false">
        ... properties omitted
    </bean>

    <bean id="poolTargetSource" 
        class="org.springframework.aop.target.CommonsPoolTargetSource">
        <property name="targetBeanName"><value>businessObjectTarget</value></property>

        <property name="maxSize"><value>25</value></property>
    </bean>

    <bean id="businessObject" 
        class="org.springframework.aop.framework.ProxyFactoryBean"
    >
        <property name="targetSource"><ref local="poolTargetSource"/></property>

        <property name="interceptorNames"><value>myInterceptor</value></property>
    </bean>
By Lorimer

Struts2 + Spring + JUnit

Hopefully this entry serves as some search engine friendly documentation on how one might unit test Struts 2 actions configured using Spring, something I would think many, many people want to do. This used to be done using StrutsTestCase in the Struts 1.x days but Webwork/Struts provides enough flexibility in its architecture to accommodate unit testing fairly easily. I’m not going to go over how the Spring configuration is setup. I’m assuming you have a struts.xml file which has actions configured like this:

<struts>
 <package namespace="/site" extends="struts-default">
  <action name="deletePerson" class="personAction"
                  method="deletePerson">
   <result name="success">/WEB-INF/pages/person.jsp</result>
  </action>
 </package>
 ...
</struts>
You also might have an applicationContext.xml file where you might define your Spring beans like this.
<beans>
 <bean id="personAction"
  class="com.arsenalist.action.PersonAction"/>
 ...
</beans>
Then of course you also need to have an action which you want to test which might look something like:
public class PersonAction extend ActionSupport { 

  private int id; 

  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String deletePerson() {
    ....
    return SUCCESS;
  }
}
Remember than in Struts 2, an action is usually called before and after various other interceptors are invoked. Interceptor configuration is usually specified in the struts.xml file. At this point we need to cover three different methods of how you might want to call your actions. 1. Specify request parameters which are translated and mapped to the actions domain objects (id in the PersonAction class) and then execute the action while also executing all configured interceptors. 2. Instead of specifying request parameters, directly specify the values of the domain objects and then execute the action while also executing all configured interceptors. 3. Finally, you just might want to execute the action and not worry about executing the interceptors. Here you’ll specify the values of the actions domain objects and then execute the action. Depending on what you’re testing and what scenario you want to reproduce, you should pick the one that suits the case. There’s an example of all three cases below. The best way I find to test all your action classes is to have one base class which sets up the Struts 2 environment and then your action test classes can extend it. Here’s a class that could be used as one of those base classes. See the comments for a little more detail about whats going on. One point to note is that the class being extended here is junit.framework.TestCase and not org.apache.struts2.StrutsTestCase as one might expect. The reason for this is that StrutsTestCase is not really a well written class and does not provide enough flexibility in how we want the very core Dispatcher object to be created. Also, the interceptor example shown in the Struts documentation does not compile as there seems to have been some sort of API change. It’s been fixed in this example.
public class BaseStrutsTestCase extends TestCase {

 private Dispatcher dispatcher;
 protected ActionProxy proxy;
 protected MockServletContext servletContext;
 protected MockHttpServletRequest request;
 protected MockHttpServletResponse response;

 /**
  * Created action class based on namespace and name
  */
 protected T createAction(Class clazz, String namespace, String name)
   throws Exception {

  // create a proxy class which is just a wrapper around the action call.
  // The proxy is created by checking the namespace and name against the
  // struts.xml configuration
  proxy = dispatcher.getContainer().getInstance(ActionProxyFactory.class).
    createActionProxy(
    namespace, name, null, true, false);

  // set to true if you want to process Freemarker or JSP results
  proxy.setExecuteResult(false);
  // by default, don't pass in any request parameters
  proxy.getInvocation().getInvocationContext().
    setParameters(new HashMap());

  // set the actions context to the one which the proxy is using
  ServletActionContext.setContext(
    proxy.getInvocation().getInvocationContext());
  request = new MockHttpServletRequest();
  response = new MockHttpServletResponse();
  ServletActionContext.setRequest(request);
  ServletActionContext.setResponse(response);
  ServletActionContext.setServletContext(servletContext);
  return (T) proxy.getAction();
 }

 protected void setUp() throws Exception {
  String[] config = new String[] { "META-INF/applicationContext-aws.xml" };

  // Link the servlet context and the Spring context
  servletContext = new MockServletContext();
  XmlWebApplicationContext appContext = new XmlWebApplicationContext();
  appContext.setServletContext(servletContext);
  appContext.setConfigLocations(config);
  appContext.refresh();
  servletContext.setAttribute(WebApplicationContext.
    ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appContext);

  // Use spring as the object factory for Struts
  StrutsSpringObjectFactory ssf = new StrutsSpringObjectFactory(
    null, null, servletContext);
  ssf.setApplicationContext(appContext);
  //ssf.setServletContext(servletContext);
  StrutsSpringObjectFactory.setObjectFactory(ssf);

  // Dispatcher is the guy that actually handles all requests.  Pass in
  // an empty Map as the parameters but if you want to change stuff like
  // what config files to read, you need to specify them here
  // (see Dispatcher's source code)
  dispatcher = new Dispatcher(servletContext,
    new HashMap());
  dispatcher.init();
  Dispatcher.setInstance(dispatcher);
 }
}
By extending the above class for our action test classes we can easily simulate any of the three scenarios listed above. I’ve added three methods to PersonActionTest which illustrate how to test the above three cases: testInterceptorsBySettingRequestParameters, testInterceptorsBySettingDomainObjects() and testActionAndSkipInterceptors(), respectively.
public class PersonActionTest extends BaseStrutsTestCase { 

 /**
  * Invoke all interceptors and specify value of the action
  * class' domain objects directly.
  * @throws Exception Exception
  */
 public void testInterceptorsBySettingDomainObjects()
         throws Exception {
  PersonAction action = createAction(PersonAction.class,
                "/site", "deletePerson");
  pa.setId(123);
  String result = proxy.execute();
  assertEquals(result, "success");
 } 

 /**
  * Invoke all interceptors and specify value of action class'
  * domain objects through request parameters.
  * @throws Exception Exception
  */
 public void testInterceptorsBySettingRequestParameters()
                     throws Exception {
  createAction(PersonAction.class, "/site", "deletePerson");
  Map params = new HashMap();
  params.put("id", "123");
  proxy.getInvocation().getInvocationContext().setParameters(params);
  String result = proxy.execute();
  assertEquals(result, "success");
 } 

 /**
  * Skip interceptors and specify value of action class'
  * domain objects by setting them directly.
  * @throws Exception Exception
  */
 public void testActionAndSkipInterceptors() throws Exception {
  PersonAction action = createAction(PersonAction.class,
                  "/site", "deletePerson");
  action.setId(123);
  String result = action.deletePerson();
  assertEquals(result, "success");
 }
}
The source code for Dispatcher is probably a good thing to look at if you want to configure your actions more specifically. There are options to specify zero-configuration, alternate XML files and others. Ideally the StrutsTestCaseHelper should be doing a lot more than what it does right now (creating a badly configured Dispatcher) and should allow creation of custom dispatchers and object factories. That’s the reason why I’m not using StrutsTestCase since all that does is make a couple calls using StrutsTestCaseHelper. If you want to test your validation, its pretty easy. Here’s a snippet of code that might do that:
 public void testValidation() throws Exception {
  SomeAction action = createAction(SomeAction.class,
                  "/site", "someAction");
  // lets forget to set a required field: action.setId(123);
  String result = proxy.invoke();
  assertEquals(result, "input");
  assertTrue("Must have one field error",
                  action.getFieldErrors().size() == 1);
 }
This example uses Struts 2.0.8 and Spring 2.0.5.