Skip to content

Commit 3cc245c

Browse files
committed
Added filtering optionals to DefaultMethodSecurityExpressionHandler
1 parent a443126 commit 3cc245c

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

Diff for: core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.LinkedHashMap;
2424
import java.util.List;
2525
import java.util.Map;
26+
import java.util.Optional;
2627
import java.util.stream.Stream;
2728

2829
import org.aopalliance.intercept.MethodInvocation;
@@ -94,7 +95,7 @@ protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authen
9495

9596
/**
9697
* Filters the {@code filterTarget} object (which must be either a collection, array,
97-
* map or stream), by evaluating the supplied expression.
98+
* map, stream or optional), by evaluating the supplied expression.
9899
* <p>
99100
* If a {@code Collection} or {@code Map} is used, the original instance will be
100101
* modified to contain the elements for which the permission expression evaluates to
@@ -117,8 +118,11 @@ public Object filter(Object filterTarget, Expression filterExpression, Evaluatio
117118
if (filterTarget instanceof Stream) {
118119
return filterStream((Stream<?>) filterTarget, filterExpression, ctx, rootObject);
119120
}
121+
if (filterTarget instanceof Optional) {
122+
return filterOptional((Optional<?>) filterTarget, filterExpression, ctx, rootObject);
123+
}
120124
throw new IllegalArgumentException(
121-
"Filter target must be a collection, array, map or stream type, but was " + filterTarget);
125+
"Filter target must be a collection, array, map, stream or optional type, but was " + filterTarget);
122126
}
123127

124128
private <T> Object filterCollection(Collection<T> filterTarget, Expression filterExpression, EvaluationContext ctx,
@@ -186,6 +190,14 @@ private Object filterStream(final Stream<?> filterTarget, Expression filterExpre
186190
}).onClose(filterTarget::close);
187191
}
188192

193+
private Object filterOptional(final Optional<?> filterTarget, Expression filterExpression, EvaluationContext ctx,
194+
MethodSecurityExpressionOperations rootObject) {
195+
return filterTarget.filter(filterObject -> {
196+
rootObject.setFilterObject(filterObject);
197+
return ExpressionUtils.evaluateAsBoolean(filterExpression, ctx);
198+
});
199+
}
200+
189201
/**
190202
* Sets the {@link AuthenticationTrustResolver} to be used. The default is
191203
* {@link AuthenticationTrustResolverImpl}.

Diff for: core/src/test/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandlerTests.java

+34
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.HashMap;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.Optional;
2223
import java.util.stream.Collectors;
2324
import java.util.stream.Stream;
2425

@@ -167,6 +168,39 @@ public void filterStreamWhenClosedThenUpstreamGetsClosed() {
167168
verify(upstream).close();
168169
}
169170

171+
@Test
172+
@SuppressWarnings("unchecked")
173+
public void filterMatchingOptional() {
174+
final Optional<String> optional = Optional.of("1");
175+
Expression expression = this.handler.getExpressionParser().parseExpression("filterObject ne '2'");
176+
EvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);
177+
Object filtered = this.handler.filter(optional, expression, context);
178+
Optional<String> result = ((Optional<String>) filtered);
179+
assertThat(result).isPresent().get().isEqualTo("1");
180+
}
181+
182+
@Test
183+
@SuppressWarnings("unchecked")
184+
public void filterNotMatchingOptional() {
185+
final Optional<String> optional = Optional.of("2");
186+
Expression expression = this.handler.getExpressionParser().parseExpression("filterObject ne '2'");
187+
EvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);
188+
Object filtered = this.handler.filter(optional, expression, context);
189+
Optional<String> result = ((Optional<String>) filtered);
190+
assertThat(result).isNotPresent();
191+
}
192+
193+
@Test
194+
@SuppressWarnings("unchecked")
195+
public void filterEmptyOptional() {
196+
final Optional<String> optional = Optional.empty();
197+
Expression expression = this.handler.getExpressionParser().parseExpression("filterObject ne '2'");
198+
EvaluationContext context = this.handler.createEvaluationContext(this.authentication, this.methodInvocation);
199+
Object filtered = this.handler.filter(optional, expression, context);
200+
Optional<String> result = ((Optional<String>) filtered);
201+
assertThat(result).isNotPresent();
202+
}
203+
170204
static class Foo {
171205

172206
void bar() {

0 commit comments

Comments
 (0)