5

I have an example class to test @PreAuthorize annotations, which looks more or less like this one:

class BankService {

    @PreAuthorize("hasCustomRole('ROLE_CUSTOM') or hasRole('ROLE_EXAMPLE')")
    Double getAccountBalance(Integer accountNumber) {
        return 1234;
    }

    @PreAuthorize("#accountNumber > 400")
    int getValue(Integer accountNumber) {
        return 1234;
    }
}

You can notice hasCustomRole(String expression) in the @PreAuthorize annotation, which I'm adding in:

public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {

    public CustomSecurityExpressionRoot(Authentication auth) {
        super(auth);
    }

    public boolean hasCustomRole(String expression) {
       return /* some magic */;
    }
}

Also, I'm extending DefaultMethodSecurityExpressionHandler in the following way:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    public CustomMethodSecurityExpressionHandler() {
        super();
    }

    @Override
    public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
        StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
        ctx.setRootObject(new CustomSecurityExpressionRoot(auth));
        return ctx;
    }
}

In the end, everything is wrapped in resources.groovy:

beans = {
  /* ... some stuff ... */

  xmlns security:'http://www.springframework.org/schema/security'

  security.'global-method-security'('pre-post-annotations': 'enabled') {
    security.'expression-handler'(ref: 'expressionHandler')
  }

  expressionHandler(my.package.plugin.security.expression.CustomMethodSecurityExpressionHandler)
}

Now, if I remove the security part from resources.groovy, I naturally lose the ability to use the hasCustomRole() method, but the following works:

assert bankService.getValue(500) == 1234

But if I inject my own implementation, the previous statement causes:

Access is denied
org.springframework.security.access.AccessDeniedException: Access is denied

After further investigation I found this:

prepost.PrePostAnnotationSecurityMetadataSource Looking for Pre/Post annotations for method 'getValue' on target class 'class my.package.plugin.security.test.BankService'
prepost.PrePostAnnotationSecurityMetadataSource @org.springframework.security.access.prepost.PreAuthorize(value=#accountNumber > 400) found on specific method: public int my.package.plugin.security.test.BankService.getValue(java.lang.Integer)
method.DelegatingMethodSecurityMetadataSource Adding security method [CacheKey[my.package.plugin.security.test.BankService; public int my.package.plugin.security.test.BankService.getValue(java.lang.Integer)]] with attributes [[authorize: '#accountNumber > 400', filter: 'null', filterTarget: 'null']]
aopalliance.MethodSecurityInterceptor Secure object: ReflectiveMethodInvocation: public int my.package.plugin.security.test.BankService.getValue(java.lang.Integer); target is of class [my.package.plugin.security.test.BankService$$EnhancerByCGLIB$$c590f9ac]; Attributes: [[authorize: '#accountNumber > 400', filter: 'null', filterTarget: 'null']]
aopalliance.MethodSecurityInterceptor Previously Authenticated: org.springframework.security.authentication.TestingAuthenticationToken@b35bafc3: Principal: test; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_TELLER
method.MethodSecurityEvaluationContext Unable to resolve method parameter names for method: public final int my.package.plugin.security.test.BankService$$EnhancerByCGLIB$$c590f9ac.getValue(java.lang.Integer). Debug symbol information is required if you are using parameter names in expressions.

The interesting part is Debug symbol information is required if you are using parameter names in expressions., which suggests that classes are compiled without debug information about variable names. But everything works fine if I don't inject my own bean.

What could be the reason for the missing debugging info, and how to fix it?

It's a Grails plugin, developed for Grails 2.0.4, using spring-security-core plugin at version 1.2.7.3, spring-security-acl plugin at version 1.1, and Spring Security 3.0.7.RELEASE.

EDIT:

To make the issue more interesting, this is what I discovered later: the "missing" debug information is actually there, if you look into .class files with javap. So classes are compiled correctly, but Spring complains anyway...

konradstrack
  • 2,852
  • 2
  • 18
  • 23

1 Answers1

0

I fixed the issue, however, I'm not exactly sure why the exceptions and messages in logs I had been getting were so far from the problem.

I did one mistake assuming that grails-app/conf/spring/resources.groovy can be used in similar way as in applications built with Grails. And although the documentation doesn't explicitly say that beans configured in resources.groovy won't work in this case, it states that resources.groovy (among some other files) will be by default excluded from packaging.

It doesn't explain the strange behavior while running tests, but it's not a good place for this kind of configuration.

After moving the Spring Security configuration from resources.groovy into the plugin descriptor, in the following way:

class MyOwnGrailsPlugin {

  /* ... some stuff ... */

  def doWithSpring = {
    /* ... some spring stuff ... */

    xmlns security:'http://www.springframework.org/schema/security'

    security.'global-method-security'('pre-post-annotations': 'enabled') {
      security.'expression-handler'(ref: 'expressionHandler')
    }

    expressionHandler(my.package.plugin.security.expression.CustomMethodSecurityExpressionHandler)
  }
}

everything works fine and the test passes.

konradstrack
  • 2,852
  • 2
  • 18
  • 23