27 July 2009

Don't translate this ! Security with Spring, my notebook

In my previous post titled "SpringFramework/BeanFactory My notebook" I showed how to use the Spring Framework to manage a set of java beans with a xml file and a BeanFactory.

Now, let's add a drop of Security in our model.

Let's say you're working for a big paranoid pharma company where the user "lindenb" is not allowed to use the class spring.Translator with the standard genetic code. A naive approach is to extends spring.Translate

package spring;

public class TranslateSecurity extends Translate{
@Override
public String translate(CharSequence sequence)
{
if("lindenb".equals(System.getProperty("user.name")))
{
Translate geneticCode= Translate.class.cast(jp.getTarget());
if(geneticCode.getName().equals("Standard Code"))
{
throw new SecurityException(
"User lindenb is not allowed to use the genetic code \""+geneticCode.getName()+"\" !"
);
}
}
return super.translate(sequence);
}
}
But this strategy is not maintainable because you'll also have to copy this code in all the other classes implementing spring.Translate such as spring.NoProlineTranslate.

Here comes The Aspect Oriented Programming (AOP). You can think of AOP as a dynamic decorator design pattern. The decorator pattern allows additional behavior to be added to an existing class by wrapping the original class and duplicating its interface and then delegating to the original.

Let's create an Aspect:
package spring;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class SecurityAspect
{
@Before("execution(* spring.Translator.*(..))")
public void checkSecurity(JoinPoint jp)
{
if("lindenb".equals(System.getProperty("user.name")))
{
Translate geneticCode= Translate.class.cast(jp.getTarget());
if(geneticCode.getName().equals("Standard Code"))
{
throw new SecurityException(
"User lindenb is not allowed to use the genetic code \""+geneticCode.getName()+"\" !"
);
}
}
}
}
This Aspect calls spring.SecurityAspect.checkSecurity() each time before calling any method of spring.Translator. This aspect is plugged our original code just by adding a few nodes in the original beans.xml file.
<beans>
(...)
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="aspect01" class="spring.SecurityAspect"/>
(...)
</beans>


Compile & Execute as user "lindenb"

mkdir -p build
javac -d build -cp ${SPRING}/dist/spring.jar:${SPRING}/lib/aspectj/aspectjrt.jar -sourcepath src src/spring/*.java
java -cp build:${SPRING}/dist/spring.jar:${SPRING}/lib/jakarta-commons/commons-logging.jar:${SPRING}/lib/aspectj/aspectjrt.jar:${SPRING}/lib/aspectj/aspectjweaver.jar:${SPRING}/lib/cglib/cglib-nodep-2.1_3.jar spring.SprintTest01

Jul 27, 2009 3:55:38 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5e3974: display name [org.springframework.context.support.ClassPathXmlApplicationContext@5e3974]; startup date [Mon Jul 27 15:55:38 CEST 2009]; root of context hierarchy
Jul 27, 2009 3:55:38 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [beans.xml]
Jul 27, 2009 3:55:38 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@5e3974]: org.springframework.beans.factory.support.DefaultListableBeanFactory@c44b88
Jul 27, 2009 3:55:38 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c44b88: defining beans [gencode1,gencode2,gencode3,listOfGenCodes,org.springframework.aop.config.internalAutoProxyCreator,aspect01]; root of factory hierarchy
java.lang.SecurityException: User lindenb is not allowed to use the genetic code "Standard Code" !
at spring.SecurityAspect.checkSecurity(SecurityAspect.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:627)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:609)
at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:39)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:49)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:635)
at spring.Translate$$EnhancerByCGLIB$$75c8a9e3.getName(<generated>)
at spring.SprintTest01.main(SprintTest01.java:20)


This security layer was added in our model without modifying the original classes.
This security layer was added in our model without modifying the original classes.
This security layer was added in our model without modifying the original classes.
This security layer was added in our model without modifying the original classes
This security layer was added in our model without modifying the original classes


That's it.
Pierre

No comments: