Search code examples
securityspring-data-jpaspring-el

JPA - findById with @PostFilter: Filter target must be a collection or array type, but was Optional


I just updated my application to Spring Boot 2.0.3.RELEASE and found out my SpEL in @PostFilter is not working anymore:

@PreAuthorize("isFullyAuthenticated()")
@PostFilter("hasAuthority('ADMIN') or @companyService.isOwnerOfPerson(#id, principal)")
@Override
public Optional<Person> findById(@Param("id") String id);

The error is org.springframework.security.authentication.InternalAuthenticationServiceException: Filter target must be a collection or array type, but was Optional[Person ...]

I have seen that this is being discussed on https://jira.spring.io/browse/SPR-15878 but I was trying to get a work around up and running: I extended DefaultMethodSecurityExpressionHandler and overwrote the setReturnObject method as suggested in the ticket referenced above:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    @Override
    public void setReturnObject(Object returnObject, EvaluationContext ctx) {
        if (returnObject instanceof Optional) {
            Optional<?> optional = (Optional<?>)returnObject;

            Object value = null;
            if (optional.isPresent()) {
                value = optional.get();
            }

            ((MethodSecurityExpressionOperations) ctx.getRootObject().getValue())
            .setReturnObject(value);
        } else {
            super.setReturnObject(returnObject, ctx);
        }
    }

}

Next, I configured method security as follows:

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

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }

}

I am not sure this is how it is supposed to be, but I know that it does not work yet ;)

Can anyone help out?


Solution

  • this bug appears because @PostFilter only works on collections (or arrays) as return values while findById only returns an optional of a single value. You can see it in your exception message:

    Filter target must be a collection or array type