PostFilter Support for Streams

Users can return a Stream from a @PostFilter-annotated method.

Fixes: gh-3743
This commit is contained in:
Karl Goffin 2018-10-27 21:47:07 -04:00 committed by Josh Cummings
parent e1c7dd6480
commit 92e68a589a
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
2 changed files with 47 additions and 5 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.*;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
@ -86,8 +87,8 @@ public class DefaultMethodSecurityExpressionHandler extends
}
/**
* Filters the {@code filterTarget} object (which must be either a collection or an
* array), by evaluating the supplied expression.
* Filters the {@code filterTarget} object (which must be either a collection, array,
* or stream), by evaluating the supplied expression.
* <p>
* If a {@code Collection} is used, the original instance will be modified to contain
* the elements for which the permission expression evaluates to {@code true}. For an
@ -172,8 +173,27 @@ public class DefaultMethodSecurityExpressionHandler extends
return filtered;
}
if (filterTarget instanceof Stream) {
final Stream<?> original = (Stream<?>) filterTarget;
if (debug) {
logger.debug("Filtering stream with " + original.count() + " elements");
}
Stream<?> filtered = original.filter(filterObject -> {
rootObject.setFilterObject(filterObject);
return ExpressionUtils.evaluateAsBoolean(filterExpression, ctx);
})
.onClose(original::close);
if (debug) {
logger.debug("Retaining elements: " + filtered.collect(Collectors.toList()));
}
return filtered;
}
throw new IllegalArgumentException(
"Filter target must be a collection or array type, but was "
"Filter target must be a collection, array, or stream type, but was "
+ filterTarget);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@ import static org.mockito.Mockito.verify;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -30,6 +31,9 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.List;
import java.util.stream.*;
@RunWith(MockitoJUnitRunner.class)
public class DefaultMethodSecurityExpressionHandlerTests {
private DefaultMethodSecurityExpressionHandler handler;
@ -68,4 +72,22 @@ public class DefaultMethodSecurityExpressionHandlerTests {
verify(trustResolver).isAnonymous(authentication);
}
@Test
@SuppressWarnings("unchecked")
public void testFilteringStream() {
final Stream<String> stream = Stream.of("1", "2", "3");
Expression expression = handler.getExpressionParser().parseExpression("filterObject ne '2'");
EvaluationContext context = handler.createEvaluationContext(authentication,
methodInvocation);
Object filtered = handler.filter(stream, expression, context);
Assert.assertTrue("response was wrong type", Stream.class.isAssignableFrom(filtered.getClass()));
List<String> list = ((Stream<String>) filtered).collect(Collectors.toList());
Assert.assertEquals(2, list.size());
Assert.assertFalse("contains filtered element", list.contains("2"));
}
}