Airy
Airy

Reputation: 758

@PostFilter on Optional

I would like to use @PostFilter on method returning Optional.

Spring DefaultMethodSecurityExpressionHandler currently not handling Optional but I could make a simple decorator and transform the Optional into Stream. How can I register such decorator without creating a new GlobalMethodSecurityConfiguration as I am using an internal framework I can't modify.

Is there another way to do that ?

Upvotes: 2

Views: 434

Answers (2)

Stefan
Stefan

Reputation: 51

The easier way to do it is like this:

import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.stereotype.Service;

@Service
public class AnyTypeMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
    @Override
    public Object filter(final Object filterTarget, final Expression filterExpression, final EvaluationContext ctx) {
        if (filterTarget instanceof Collection || filterTarget != null && filterTarget.getClass().isArray() || filterTarget instanceof Stream) {
            return super.filter(filterTarget, filterExpression, ctx);
        }
        if (filterTarget instanceof Optional) {
            return ((Stream<?>) super.filter(((Optional<?>) filterTarget).stream(), filterExpression, ctx)).findAny();
        }
        return ((Stream<?>) super.filter(Stream.ofNullable(filterTarget), filterExpression, ctx)).findAny().orElse(null);
    }
}

That will handle Optionals and also plain Java Objects (making them null if they are filtered).

Upvotes: 5

Airy
Airy

Reputation: 758

The solution was as simple as declaring the decorator as a @Service

@Service
public class OptionalAdapterMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {

    private final MethodSecurityExpressionHandler handler

    public CustomMethodSecurityExpressionHandler(MethodSecurityExpressionHandler handler){
        this.handler = handler;
    }

    @Autowired
    public CustomMethodSecurityExpressionHandler(ApplicationContext applicationContext) {
        this(new DefaultMethodSecurityExpressionHandler());
        ((DefaultMethodSecurityExpressionHandler)this.original).setApplicationContext(applicationContext);
    }

    @Override
    public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
        if (filterTarget instanceof Optional) {
            final List<?> optionalUnfilteredList = ((Optional<?>) filterTarget).stream()
                .collect(Collectors.toList());

            final List<?> optionalFilteredList = (List<?>) this.handler.filter(optionalUnfilteredList, filterExpression, ctx);
            return optionalFilteredList.stream()
                .findFirst();
        }
        return this.handler.filter(filterTarget, filterExpression, ctx);
    }
    
    ...
}

Upvotes: 0

Related Questions