Spring Security
  1. Spring Security
  2. SEC-2151

Method Security support for binding parameter names with annotations

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Complete
    • Affects Version/s: 3.1.3
    • Fix Version/s: 3.2.0.RC2
    • Component/s: Core
    • Labels:
      None

      Description

      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.

        Activity

        Hide
        Rob Winch added a comment - - edited

        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.

        Show
        Rob Winch added a comment - - edited 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.
        Hide
        Rob Winch added a comment -

        Complete example using the ParaemterAnnotationsNameDiscoverer.java

        Show
        Rob Winch added a comment - Complete example using the ParaemterAnnotationsNameDiscoverer.java
        Hide
        Ashley Banks added a comment -

        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...

        Show
        Ashley Banks added a comment - 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...
        Hide
        Rob Winch added a comment -

        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.

        Show
        Rob Winch added a comment - 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.

          People

          • Assignee:
            Rob Winch
            Reporter:
            Rob Winch
          • Votes:
            2 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: