DATAMONGO-2418 - Obtain EvaluationContextExtensions lazily when parsing Bson queries.
An eager evaluation of the context extension can lead to errors when e.g. the security context was not present. Original pull request: #814.
This commit is contained in:
committed by
Mark Paluch
parent
277b7a1c7c
commit
60112b4d14
@@ -113,7 +113,7 @@ class AggregationUtils {
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues()));
|
||||
() -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues()));
|
||||
|
||||
List<AggregationOperation> target = new ArrayList<>(method.getAnnotatedAggregation().length);
|
||||
for (String source : method.getAnnotatedAggregation()) {
|
||||
|
||||
@@ -73,7 +73,7 @@ class CollationUtils {
|
||||
if (StringUtils.trimLeadingWhitespace(collationExpression).startsWith("{")) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue),
|
||||
expressionParser, evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues()));
|
||||
expressionParser, () -> evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues()));
|
||||
|
||||
return Collation.from(CODEC.decode(collationExpression, bindingContext));
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
() -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
|
||||
Document queryObject = CODEC.decode(this.query, bindingContext);
|
||||
Document fieldsObject = CODEC.decode(this.fieldSpec, bindingContext);
|
||||
|
||||
@@ -116,7 +116,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
() -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
|
||||
Document queryObject = CODEC.decode(this.query, bindingContext);
|
||||
Document fieldsObject = CODEC.decode(this.fieldSpec, bindingContext);
|
||||
|
||||
@@ -15,28 +15,54 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.util.json;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Reusable context for binding parameters to an placeholder or a SpEL expression within a JSON structure. <br />
|
||||
* Reusable context for binding parameters to a placeholder or a SpEL expression within a JSON structure. <br />
|
||||
* To be used along with {@link ParameterBindingDocumentCodec#decode(String, ParameterBindingContext)}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.2
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public class ParameterBindingContext {
|
||||
|
||||
private final ValueProvider valueProvider;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final EvaluationContext evaluationContext;
|
||||
private final Lazy<EvaluationContext> evaluationContext;
|
||||
|
||||
/**
|
||||
* @param valueProvider
|
||||
* @param expressionParser
|
||||
* @param evaluationContext
|
||||
* @deprecated since 2.2.3 - Please use
|
||||
* {@link #ParameterBindingContext(ValueProvider, SpelExpressionParser, Supplier)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
|
||||
EvaluationContext evaluationContext) {
|
||||
|
||||
this(valueProvider, expressionParser, () -> evaluationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param valueProvider
|
||||
* @param expressionParser
|
||||
* @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval.
|
||||
* @since 2.2.3
|
||||
*/
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this.valueProvider = valueProvider;
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContext = evaluationContext instanceof Lazy ? (Lazy) evaluationContext : Lazy.of(evaluationContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object bindableValueForIndex(int index) {
|
||||
@@ -47,6 +73,18 @@ public class ParameterBindingContext {
|
||||
public Object evaluateExpression(String expressionString) {
|
||||
|
||||
Expression expression = expressionParser.parseExpression(expressionString);
|
||||
return expression.getValue(this.evaluationContext, Object.class);
|
||||
return expression.getValue(getEvaluationContext(), Object.class);
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
return this.evaluationContext.get();
|
||||
}
|
||||
|
||||
public SpelExpressionParser getExpressionParser() {
|
||||
return expressionParser;
|
||||
}
|
||||
|
||||
public ValueProvider getValueProvider() {
|
||||
return valueProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
||||
public Document decode(@Nullable String json, Object[] values) {
|
||||
|
||||
return decode(json, new ParameterBindingContext((index) -> values[index], new SpelExpressionParser(),
|
||||
EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
|
||||
() -> EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
|
||||
}
|
||||
|
||||
public Document decode(@Nullable String json, ParameterBindingContext bindingContext) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -96,6 +97,15 @@ public class ParameterBindingJsonReader extends AbstractBsonReader {
|
||||
public ParameterBindingJsonReader(String json, ValueProvider accessor, SpelExpressionParser spelExpressionParser,
|
||||
EvaluationContext evaluationContext) {
|
||||
|
||||
this(json, accessor, spelExpressionParser, () -> evaluationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.2.3
|
||||
*/
|
||||
public ParameterBindingJsonReader(String json, ValueProvider accessor, SpelExpressionParser spelExpressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this.scanner = new JsonScanner(json);
|
||||
setContext(new Context(null, BsonContextType.TOP_LEVEL));
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ import java.util.List;
|
||||
import org.bson.Document;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.junit.Test;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ParameterBindingJsonReader}.
|
||||
@@ -197,6 +201,25 @@ public class ParameterBindingJsonReaderUnitTests {
|
||||
assertThat(target).isEqualTo(Document.parse("{ 'end_date' : { $gte : { $date : " + time + " } } } "));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2418
|
||||
public void shouldNotAccessSpElEveluationContextWhenNoSpelPresentInBindableTarget() {
|
||||
|
||||
Object[] args = new Object[] { "value" };
|
||||
EvaluationContext evaluationContext = new StandardEvaluationContext() {
|
||||
|
||||
@Override
|
||||
public TypedValue getRootObject() {
|
||||
throw new RuntimeException("o_O");
|
||||
}
|
||||
};
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader("{ 'name':'?0' }",
|
||||
new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext));
|
||||
Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build());
|
||||
|
||||
assertThat(target).isEqualTo(new Document("name", "value"));
|
||||
}
|
||||
|
||||
private static Document parse(String json, Object... args) {
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);
|
||||
|
||||
Reference in New Issue
Block a user