spring-projects / spring-security

Spring Security

Home Page:http://spring.io/projects/spring-security

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support customized `TargetVisitor` implement for authorization proxying

la3rence opened this issue · comments

Expected Behavior

When using @AuthorizeReturnObject annotation to mask object fields, AuthorizationAdvisorProxyFactory now working with some built-in TargetVisitor implements like ClassVisitor, ContainerTypeVisitor. I want the AuthorizationAdvisorProxyFactory can provide some methods to register some TargetVisitors that user implements for some returned object that not in Java native types.

Current Behavior

Creating a proxyFactory by withDefaults() method.

AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();
Foo foo = new Foo();
foo.bar(); // passes
Foo securedFoo = proxyFactory.proxy(foo);
securedFoo.bar(); // access denied!

withDefaults() using these default TargetVistor implements:

private static final TargetVisitor DEFAULT_VISITOR = isReactivePresent
	? TargetVisitor.of(new ClassVisitor(), new ReactiveTypeVisitor(), new ContainerTypeVisitor())
	: TargetVisitor.of(new ClassVisitor(), new ContainerTypeVisitor());

Context

For example, some ORM provide an Interface to do pagination, the DAO interface directly returning type with class like Page<T> rather than List<T>. This Page<T> looks like:

@Data
public class Page<T> {
    private long size;
    private long offset;
    private long total; 
    private List<T> records;
}

So if I want to apply @AuthorizeReturnObject on methods like Page<T> method(), that would not work. I tried to create an implement of TargetVisitor for this type but there seems no where to add it into DEFAULT_VISITOR.

This is what you need?

  @Bean
  Customizer<AuthorizationAdvisorProxyFactory> customizer() {
      return factory -> factory.setTargetVisitor(AuthorizationAdvisorProxyFactory.TargetVisitor.of(new CustomeTargetVisitor(), AuthorizationAdvisorProxyFactory.TargetVisitor.defaults()));
  }

  record CustomeTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor {

      @Override
      public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
          if (target instanceof Page<?> page) {
              return proxyPage(proxyFactory, page);
          }
          return null;
      }

      @SuppressWarnings("unchecked")
      private <T> Page<T> proxyPage(AuthorizationProxyFactory proxyFactory, Page<T> page) {
          page.setRecords((List<T>) proxyFactory.proxy(page.getRecords()));
          return page;
      }

  }

This is what you need?

  @Bean
  Customizer<AuthorizationAdvisorProxyFactory> customizer() {
      return factory -> factory.setTargetVisitor(AuthorizationAdvisorProxyFactory.TargetVisitor.of(new CustomeTargetVisitor(), AuthorizationAdvisorProxyFactory.TargetVisitor.defaults()));
  }

  record CustomeTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor {

      @Override
      public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
          if (target instanceof Page<?> page) {
              return proxyPage(proxyFactory, page);
          }
          return null;
      }

      @SuppressWarnings("unchecked")
      private <T> Page<T> proxyPage(AuthorizationProxyFactory proxyFactory, Page<T> page) {
          page.setRecords((List<T>) proxyFactory.proxy(page.getRecords()));
          return page;
      }

  }

Yes, exactly! I didn't notice the method AuthorizationAdvisorProxyFactory.TargetVisitor.defaults() is already available.
Thanks!