Skip to content

Commit

Permalink
fix: missing ServerWebExchange in plugin template processor extension (
Browse files Browse the repository at this point in the history
…halo-dev#6877)

#### What type of PR is this?
/kind bug
/area core
/milestone 2.20.x

#### What this PR does / why we need it:
修复由 halo-dev#6680 导致的插件模板处理扩展中无法获取到请求上下文的问题

halo-dev#6680 修复了插件可以在模板处理扩展中通过请求上下文获取到 Halo 的 ApplicationContext 的问题
但这也引入了新的问题就是导致模板处理扩展无法获取到请求上下文,此 PR 通过判断传递给插件的 ITemplateContext 是否为 IWebContext,如果是则包装为 SecureTemplateWebContext 传递给插件,以解决此问题

#### Which issue(s) this PR fixes:
Fixes halo-dev#6875

#### Does this PR introduce a user-facing change?
```release-note
修复插件模板处理扩展中无法获取到请求上下文的问题
```
  • Loading branch information
guqing authored Oct 16, 2024
1 parent ecc7c07 commit c577deb
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag,
structureHandler.replaceWith("", false);
return;
}
commentWidget.render(new SecureTemplateContext(context), tag, structureHandler);
commentWidget.render(SecureTemplateContextWrapper.wrap(context), tag, structureHandler);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected void doProcess(ITemplateContext context, IModel model,
// apply processors to modelToInsert
getTemplateHeadProcessors(context)
.concatMap(processor -> processor.process(
new SecureTemplateContext(context), modelToInsert, structureHandler)
SecureTemplateContextWrapper.wrap(context), modelToInsert, structureHandler)
)
.then()
.block();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ private IProcessableElementTag handleElementTag(
var context = getContext();
for (ElementTagPostProcessor elementTagPostProcessor : postProcessors) {
tagProcessorChain = tagProcessorChain.flatMap(
tag -> elementTagPostProcessor.process(new SecureTemplateContext(context), tag)
tag -> elementTagPostProcessor.process(
SecureTemplateContextWrapper.wrap(context), tag)
.defaultIfEmpty(tag)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package run.halo.app.theme.dialect;

import org.thymeleaf.context.Contexts;
import org.thymeleaf.context.ITemplateContext;

/**
* Wrap the delegate template context to a secure template context according to whether it is a
* WebContext.
*
* @author guqing
* @since 2.20.4
*/
public class SecureTemplateContextWrapper {

/**
* Wrap the delegate template context to a secure template context.
*/
static SecureTemplateContext wrap(ITemplateContext delegate) {
if (Contexts.isWebContext(delegate)) {
return new SecureTemplateWebContext(delegate);
}
return new SecureTemplateContext(delegate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package run.halo.app.theme.dialect;

import org.springframework.context.ApplicationContext;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.context.IWebContext;
import org.thymeleaf.web.IWebExchange;

/**
* Secure template web context.
* <p>It's used to prevent some dangerous variables such as {@link ApplicationContext} from being
* accessed.
*
* @author guqing
* @see SecureTemplateContext
* @since 2.20.4
*/
class SecureTemplateWebContext extends SecureTemplateContext implements IWebContext {
private final IWebContext delegate;

/**
* The delegate must be an instance of IWebContext to create a SecureTemplateWebContext.
*/
public SecureTemplateWebContext(ITemplateContext delegate) {
super(delegate);
if (delegate instanceof IWebContext webContext) {
this.delegate = webContext;
} else {
throw new IllegalArgumentException("The delegate must be an instance of IWebContext");
}
}

@Override
public IWebExchange getExchange() {
return delegate.getExchange();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag,

getTemplateFooterProcessors(context)
.concatMap(processor -> processor.process(
new SecureTemplateContext(context), tag, structureHandler, modelToInsert)
SecureTemplateContextWrapper.wrap(context), tag, structureHandler, modelToInsert)
)
.then()
.block();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ void shouldHandleStandaloneElementIfNoElementTagProcessors(
void shouldHandleStandaloneElementIfOneElementTagProcessorProvided() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IStandaloneElementTag.class);
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
when(processor.process(SecureTemplateContextWrapper.wrap(templateContext),
standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand All @@ -97,7 +98,8 @@ void shouldHandleStandaloneElementIfOneElementTagProcessorProvided() {
void shouldHandleStandaloneElementIfTagTypeChanged() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IStandaloneElementTag.class);
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
when(processor.process(SecureTemplateContextWrapper.wrap(templateContext),
standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand All @@ -114,9 +116,10 @@ void shouldHandleStandaloneElementIfMoreElementTagProcessorsProvided() {
var processor2 = mock(ElementTagPostProcessor.class);
var newTag1 = mock(IStandaloneElementTag.class);
var newTag2 = mock(IStandaloneElementTag.class);
when(processor1.process(new SecureTemplateContext(templateContext), standaloneElementTag))
when(processor1.process(SecureTemplateContextWrapper.wrap(templateContext),
standaloneElementTag))
.thenReturn(Mono.just(newTag1));
when(processor2.process(new SecureTemplateContext(templateContext), newTag1))
when(processor2.process(SecureTemplateContextWrapper.wrap(templateContext), newTag1))
.thenReturn(Mono.just(newTag2));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor1, processor2));
Expand All @@ -131,7 +134,8 @@ void shouldHandleStandaloneElementIfMoreElementTagProcessorsProvided() {
void shouldNotHandleIfProcessedTagTypeChanged() {
var processor = mock(ElementTagPostProcessor.class);
var newTag = mock(IOpenElementTag.class);
when(processor.process(new SecureTemplateContext(templateContext), standaloneElementTag))
when(processor.process(SecureTemplateContextWrapper.wrap(templateContext),
standaloneElementTag))
.thenReturn(Mono.just(newTag));
when(extensionGetter.getExtensionList(ElementTagPostProcessor.class))
.thenReturn(List.of(processor));
Expand Down

0 comments on commit c577deb

Please sign in to comment.