Skip to content

Commit ae26ce2

Browse files
authored
Merge pull request #1297 from HubSpot/3.0/validators
[9] Jinjava 3.0: Add method and return type validator framework
2 parents f6cd37a + da2b67b commit ae26ce2

71 files changed

Lines changed: 1718 additions & 627 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/com/hubspot/jinjava/JinjavaConfig.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.hubspot.jinjava.el.JinjavaObjectUnwrapper;
2525
import com.hubspot.jinjava.el.JinjavaProcessors;
2626
import com.hubspot.jinjava.el.ObjectUnwrapper;
27+
import com.hubspot.jinjava.el.ext.AllowlistMethodValidator;
28+
import com.hubspot.jinjava.el.ext.AllowlistReturnTypeValidator;
2729
import com.hubspot.jinjava.features.FeatureConfig;
2830
import com.hubspot.jinjava.features.Features;
2931
import com.hubspot.jinjava.interpret.Context.Library;
@@ -98,8 +100,6 @@ default int getMaxMacroRecursionDepth() {
98100
}
99101

100102
Map<Library, Set<String>> getDisabled();
101-
Set<String> getRestrictedMethods();
102-
Set<String> getRestrictedProperties();
103103

104104
@Value.Default
105105
default boolean isFailOnUnknownTokens() {
@@ -161,6 +161,16 @@ default TokenScannerSymbols getTokenScannerSymbols() {
161161
return new DefaultTokenScannerSymbols();
162162
}
163163

164+
@Value.Default
165+
default AllowlistMethodValidator getMethodValidator() {
166+
return AllowlistMethodValidator.DEFAULT;
167+
}
168+
169+
@Value.Default
170+
default AllowlistReturnTypeValidator getReturnTypeValidator() {
171+
return AllowlistReturnTypeValidator.DEFAULT;
172+
}
173+
164174
@Value.Default
165175
default ELResolver getElResolver() {
166176
return isDefaultReadOnlyResolver()

src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
import com.hubspot.jinjava.interpret.UnknownTokenException;
2323
import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory;
2424
import com.hubspot.jinjava.lib.fn.ELFunctionDefinition;
25+
import com.hubspot.jinjava.objects.serialization.PyishObjectMapper;
2526
import com.hubspot.jinjava.util.WhitespaceUtils;
2627
import de.odysseus.el.tree.TreeBuilderException;
2728
import java.util.Arrays;
2829
import java.util.List;
30+
import java.util.Objects;
2931
import javax.el.ELException;
3032
import javax.el.ExpressionFactory;
3133
import javax.el.PropertyNotFoundException;
@@ -39,7 +41,7 @@ public class ExpressionResolver {
3941

4042
private final JinjavaInterpreter interpreter;
4143
private final ExpressionFactory expressionFactory;
42-
private final JinjavaInterpreterResolver resolver;
44+
private final ReturnTypeValidatingJinjavaInterpreterResolver resolver;
4345
private final JinjavaELContext elContext;
4446
private final ObjectUnwrapper objectUnwrapper;
4547

@@ -53,7 +55,11 @@ public ExpressionResolver(JinjavaInterpreter interpreter, Jinjava jinjava) {
5355
? jinjava.getEagerExpressionFactory()
5456
: jinjava.getExpressionFactory();
5557

56-
this.resolver = new JinjavaInterpreterResolver(interpreter);
58+
this.resolver =
59+
new ReturnTypeValidatingJinjavaInterpreterResolver(
60+
interpreter.getConfig().getReturnTypeValidator(),
61+
new JinjavaInterpreterResolver(interpreter)
62+
);
5763
this.elContext = new JinjavaELContext(interpreter, resolver);
5864
for (ELFunctionDefinition fn : jinjava.getGlobalContext().getAllFunctions()) {
5965
this.elContext.setFunction(fn.getNamespace(), fn.getLocalName(), fn.getMethod());
@@ -343,4 +349,11 @@ public Object resolveProperty(Object object, List<String> propertyNames) {
343349
public Object wrap(Object object) {
344350
return resolver.wrap(object);
345351
}
352+
353+
public String getAsString(Object object) {
354+
if (interpreter.getConfig().getLegacyOverrides().isUsePyishObjectMapper()) {
355+
return PyishObjectMapper.getAsUnquotedPyishString(object);
356+
}
357+
return Objects.toString(object, "");
358+
}
346359
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.hubspot.jinjava.el;
2+
3+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
4+
5+
public interface HasInterpreter {
6+
JinjavaInterpreter interpreter();
7+
}

src/main/java/com/hubspot/jinjava/el/JinjavaELContext.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import java.lang.reflect.Method;
66
import javax.el.ELResolver;
77

8-
public class JinjavaELContext extends SimpleContext {
8+
public class JinjavaELContext extends SimpleContext implements HasInterpreter {
99

1010
private JinjavaInterpreter interpreter;
1111
private MacroFunctionMapper functionMapper;
1212

13+
@Deprecated
1314
public JinjavaELContext() {
1415
super();
1516
}
@@ -19,6 +20,11 @@ public JinjavaELContext(JinjavaInterpreter interpreter, ELResolver resolver) {
1920
this.interpreter = interpreter;
2021
}
2122

23+
@Override
24+
public JinjavaInterpreter interpreter() {
25+
return interpreter;
26+
}
27+
2228
@Override
2329
public MacroFunctionMapper getFunctionMapper() {
2430
if (functionMapper == null) {

src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.hubspot.jinjava.objects.PyWrapper;
2222
import com.hubspot.jinjava.objects.collections.SizeLimitingPyList;
2323
import com.hubspot.jinjava.objects.collections.SizeLimitingPyMap;
24+
import com.hubspot.jinjava.objects.collections.SizeLimitingPySet;
2425
import com.hubspot.jinjava.objects.date.FormattedDate;
2526
import com.hubspot.jinjava.objects.date.InvalidDateFormatException;
2627
import com.hubspot.jinjava.objects.date.PyishDate;
@@ -39,6 +40,7 @@
3940
import java.util.Locale;
4041
import java.util.Map;
4142
import java.util.Objects;
43+
import java.util.Set;
4244
import javax.el.ArrayELResolver;
4345
import javax.el.CompositeELResolver;
4446
import javax.el.ELContext;
@@ -274,7 +276,7 @@ private Object getValue(
274276
}
275277

276278
context.setPropertyResolved(true);
277-
return wrap(value);
279+
return value;
278280
}
279281

280282
@SuppressWarnings("unchecked")
@@ -308,6 +310,12 @@ Object wrap(Object value) {
308310
interpreter.getConfig().getMaxListSize()
309311
);
310312
}
313+
if (Set.class.isAssignableFrom(value.getClass())) {
314+
return new SizeLimitingPySet(
315+
(Set<Object>) value,
316+
interpreter.getConfig().getMaxListSize()
317+
);
318+
}
311319
if (Map.class.isAssignableFrom(value.getClass())) {
312320
// FIXME: ensure keys are actually strings, if not, convert them
313321
return new SizeLimitingPyMap(

src/main/java/com/hubspot/jinjava/el/NoInvokeELContext.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package com.hubspot.jinjava.el;
22

3+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
34
import javax.el.ELContext;
45
import javax.el.ELResolver;
56
import javax.el.FunctionMapper;
67
import javax.el.VariableMapper;
78

8-
public class NoInvokeELContext extends ELContext {
9+
public class NoInvokeELContext extends ELContext implements HasInterpreter {
910

10-
private ELContext delegate;
11+
private final ELContext delegate;
1112
private NoInvokeELResolver elResolver;
1213

1314
public NoInvokeELContext(ELContext delegate) {
@@ -31,4 +32,12 @@ public FunctionMapper getFunctionMapper() {
3132
public VariableMapper getVariableMapper() {
3233
return delegate.getVariableMapper();
3334
}
35+
36+
@Override
37+
public JinjavaInterpreter interpreter() {
38+
if (delegate instanceof HasInterpreter hasInterpreter) {
39+
return hasInterpreter.interpreter();
40+
}
41+
return JinjavaInterpreter.getCurrent();
42+
}
3443
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.hubspot.jinjava.el;
2+
3+
import com.hubspot.jinjava.el.ext.AllowlistReturnTypeValidator;
4+
import java.beans.FeatureDescriptor;
5+
import java.util.Iterator;
6+
import javax.el.ELContext;
7+
import javax.el.ELResolver;
8+
9+
class ReturnTypeValidatingJinjavaInterpreterResolver extends ELResolver {
10+
11+
private final AllowlistReturnTypeValidator returnTypeValidator;
12+
private final JinjavaInterpreterResolver delegate;
13+
14+
ReturnTypeValidatingJinjavaInterpreterResolver(
15+
AllowlistReturnTypeValidator returnTypeValidator,
16+
JinjavaInterpreterResolver delegate
17+
) {
18+
this.returnTypeValidator = returnTypeValidator;
19+
this.delegate = delegate;
20+
}
21+
22+
@Override
23+
public Class<?> getCommonPropertyType(ELContext context, Object base) {
24+
return delegate.getCommonPropertyType(context, base);
25+
}
26+
27+
@Override
28+
public Iterator<FeatureDescriptor> getFeatureDescriptors(
29+
ELContext context,
30+
Object base
31+
) {
32+
return delegate.getFeatureDescriptors(context, base);
33+
}
34+
35+
@Override
36+
public Class<?> getType(ELContext context, Object base, Object property) {
37+
return delegate.getType(context, base, property);
38+
}
39+
40+
@Override
41+
public Object getValue(ELContext context, Object base, Object property) {
42+
return returnTypeValidator.validateReturnType(
43+
wrap(delegate.getValue(context, base, property))
44+
);
45+
}
46+
47+
@Override
48+
public boolean isReadOnly(ELContext context, Object base, Object property) {
49+
return delegate.isReadOnly(context, base, property);
50+
}
51+
52+
@Override
53+
public void setValue(ELContext context, Object base, Object property, Object value) {
54+
delegate.setValue(context, base, property, value);
55+
}
56+
57+
@Override
58+
public Object invoke(
59+
ELContext context,
60+
Object base,
61+
Object method,
62+
Class<?>[] paramTypes,
63+
Object[] params
64+
) {
65+
return returnTypeValidator.validateReturnType(
66+
wrap(delegate.invoke(context, base, method, paramTypes, params))
67+
);
68+
}
69+
70+
Object wrap(Object object) {
71+
return delegate.wrap(object);
72+
}
73+
}

0 commit comments

Comments
 (0)