SEC-2151: Method Security support for binding parameter names with annotations #2376

Closed
spring-issuemaster opened this Issue Mar 21, 2013 · 4 comments

2 participants

@spring-issuemaster

Rob Winch (Migrated from SEC-2151) said:

Currently Spring Security does not allow binding parameters on Spring Data Repositories. Right now there are two problems:

  • LocalVariableTableParameterNameDiscoverer cannot locate the variable name because the interface does not contain debug information about the variable name (interfaces cannot contain debug info)
  • AopProxyUtils.ultimateTargetClass(targetObject) resolves the target class to be SimpleJpaRepository which does not have a method with the correct signature (it is a generic method)

One option to resolve this is to rely on the parameter names defined by SimpleJpaRepository and update the logic of MethodSecurityEvaluationContext so that it can resolve the variable name from there.

Another option is to use an annotation on the interface to explicitly define the parameter name. We could use @Param from Spring Data or use our own generic annotation.

@spring-issuemaster

Rob Winch said:

Attached is a custom ParameterNameDiscoverer that can be used to work with Spring Data. For example, if you are using Spring Security Java Configuration you could use the following config:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler expressionHandler() {
        DefaultMethodSecurityExpressionHandler result = new DefaultMethodSecurityExpressionHandler();
        result.setParameterNameDiscoverer(new ParameterAnnotationsNameDiscoverer(Param.class.getName()));
        return result;
    }

    @Autowired
    public void registerAuthentication(AuthenticationManagerBuilder auth)
            throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
}

You can then use the parameter names within SpEL as long as you annotate the parameter names like this:


public interface MessageRepository extends CrudRepository<Message, Long> {

    @PreAuthorize("#id == principal")
    Message findOne(@Param("id") Long id);
}

Note this annotation assumes the principal is a Long which is not typical of applications. You may need to adjust your SpEL expression.

@spring-issuemaster

Rob Winch said:

Complete example using the ParaemterAnnotationsNameDiscoverer.java

@spring-issuemaster

Ashley Banks said:

Hi Rob,

I've tried to post a comment on the original thread, but my permissions seem a bit awry...

Anyway - yes this fixed it, so thank you so much...!

The only issue I encountered was getting the xml config nailed

I was using an annotation of the form:

    @PreAuthorize("hasPermission(#testRecord, 'write')")
    public <S extends TestRecord> S save(@Param("testRecord") S testRecord);

And the following config seemed to do the trick:

    <b:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <b:property name="permissionEvaluator" ref="permissionEvaluator"/>
        <b:property name="permissionCacheOptimizer">
            <b:bean class="org.springframework.security.acls.AclPermissionCacheOptimizer">
                <b:constructor-arg ref="aclService"/>
            </b:bean>
        </b:property>
        <b:property name="parameterNameDiscoverer">
            <b:bean class="uk.co.twofiveone.app.security.ParameterAnnotationsNameDiscoverer">
                <b:constructor-arg value="org.springframework.data.repository.query.Param" />
            </b:bean>
        </b:property>
    </b:bean>

... the key being the value of the constructor arg for the ParameterAnnotationsNameDiscoverer

Anyway, thought I'd post my config in case anyone else finds it helpful...

@spring-issuemaster

Rob Winch said:

Method Security support for binding parameter names with annotations was added with default support (i.e. no special configuration) for Spring Security's new \@P and Spring Data's \@Param. Once the build completes you can refer to the reference for details. From the updated reference:

If Spring Security's @P annotation is present on a single argument to the method, the value will be used. This is useful for interfaces which do not contain debug symbols for the parameter names. For example:

import org.springframework.security.access.method.P;

...

@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);

Behind the scenes this use implemented using AnnotationParameterNameDiscoverer which can be customized to support the value attribute of any specified annotation.

If Spring Data's @Param annotation is present on at least one parameter for the method, the value will be used. For example:

import org.springframework.data.repository.query.Param;

...

@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);

Behind the scenes this use implemented using AnnotationParameterNameDiscoverer which can be customized to support the value attribute of any specified annotation.

@spring-issuemaster spring-issuemaster added this to the 3.2.0.RC2 milestone Feb 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment