Skip to content

Commit

Permalink
replace allowUnsafeMethod with a Method Access validator interface. P… (
Browse files Browse the repository at this point in the history
#511)

* replace allowUnsafeMethod with a Method Access validator interface. Provide a NoOp method access and a default one (#493)

* Increase performance

* Add possibility to define a MethodAccessValidator bean for spring-boot app

* Remove possibility to turn sandbox off via template
  • Loading branch information
ebussieres authored May 21, 2020
1 parent 0e829c0 commit d967910
Show file tree
Hide file tree
Showing 26 changed files with 571 additions and 111 deletions.
5 changes: 3 additions & 2 deletions docs/src/orchid/resources/changelog/v3_1_4.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ version: '3.1.4'
- Slice filter: Use collection size when toIndex is greater than collection size (#504)
- Adjust spring boot doc (#509)
- Build with jdk14 (#508)
- Set proxyBeanMethods to false (#507)
- Add access to Spring Beans/request/session and response when using Pebble with WebFlux (#512)
- Set proxyBeanMethods to false and build with spring boot 2.3 (#507)
- Add access to Spring Beans/request/session and response when using Pebble with WebFlux (#512)
- Remove allowUnsafeMethods property and replace it with methodAccessValidator. Default one is BlacklistMethodAccessValidtor (#511)
2 changes: 1 addition & 1 deletion docs/src/orchid/resources/wiki/guide/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ All the settings are set during the construction of the `PebbleEngine` object.
| `executorService` | An `ExecutorService` that allows the usage of some advanced multithreading features, such as the `parallel` tag. | `null` |
| `loader` | An implementation of the `Loader` interface which is used to find templates. | An implementation of the `DelegatingLoader` which uses a `ClasspathLoader` and a `FileLoader` behind the scenes. |
| `strictVariables` | If set to true, Pebble will throw an exception if you try to access a variable or attribute that does not exist (or an attribute of a null variable). If set to false, your template will treat non-existing variables/attributes as null without ever skipping a beat. | `false` |
| `allowUnsafeMethods` | If set to false, Pebble will throw an exception if you try to access unsafe methods. Unsafe methods are defined [here](https://github.com/PebbleTemplates/pebble/tree/master/pebble/src/main/resources/unsafeMethods.properties) | `true` in 2.x, 'false' in v3.x |
| `methodAccessValidator` | Pebble provides two implementations. NoOpMethodAccessValidator which do nothing and BlacklistMethodAccessValidator which checks that the method being called is not blacklisted. | `BlacklistMethodAccessValidator`
| `literalDecimalTreatedAsInteger` | option for toggling to enable/disable literal decimal treated as integer | `false` |
| `literalNumbersAsBigDecimals` | option for toggling to enable/disable literal numbers treated as BigDecimals | `false` |
| `greedyMatchMethod` | option for toggling to enable/disable greedy matching mode for finding java method. Reduce the limit of the parameter type, try to find other method which has compatible parameter types. | `false` |
16 changes: 13 additions & 3 deletions docs/src/orchid/resources/wiki/guide/spring-boot-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ A number of properties can be defined in Spring Boot externalized configuration,
* ``pebble.exposeSessionAttributes``: defines whether all session attributes should be added to the model prior to merging with the template for the ViewResolver. Defaults to ``false``
* ``pebble.defaultLocale``: defines the default locale that will be used to configure the PebbleEngine. Defaults to ``null``
* ``pebble.strictVariables``: enable or disable the strict variable checking in the PebbleEngine. Defaults to ``false``
* ``pebble.greedyMatchMethod``: enable or disable the greedy matching mode for finding java method in the PebbleEngine. Defaults to ``false``

## Examples
There is the spring petclinic example which has been migrated to [pebble](https://github.com/PebbleTemplates/spring-petclinic)
Expand Down Expand Up @@ -86,7 +87,7 @@ public Loader<?> pebbleLoader() {
return new MyCustomLoader();
}
```
PLEASE NOTE: this loader's prefix and suffix will be both overwritten when the ViewResolver is configured. You should use the externalized configuration for changing these properties.
**PLEASE NOTE**: this loader's prefix and suffix will be both overwritten when the ViewResolver is configured. You should use the externalized configuration for changing these properties.

### Customizing the PebbleEngine
Likewise, you can build a custom engine and make it the default by using the bean name ``pebbleEngine``:
Expand All @@ -97,16 +98,25 @@ public PebbleEngine pebbleEngine() {
}
```

### Customizing the MethodAccessValidator
You can provide your own MethodAccessValidator or switch to NoOpMethodAccessValidator by providing a MethodAccessValidator Bean
```java
@Bean
public MethodAccessValidator methodAccessValidator() {
return new NoOpMethodAccessValidator();
}
```

### Customizing the ViewResolver
And the same goes for the ViewResolver, using the bean name ``pebbleViewResolver``:
And the same goes for the ViewResolver
```java
@Bean
public PebbleViewResolver pebbleViewResolver() {
return new PebbleViewResolver();
}
```

For reactive app, you need to use the bean name ``pebbleReactiveViewResolver``:
For reactive app
```java
@Bean
public PebbleReactiveViewResolver pebbleReactiveViewResolver() {
Expand Down
4 changes: 2 additions & 2 deletions pebble-spring/pebble-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<artifactId>pebble-spring-boot-starter</artifactId>

<name>Pebble Spring Boot 2 Starter</name>
<description>Spring Boot 2 starter or Pebble Template Engine</description>
<description>Spring Boot 2 starter for Pebble Template Engine</description>
<url>http://pebbletemplates.io</url>

<properties>
<boot.version>2.2.6.RELEASE</boot.version>
<boot.version>2.3.0.RELEASE</boot.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mitchellbosecke.pebble.boot.autoconfigure;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.attributes.methodaccess.MethodAccessValidator;
import com.mitchellbosecke.pebble.extension.Extension;
import com.mitchellbosecke.pebble.loader.ClasspathLoader;
import com.mitchellbosecke.pebble.loader.Loader;
Expand Down Expand Up @@ -34,7 +35,7 @@ public Loader<?> pebbleLoader(PebbleProperties properties) {

@Bean
@ConditionalOnMissingBean
public SpringExtension pebbleSpringExtension(MessageSource messageSource) {
public SpringExtension springExtension(MessageSource messageSource) {
return new SpringExtension(messageSource);
}

Expand All @@ -43,7 +44,8 @@ public SpringExtension pebbleSpringExtension(MessageSource messageSource) {
public PebbleEngine pebbleEngine(PebbleProperties properties,
Loader<?> pebbleLoader,
SpringExtension springExtension,
@Nullable List<Extension> extensions) {
@Nullable List<Extension> extensions,
@Nullable MethodAccessValidator methodAccessValidator) {
PebbleEngine.Builder builder = new PebbleEngine.Builder();
builder.loader(pebbleLoader);
builder.extension(springExtension);
Expand All @@ -58,6 +60,9 @@ public PebbleEngine pebbleEngine(PebbleProperties properties,
}
builder.strictVariables(properties.isStrictVariables());
builder.greedyMatchMethod(properties.isGreedyMatchMethod());
if (methodAccessValidator != null) {
builder.methodAccessValidator(methodAccessValidator);
}
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class PebbleReactiveWebConfiguration extends AbstractPebbleConfiguration {

@Bean
@ConditionalOnMissingBean(name = "pebbleReactiveViewResolver")
@ConditionalOnMissingBean
PebbleReactiveViewResolver pebbleReactiveViewResolver(PebbleProperties properties,
PebbleEngine pebbleEngine) {
String prefix = properties.getPrefix();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class PebbleServletWebConfiguration extends AbstractPebbleConfiguration {

@Bean
@ConditionalOnMissingBean(name = "pebbleViewResolver")
@ConditionalOnMissingBean
PebbleViewResolver pebbleViewResolver(PebbleProperties properties,
PebbleEngine pebbleEngine) {
PebbleViewResolver pvr = new PebbleViewResolver(pebbleEngine);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.mitchellbosecke.pebble.boot.autoconfigure;

import static java.util.Locale.CHINESE;
import static java.util.Locale.FRENCH;
import static org.assertj.core.api.Assertions.assertThat;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.attributes.methodaccess.BlacklistMethodAccessValidator;
import com.mitchellbosecke.pebble.attributes.methodaccess.MethodAccessValidator;
import com.mitchellbosecke.pebble.attributes.methodaccess.NoOpMethodAccessValidator;
import com.mitchellbosecke.pebble.loader.Loader;
import com.mitchellbosecke.pebble.spring.extension.SpringExtension;
import com.mitchellbosecke.pebble.spring.reactive.PebbleReactiveViewResolver;
Expand All @@ -21,6 +25,7 @@
class PebbleAutoConfigurationTest {

private static final Locale DEFAULT_LOCALE = CHINESE;
private static final Locale CUSTOM_LOCALE = FRENCH;
private AnnotationConfigServletWebApplicationContext webContext;

private AnnotationConfigReactiveWebApplicationContext reactiveWebContext;
Expand All @@ -31,6 +36,15 @@ void registerBeansForServletApp() {
assertThat(this.webContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.webContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(DEFAULT_LOCALE);
assertThat(this.webContext.getBean(PebbleEngine.class).isStrictVariables()).isTrue();
assertThat(
this.webContext.getBean(PebbleEngine.class).getEvaluationOptions().isGreedyMatchMethod())
.isTrue();
assertThat(this.webContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
BlacklistMethodAccessValidator.class);
assertThat(this.webContext.getBeansOfType(PebbleViewResolver.class)).hasSize(1);
}

Expand All @@ -40,7 +54,34 @@ void registerCompilerForServletApp() {
assertThat(this.webContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.webContext.getBean(PebbleEngine.class).getDefaultLocale()).isEqualTo(DEFAULT_LOCALE);
assertThat(this.webContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(CUSTOM_LOCALE);
assertThat(this.webContext.getBean(PebbleEngine.class).isStrictVariables()).isFalse();
assertThat(
this.webContext.getBean(PebbleEngine.class).getEvaluationOptions().isGreedyMatchMethod())
.isFalse();
assertThat(this.webContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
BlacklistMethodAccessValidator.class);
assertThat(this.webContext.getBeansOfType(PebbleViewResolver.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(PebbleReactiveViewResolver.class)).isEmpty();
}

@Test
void registerCustomMethodAccessValidatorForServletApp() {
this.loadWithServlet(CustomMethodAccessValidatorConfiguration.class);
assertThat(this.webContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.webContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(DEFAULT_LOCALE);
assertThat(this.webContext.getBean(PebbleEngine.class).isStrictVariables()).isTrue();
assertThat(
this.webContext.getBean(PebbleEngine.class).getEvaluationOptions().isGreedyMatchMethod())
.isTrue();
assertThat(this.webContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
NoOpMethodAccessValidator.class);
assertThat(this.webContext.getBeansOfType(PebbleViewResolver.class)).hasSize(1);
assertThat(this.webContext.getBeansOfType(PebbleReactiveViewResolver.class)).isEmpty();
}
Expand All @@ -51,6 +92,14 @@ void registerBeansForReactiveApp() {
assertThat(this.reactiveWebContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(DEFAULT_LOCALE);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).isStrictVariables()).isTrue();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.isGreedyMatchMethod()).isTrue();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
BlacklistMethodAccessValidator.class);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleViewResolver.class)).isEmpty();
assertThat(this.reactiveWebContext.getBeansOfType(PebbleReactiveViewResolver.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleViewResolver.class)).isEmpty();
Expand All @@ -62,14 +111,43 @@ void registerCompilerForReactiveApp() {
assertThat(this.reactiveWebContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getDefaultLocale()).isEqualTo(DEFAULT_LOCALE);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(CUSTOM_LOCALE);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).isStrictVariables()).isFalse();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.isGreedyMatchMethod()).isFalse();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
BlacklistMethodAccessValidator.class);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleReactiveViewResolver.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleViewResolver.class)).isEmpty();
}

@Test
void registerCustomMethodAccessValidatorForReactiveApp() {
this.loadWithReactive(CustomMethodAccessValidatorConfiguration.class);
assertThat(this.reactiveWebContext.getBeansOfType(Loader.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(SpringExtension.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleEngine.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getDefaultLocale())
.isEqualTo(DEFAULT_LOCALE);
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).isStrictVariables()).isTrue();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.isGreedyMatchMethod()).isTrue();
assertThat(this.reactiveWebContext.getBean(PebbleEngine.class).getEvaluationOptions()
.getMethodAccessValidator()).isInstanceOf(
NoOpMethodAccessValidator.class);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleViewResolver.class)).isEmpty();
assertThat(this.reactiveWebContext.getBeansOfType(PebbleReactiveViewResolver.class)).hasSize(1);
assertThat(this.reactiveWebContext.getBeansOfType(PebbleViewResolver.class)).isEmpty();
}

private void loadWithServlet(Class<?> config) {
this.webContext = new AnnotationConfigServletWebApplicationContext();
TestPropertyValues.of("pebble.prefix=classpath:/templates/").applyTo(this.webContext);
TestPropertyValues.of("pebble.defaultLocale=zh").applyTo(this.webContext);
TestPropertyValues.of("pebble.strictVariables=true").applyTo(this.webContext);
TestPropertyValues.of("pebble.greedyMatchMethod=true").applyTo(this.webContext);
if (config != null) {
this.webContext.register(config);
}
Expand All @@ -80,6 +158,9 @@ private void loadWithServlet(Class<?> config) {
private void loadWithReactive(Class<?> config) {
this.reactiveWebContext = new AnnotationConfigReactiveWebApplicationContext();
TestPropertyValues.of("pebble.prefix=classpath:/templates/").applyTo(this.reactiveWebContext);
TestPropertyValues.of("pebble.defaultLocale=zh").applyTo(this.reactiveWebContext);
TestPropertyValues.of("pebble.strictVariables=true").applyTo(this.reactiveWebContext);
TestPropertyValues.of("pebble.greedyMatchMethod=true").applyTo(this.reactiveWebContext);
if (config != null) {
this.reactiveWebContext.register(config);
}
Expand All @@ -98,7 +179,7 @@ protected static class CustomPebbleEngineCompilerConfiguration {

@Bean
public PebbleEngine pebbleEngine() {
return new PebbleEngine.Builder().defaultLocale(DEFAULT_LOCALE).build();
return new PebbleEngine.Builder().defaultLocale(CUSTOM_LOCALE).build();
}

@Bean
Expand All @@ -107,4 +188,13 @@ public SpringExtension customSpringExtension(MessageSource messageSource) {
}
}

@Configuration(proxyBeanMethods = false)
protected static class CustomMethodAccessValidatorConfiguration {

@Bean
public MethodAccessValidator methodAccessValidator() {
return new NoOpMethodAccessValidator();
}
}

}
2 changes: 1 addition & 1 deletion pebble-spring/pebble-spring5/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<properties>
<servlet-api.version>3.1.0</servlet-api.version>
<spring-framework.version>5.2.1.RELEASE</spring-framework.version>
<spring-framework.version>5.2.6.RELEASE</spring-framework.version>
<mockito-junit-jupiter.version>3.1.0</mockito-junit-jupiter.version>
<junit-jupiter.version>5.5.2</junit-jupiter.version>
</properties>
Expand Down
Loading

0 comments on commit d967910

Please sign in to comment.