Announcement Announcement Module
Collapse
No announcement yet.
global-method-security on a roo app Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • global-method-security on a roo app

    I'm having a heckuva time trying to use @PreAuthorize annotations, which seem to be a nice way to do some basic object-level security in a Roo-generated app. To start, I'm just trying to get global-method-security to work. So this may be a spring-security issue, not a Roo issue, but it seems to have something to do with the Roo-generated app's architecture (in particular, it seems that "global-method-security" aspects have problems when @configurable is also being used - but I can't figure out why). Here's what does not work for me:

    I set up a simple project using Roo (trunk build #345), then added to applicationContext-security.xml:

    Code:
        <global-method-security pre-post-annotations="enabled" jsr250-annotations="enabled">
            <protect-pointcut expression="execution(* com.mytest.domain.Task.findTask(..))" access="ROLE_ADMIN"/>
        </global-method-security>
    This should prevent me from using the "findTask" call, right? (I'm not an authenticated user here, I'm just using the generated Roo scaffolding which isn't yet protected.) Now, when I start Tomcat, I get a bunch of these:

    Code:
    [WebappClassLoader@1b446d1] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
    [WebappClassLoader@1b446d1] error aspect 'com.mytest.web.TaskController_Roo_Controller' woven into 'com.mytest.web.TaskController' must be defined to the weaver (placed on the aspectpath, or defined in an aop.xml file if using LTW).
    ..
    I added 'javaagent:..aspejctjweaver.jar' to Tomcat's vm args but I still get the errors warnings above. Then when I load http://localhost:8080/mytest/task, I get:
    Code:
    Failed to invoke handler method [public java.lang.String com.mytest.web.TaskController.list(java.lang.Integer,java.lang.Integer,org.springframework.ui.ModelMap)]; nested exception is java.lang.IllegalStateException: Post-processor tried to replace bean instance of type [com.mytest.domain.Task] with (proxy) object of type [$Proxy36] - not supported for aspect-configured classes!
    I added an META-INF/aop.xml:

    Code:
    <?xml version="1.0"?>
     	<aspectj>
     	 <weaver options="-Xset:weaveJavaPackages=true,weaveJavaxPackages=true "/>
     	   <aspects>
     	       <aspect name="com.mytest.web.PersonController_Roo_Controller" />
     	       <aspect name="com.mytest.web.TaskController_Roo_Controller" />
     	       <aspect name="com.mytest.domain.Person_Roo_JavaBean" />
     	       <aspect name="com.mytest.domain.Person_Roo_ToString" />
     	       <aspect name="com.mytest.domain.Person_Roo_Entity" />
     	       <aspect name="com.mytest.domain.Person_Roo_Configurable" />
     	       <aspect name="com.mytest.domain.Task_Roo_JavaBean" />
     	       <aspect name="com.mytest.domain.Task_Roo_ToString" />
     	       <aspect name="com.mytest.domain.Task_Roo_Configurable" />
     	       <aspect name="com.mytest.domain.Task_Roo_Entity" />
     	   </aspects>
     	
    </aspectj>
    The warning messages went away since I added all of the Roo aspects to my aop.xml, but I still get the "IllegalStateException" error.


    For reference, my roo script is:

    Code:
    project --topLevelPackage com.mytest
    
    persistence setup --database HYPERSONIC_IN_MEMORY --provider HIBERNATE
    
    // Task
    entity --name ~.domain.Task
    field string --fieldName description
    field date --fieldName due --type java.util.Date
    
    // Person
    entity --name ~.domain.Person
    field string name
    
    
    // relationships
    field set --class ~.domain.Person --element ~.domain.Task --fieldName tasks --mappedBy person
    field reference --fieldName person --type ~.domain.Person --class ~.domain.Task --notNull
    
    controller all --package ~.web
    security setup

  • #2
    Roo does compile-time weaving. It does not do load-time weaving, as the inter-type declarations need to be compile-time woven. Did you try it with compile-time weaving, which is the default setup you'd get out-of-the-box?

    Comment


    • #3
      Yes, I only tried adding run-time weaving to get around the exceptions in the first place. I started getting the exception with a very straight-forward project.

      Any ideas would be appreciated, though I'm starting to realize this may fall out of the Roo scope.

      Here's the full stacktrace (with no run-time weavaing enabled):

      Exception in thread "main" java.lang.IllegalStateException: Post-processor tried to replace bean instance of type [com.mytest.domain.Task] with (proxy) object of type [$Proxy30] - not supported for aspect-configured classes!
      at org.springframework.beans.factory.wiring.BeanConfi gurerSupport.checkExposedObject(BeanConfigurerSupp ort.java:170)
      at org.springframework.beans.factory.wiring.BeanConfi gurerSupport.configureBean(BeanConfigurerSupport.j ava:142)
      at org.springframework.beans.factory.aspectj.Annotati onBeanConfigurerAspect.configureBean(AnnotationBea nConfigurerAspect.aj:59)
      at org.springframework.beans.factory.aspectj.Abstract DependencyInjectionAspect.ajc$afterReturning$org_s pringframework_beans_factory_aspectj_AbstractDepen dencyInjectionAspect$2$1ea6722c(AbstractDependency InjectionAspect.aj:89)
      at com.mytest.domain.Task.<init>(Task.java:19)
      at com.mytest.Main.main(Main.java:19)
      Here's how I replicated it this time (not using STS or specifying a javaagent at all, just using a Main class and maven):
      1. Ran the script above in a new "mytest" project directory
      2. Added Main.java (below) and executed it successfully (using mvn -e exec:java -Dexec.mainClass="com.mytest.Main")
      3. Added "global-method-security" element to META-INF/spring/applicationSecurity-context.xml
      4. Added dependencies for org.aspectj.weaver and javax.annotation to pom.xml
      5. Executed Main again and got the IllegalStateException stacktrace above

      Main.java:
      Code:
      package com.mytest;
      
      import java.util.Date;
      
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      import com.mytest.domain.Person;
      import com.mytest.domain.Task;
      
      public class Main {
      	public static void main(String[] args)
      	{
              System.out.println("starting main");
      		ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/*.xml");
      		Person person = new Person();
      		person.setName("Jimmy");
      		person.persist();
      		Task task = new Task();
              task.setDescription("my task");
              task.setDue(new Date());
              task.persist();
              Task task2 = Task.findTask(1L);
              System.out.println("task: "+task2);
              System.out.println("done");
      	}
      }

      Comment


      • #4
        I believe this is because <global-method-security> is internally setting up Spring Security's MethodSecurityInterceptor and that in turn is trying to use Spring AOP to proxy the com.mytest.domain.Task.findTask(..) method. This won't work for two reasons:

        1. Task is an entity class and therefore has no interface, so Spring AOP will not work with its normal dynamic proxying approach.

        2. Task is already woven with AspectJ, so Spring AOP considers it incompatible.

        The correct usage would be to use Spring Security's AspectJSecurityInterceptor instead of the Spring AOP-requiring MethodSecurityInterceptor.

        I'll ask Luke Taylor (of Spring Security) whether he can offer any suggestions on the easiest way of configuring Spring Security to achieve the above.

        Comment


        • #5
          Mike: did you find a solution to this issue... we too are wanting to use this same mix but have stalled because of the same issue...

          I would rather NOT have to move away from using the annotations approach of PreAuthorize etc..

          cheers

          Comment


          • #6
            Originally posted by kermiedefrog View Post
            Mike: did you find a solution to this issue... we too are wanting to use this same mix but have stalled because of the same issue...
            No, sorry.

            Ben??? Did you get any tips from Luke on how to configure pre- and post- authorization (perhaps via <global-method-security>) in a roo app?

            I think it would have something to do with using AspectJSecurityInterceptor as Ben suggested (http://static.springsource.org/sprin...s.html#aspectj) and using PrePostAnnotationSecurityMetadataSource as your MetadataSource.

            Comment


            • #7
              With Spring Security V3 I can get @Secured working but only in the Entity itself, ie NOT in the Roo generated aspects... The aspects sample in the Security Source does seem to work as a configuration. However, only within the Entity, which doesn't work for us as we want to put the PreAuth/Secured on the Roo generated finders et al directly...

              Spring Security fails within the generated entity call as a result of the Roo_Entity aspect calling org.aspectj.runtime.reflect.makeJP with a null target... though this call succeeds Security fails at another point as it is checking this target value and blowing up because it is null; within AbstractSecurityInterceptor.beforeInvocation...

              Comment


              • #8
                Hmmm...I looked at this a little myself today. Is that because the methods you are trying to secure are static (and static method matches have null targets in aspectj)?

                And if that's causing AbstractSecurityInterceptor to blow up, is that a Spring Security bug? (I think it means that you can't use a security pointcut that matches any static methods....)

                Would it be fair to open a Roo ticket on getting some kind of support for security annotations (@Secured, @PreAuthorize, e.g.) since this has something to do with the generated Entities being incompatible with <global-method-security>?

                Comment


                • #9
                  Actually you are right there - they are on the Roo_entity static finder methods... I hacked the security code in one place to get the class from the signature rather than the target and @Secured works fine.. When I tried with PrePostAuth I now get a failure within the PrePostAuthVoter as it is coded up for AOP only... Might hack that tomorrow and see what I come up with...

                  Apparently SSV3.1 might be getting an aspectj version of global-method-securty, which will solve some of my issues but unfortunately not in time for our next release...

                  Comment


                  • #10
                    You're right, I missed that. Nothing for Roo to do but wait

                    According to http://jira.springframework.org/browse/SEC-1232, the aspectj-compatible beans are already in the trunk (but not integrated into namespace config).

                    Comment


                    • #11
                      there are some and with me we hack I could get the @secured working with roo - ie no aop... However cant use the PreInvocationAuthorizationAdviceVoter as that is still firmly spring aop and fails - it casts to a MethodInvocation... there are about 3+ classes around this area to look at some time today...

                      Comment


                      • #12
                        I have managed to get a test roo entity fully PreAuthorize'd using aspectj... I have only tested the hasRole stuff out so far and not the other EL combos... will do this soon and post the work-arounds once I have completed that and tried it within our main application...

                        Comment


                        • #13
                          Here is an example aspectj config file that does PreAuthorize and PostFilter within static and non-static methods within a generated Roo_entity aspect.. Obviously, it work in the entity itself...

                          There are some code changes that will also need to be made, as you will see by this config file.. I will list these as well.. they are snippets out of the full classed but should be easy to follow.

                          spring config file:
                          Code:
                          <?xml version="1.0" encoding="UTF-8"?>
                          <beans xmlns="http://www.springframework.org/schema/beans"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
                                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
                          
                              <bean id="aspectJSecurityInterceptor"
                                    class="org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor">
                                  <property name="authenticationManager" ref="authenticationManager"/>
                                  <property name="accessDecisionManager" ref="accessDecisionManager"/>
                                  <property name="securityMetadataSource" ref="PrePostAnnotationSecurityMetadataSource"/>
                                  <property name="afterInvocationManager" ref="PostInvocationManager"/>
                              </bean>
                          
                              <bean id="PostInvocationManager"
                                    class="org.springframework.security.access.intercept.AfterInvocationProviderManager">
                                  <property name="providers">
                                      <util:list>
                                          <ref bean="postInvocationProvider"/>               
                                      </util:list>
                                  </property>
                              </bean>
                          
                          
                              <bean id="PrePostAnnotationSecurityMetadataSource"
                                    class="org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource">
                                  <constructor-arg ref="ExpressionBasedAnnotationAttributeFactory"/>
                              </bean>
                          
                              <bean id="ExpressionBasedAnnotationAttributeFactory"
                                    class="org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory">
                                  <constructor-arg ref="DefaultMethodSecurityExpressionHandler"/>
                              </bean>
                          
                              <bean id="DefaultMethodSecurityExpressionHandler"
                                    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"/>
                          
                              <bean id="postInvocationProvider" class="org.springframework.security.access.prepost.AJPostInvocationAdviceProvider">
                                  <constructor-arg ref="postAdvice"/>
                              </bean>
                              <bean id="postAdvice"
                                    class="org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice">
                                  <constructor-arg ref="DefaultMethodSecurityExpressionHandler"/>
                              </bean>
                          
                          
                              <bean id="authenticationManager"
                                    class="org.springframework.security.authentication.ProviderManager">
                                  <property name="providers">
                                      <bean
                                              class="org.springframework.security.authentication.TestingAuthenticationProvider"/>
                                  </property>
                              </bean>
                          
                              <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
                                  <property name="decisionVoters">
                                      <list>
                                          <bean class="org.springframework.security.access.prepost.AJPreInvocationAuthorizationAdviceVoter">
                                              <constructor-arg ref="PreInvocationAuthorizationAdvice"/>
                                          </bean>
                                      </list>
                                  </property>
                              </bean>
                          
                              <bean id="PreInvocationAuthorizationAdvice"
                                    class="org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice">
                          
                              </bean>
                          
                              <bean class="custom.aspects.PrePostAuthoriseFilterAnnotation"
                                    factory-method="aspectOf">
                                  <property name="securityInterceptor" ref="aspectJSecurityInterceptor"/>
                              </bean>
                          
                              <bean class="sample.aspectj.Service"/>
                          
                              <bean class="sample.aspectj.SecuredService"/>
                              <bean class="template.TemplateDescription"/>
                          
                          </beans>
                          custom.aspect.PrePostAuthoriseFilterAnnotation:
                          In the spring security sample or code I have just change the following line within its aspect - there is only one so wont be hard to find. This is also not quite complete for the PostFilter stuff
                          Code:
                              private pointcut executionOfAnyPublicMethodInAtSecuredType() :
                                  execution(public * ((@PreAuthorize *)+).*(..)) && @this(PreAuthorize);
                          //        execution(public * ((@Secured *)+).*(..)) && @this(Secured);
                          
                              private pointcut executionOfSecuredMethod() :
                                  execution(* *(..)) && @annotation(PreAuthorize);
                          AJPreInvocationAuthorizationAdviceVoter - note messy code :-):
                          Code:
                          package org.springframework.security.access.prepost;
                          public class AJPreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter {
                              public boolean supports(Class<?> clazz)
                              {
                                  return clazz.isAssignableFrom(org.aspectj.lang.JoinPoint.class);
                              }
                          
                              public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
                          
                                  // Find prefilter and preauth (or combined) attributes
                                  // if both null, abstain
                                  // else call advice with them
                          
                                  PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
                          
                                  if (preAttr == null) {
                                      // No expression based metadata, so abstain
                                      return ACCESS_ABSTAIN;
                                  }
                          
                                  JoinPoint jp = (JoinPoint)object;
                                  Object[] args = jp.getArgs();
                          
                                  Object target = jp.getTarget();
                                  Method method = null;
                                  if(target != null)
                                  {
                                      String name = jp.getSignature().getName();
                          
                                      if(args.length == 0)
                                      {
                                          try
                                          {
                                              method = target.getClass().getMethod(name, null);
                                          }
                                          catch (NoSuchMethodException e)
                                          {
                                              e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                                              // todo: just ignore for now, but fix
                                          }
                                      }
                                      else
                                      {
                                          Class[] types = new Class[args.length];
                                          for (int i = 0; i < args.length; i++)
                                          {
                                              Object arg = args[i];
                                              types[i] = arg.getClass();
                                          }
                          //                try
                          //                {
                          //                    method = target.getClass().getMethod(name, types);
                          //                }
                          //                catch (NoSuchMethodException e)
                          //                {
                          //                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                          //                    // todo: just ignore for now, but fix
                          //                }
                                      }
                          
                          
                                  }
                          
                                  SimpleMethodInvocation invo = new SimpleMethodInvocation(target, method, args);
                                  boolean allowed = preAdvice.before(authentication, invo, preAttr);
                          //        boolean allowed = preAdvice.before(authentication, (MethodInvocation)object, preAttr);
                          
                                  return allowed ? ACCESS_GRANTED : ACCESS_DENIED;
                              }
                          
                          }
                          AJPreInvocationAuthorizationAdvice - note messy code :-):
                          Code:
                          package org.springframework.security.access.prepost;
                          public class AJPreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter {
                              public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
                          
                                  // Find prefilter and preauth (or combined) attributes
                                  // if both null, abstain
                                  // else call advice with them
                          
                                  PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
                          
                                  if (preAttr == null) {
                                      // No expression based metadata, so abstain
                                      return ACCESS_ABSTAIN;
                                  }
                          
                                  JoinPoint jp = (JoinPoint)object;
                                  Object[] args = jp.getArgs();
                          
                                  Object target = jp.getTarget();
                                  Method method = null;
                                  if(target != null)
                                  {
                                      String name = jp.getSignature().getName();
                          
                                      if(args.length == 0)
                                      {
                                          try
                                          {
                                              method = target.getClass().getMethod(name, null);
                                          }
                                          catch (NoSuchMethodException e)
                                          {
                                              e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                                              // todo: just ignore for now, but fix
                                          }
                                      }
                                      else
                                      {
                                          Class[] types = new Class[args.length];
                                          for (int i = 0; i < args.length; i++)
                                          {
                                              Object arg = args[i];
                                              types[i] = arg.getClass();
                                          }
                          //                try
                          //                {
                          //                    method = target.getClass().getMethod(name, types);
                          //                }
                          //                catch (NoSuchMethodException e)
                          //                {
                          //                    e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                          //                    // todo: just ignore for now, but fix
                          //                }
                                      }
                          
                          
                                  }
                          
                                  SimpleMethodInvocation invo = new SimpleMethodInvocation(target, method, args);
                                  boolean allowed = preAdvice.before(authentication, invo, preAttr);
                          //        boolean allowed = preAdvice.before(authentication, (MethodInvocation)object, preAttr);
                          
                                  return allowed ? ACCESS_GRANTED : ACCESS_DENIED;
                              }
                          Finally

                          See here http://jira.springframework.org/browse/SEC-1295 for one last file change...

                          This should be all, any issues just post a question and I will try and help..

                          Comment


                          • #14
                            Thanks very much for sharing this. I am sure many Roo users interested in the subtleties of compile time weaving of static and non-static methods with Spring Roo and Spring Security will be very interested in the above. Thanks again!

                            Comment


                            • #15
                              @kermiedefrog;

                              Can you give us a hint on the includes for each file? So far I have:

                              import org.aspectj.lang.JoinPoint;
                              import org.springframework.security.access.AccessDecision Voter;
                              import org.springframework.security.core.Authentication;
                              import org.springframework.security.util.SimpleMethodInvo cation;

                              But the type "Method" could be several things - I assume (org.aspectj.apache.bcel.classfile.Method?).

                              Comment

                              Working...
                              X