When using ACL, we were able to filter out parts of the returned object in a method invocation based on permissions. Now on spring-security 5.8.x there's a bunch of deprecated APIs.

I have a MethodSecurityInterceptor which was delegating to AclEntryAfterInvocationCollectionFilteringProvider the decision to filter out parts of the response:

        AfterInvocationProviderManager afterInvocationProviderManager = new AfterInvocationProviderManager();
        afterInvocationProviderManager.setProviders(List.of(new MyAclEntryAfterInvocationCollectionFilteringProvider()));

        MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
        interceptor.setAuthenticationManager(authenticationManager);
        interceptor.setAccessDecisionManager(accessDecisionManager);
        interceptor.setAfterInvocationManager(afterInvocationManager);
        interceptor.setSecurityMetadataSource(new MapBasedMethodSecurityMetadataSource(Map.of("com.packagea.MyClass.readAll*", new SecurityConfig("AFTER_ACL_COLLECTION_READ")));

In the javadoc and documentation it says to use AuthorizationManagerBeforeMethodInterceptor or AuthorizationManagerAfterMethodInterceptor, but these classes won't allow me to change the returned object, filtering my returned collection.

What would be the supported way in spring-security 5.8.x or 6.x to filter returned objects based on permissions like AclEntryAfterInvocationCollectionFilteringProvider ?

Comment From: sjohnr

Hi @ratoaq2, thanks so much for reaching out!

It feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it).

Having said that, have you reviewed the 5.8 migration guide and the 6.0 migration guide? For example, the 5.8 migration guide has a section I use a custom AfterInvocationManager.

If you have specific suggestions for improvements to the docs after reviewing them, please let us know what those are in more detail.

Comment From: ratoaq2

Hi @sjohnr

Yes, I went through the whole migration guide... And I was also checking the source code. From my understanding, ACL is still a feature supported by spring-security and AfterInvocationProviderManager (which is now deprecated) was able to modify the returned object from a method invocation: My returned object is a list of objects which I need to filter out some items based on permissions, e.g.: ['a', 'b', 'c', 'd'] will become ['a', 'd']

The documentation says AuthorizationManager should be used instead using either AuthorizationManagerBeforeMethodInterceptor or AuthorizationManagerAfterMethodInterceptor.

But these AuthorizationManagerAfterMethodInterceptor can only decide to approve or deny: Spring Security Document how to replace deprecated MethodSecurityInterceptor when using AclEntryAfterInvocationCollectionFilteringProvider it's not possible to change the result (returned object) based on permissions, that's exactly what AclEntryAfterInvocationCollectionFilteringProvider (which is not deprecated) allow us.

There's no clarity in the documentation if ACL and/or AclEntryAfterInvocationCollectionFilteringProvider will become unsupported/deprecated/dropped in future... Or how to use AclEntryAfterInvocationCollectionFilteringProvider with the new AuthorizationManager style.

Exploring the source code, it seems there's no way to use AclEntryAfterInvocationCollectionFilteringProvider without using the deprecated MethodSecurityInterceptor and AfterInvocationProviderManager

Thanks

Comment From: sjohnr

Thanks @ratoaq2 for the details!

Comment From: jzheaux

Hi, @ratoaq2. I agree that this would be helpful to document.

Have you considered using @PostFilter like so:

@PostFilter("hasPermission(filterObject, `READ`)")

or, if your Permission instances are more sophisticated, like so:

@PostFilter("@post.filter(#root)") 

where post is a custom bean like:

@Component("post")
public class Permissions {
    public boolean filter(MethodSecurityExpressionOperations root) {
        Object filterObject = root.getFilterObject();
        Permission permission = myDerivePermission(...);
        return root.hasPermission(filterObject, permission);
    }
}

And then in both cases make sure method security is configured with an AclPermissionEvaluator instance like so:

@Bean 
static MethodSecurityExpressionHandler expressionHandler() {
    DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
    handler.setPermissionEvaluator(new AclPremissionEvaluator(aclService));
    return handler;
}

If that works for you, then I'll add it to the documentation.

I also believe that AbstractAclProvider and its subclasses should be deprecated in favor of using AclPermissionEvaluator and standard method security components.

Comment From: ratoaq2

Thanks for the response.

If I understood you correctly, the @PostFilter could be used to annotate methods that needs filtering. Is it possible to do that without annotating every method?

In our cases we have an interceptor for all methods which matches a certain expression (e.g.: *Dao.read*) and we apply the filtering on that, using the deprecated MethodSecurityInterceptor / AuthenticationManager:

        MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
        interceptor.setAuthenticationManager(authenticationManager);
        interceptor.setAccessDecisionManager(accessDecisionManager);
        interceptor.setAfterInvocationManager(afterInvocationManager);
        interceptor.setSecurityMetadataSource(new MapBasedMethodSecurityMetadataSource(Map.of(
                "*Dao.read*", List.of(new SecurityConfig("AFTER_ACL_READ")))));

        return interceptor;
    }

I'm just trying to understand how I can migrate these deprecated parts into the the supported way.

Comment From: ratoaq2

Just read the documentation changes and it seems the answer to my question is to create my custom method interceptor based on PostFilterAuthorizationMethodInterceptor But without using the annotation.

I believe the documentation now gives us better directions where to look when replacing these deprecated api. Thanks