From bcfa8113acff7e7d408922ce8cb51844395ff24f Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 9 Nov 2022 16:20:54 +0100 Subject: [PATCH] WW-4173 Introduces a dedicated interface to allow conditionally executing a given interceptor --- .../xwork2/DefaultActionInvocation.java | 17 +++- .../interceptor/AbstractInterceptor.java | 12 ++- .../interceptor/ConditionalInterceptor.java | 39 +++++++++ .../xwork2/interceptor/Interceptor.java | 8 -- .../struts2/interceptor/CoepInterceptor.java | 10 +-- .../struts2/interceptor/CoopInterceptor.java | 9 +- .../interceptor/FetchMetadataInterceptor.java | 4 - .../interceptor/csp/CspInterceptor.java | 13 +-- .../xwork2/DefaultActionInvocationTest.java | 82 +++++++++++-------- .../interceptor/CoepInterceptorTest.java | 9 -- .../interceptor/CoopInterceptorTest.java | 9 -- .../interceptor/CspInterceptorTest.java | 11 --- .../FetchMetadataInterceptorTest.java | 8 -- 13 files changed, 115 insertions(+), 116 deletions(-) create mode 100644 core/src/main/java/com/opensymphony/xwork2/interceptor/ConditionalInterceptor.java diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java index c1e049dfa..772e0adb0 100644 --- a/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java +++ b/core/src/main/java/com/opensymphony/xwork2/DefaultActionInvocation.java @@ -24,6 +24,7 @@ import com.opensymphony.xwork2.config.entities.InterceptorMapping; import com.opensymphony.xwork2.config.entities.ResultConfig; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.interceptor.ConditionalInterceptor; import com.opensymphony.xwork2.interceptor.Interceptor; import com.opensymphony.xwork2.interceptor.PreResultListener; import com.opensymphony.xwork2.interceptor.WithLazyParams; @@ -248,10 +249,10 @@ public class DefaultActionInvocation implements ActionInvocation { if (interceptor instanceof WithLazyParams) { interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext); } - if (interceptor.isDisabled(this)) { - LOG.debug("Interceptor: {} is disabled, skipping to next", interceptor.getClass().getSimpleName()); - resultCode = this.invoke(); + if (interceptor instanceof ConditionalInterceptor) { + resultCode = executeConditional((ConditionalInterceptor) interceptor); } else { + LOG.debug("Executing normal interceptor: {}", interceptorMapping.getName()); resultCode = interceptor.intercept(this); } } else { @@ -292,6 +293,16 @@ public class DefaultActionInvocation implements ActionInvocation { return resultCode; } + protected String executeConditional(ConditionalInterceptor conditionalInterceptor) throws Exception { + if (conditionalInterceptor.shouldIntercept(this)) { + LOG.debug("Executing conditional interceptor: {}", conditionalInterceptor.getClass().getSimpleName()); + return conditionalInterceptor.intercept(this); + } else { + LOG.debug("Interceptor: {} is disabled, skipping to next", conditionalInterceptor.getClass().getSimpleName()); + return this.invoke(); + } + } + public String invokeActionOnly() throws Exception { return invokeAction(getAction(), proxy.getConfig()); } diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/AbstractInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/AbstractInterceptor.java index efa009052..21e459c29 100644 --- a/core/src/main/java/com/opensymphony/xwork2/interceptor/AbstractInterceptor.java +++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/AbstractInterceptor.java @@ -23,7 +23,7 @@ import com.opensymphony.xwork2.ActionInvocation; /** * Provides default implementations of optional lifecycle methods */ -public abstract class AbstractInterceptor implements Interceptor { +public abstract class AbstractInterceptor implements ConditionalInterceptor { private boolean disabled; @@ -44,12 +44,18 @@ public abstract class AbstractInterceptor implements Interceptor { */ public abstract String intercept(ActionInvocation invocation) throws Exception; + /** + * Allows to skip executing a given interceptor, just define {@code true} + * or use other way to override interceptor's parameters, see + * docs. + * @param disable if set to true, execution of a given interceptor will be skipped. + */ public void setDisabled(String disable) { this.disabled = Boolean.parseBoolean(disable); } @Override - public boolean isDisabled(ActionInvocation invocation) { - return this.disabled; + public boolean shouldIntercept(ActionInvocation invocation) { + return !this.disabled; } } diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ConditionalInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ConditionalInterceptor.java new file mode 100644 index 000000000..12752fce8 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ConditionalInterceptor.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.opensymphony.xwork2.interceptor; + +import com.opensymphony.xwork2.ActionInvocation; + +/** + * A marking interface, when implemented allows to conditionally execute a given interceptor + * within the current action invocation. + * + * @since Struts 6.1.1 + */ +public interface ConditionalInterceptor extends Interceptor { + + /** + * Determines if a given interceptor should be executed in the current processing of action invocation. + * + * @param invocation current {@link ActionInvocation} to determine if the interceptor should be executed + * @return true if the given interceptor should be included in the current action invocation + * @since 6.1.1 + */ + boolean shouldIntercept(ActionInvocation invocation); +} diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java index 3488314d2..cafa08fc0 100644 --- a/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java +++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/Interceptor.java @@ -219,12 +219,4 @@ public interface Interceptor extends Serializable { */ String intercept(ActionInvocation invocation) throws Exception; - /** - * Allows to disable processing a given interceptor - * - * @param invocation current {@link ActionInvocation} to determine if the interceptor should be executed - * @return true if the given interceptor should be skipped - * @since 6.1.0 - */ - boolean isDisabled(ActionInvocation invocation); } diff --git a/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java index 6d550c19f..850556e32 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java @@ -51,20 +51,12 @@ public class CoepInterceptor extends AbstractInterceptor implements PreResultLis @Override public String intercept(ActionInvocation invocation) throws Exception { - if (this.isDisabled(invocation)) { - LOG.trace("COEP interceptor has been disabled"); - } else { - invocation.addPreResultListener(this); - } + invocation.addPreResultListener(this); return invocation.invoke(); } @Override public void beforeResult(ActionInvocation invocation, String resultCode) { - if (this.isDisabled(invocation)) { - return; - } - HttpServletRequest req = invocation.getInvocationContext().getServletRequest(); final String path = req.getContextPath(); diff --git a/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java index 9827ceb13..123170baf 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java @@ -53,19 +53,12 @@ public class CoopInterceptor extends AbstractInterceptor implements PreResultLis @Override public String intercept(ActionInvocation invocation) throws Exception { - if (this.isDisabled(invocation)) { - LOG.trace("COOP interceptor has been disabled"); - } else { - invocation.addPreResultListener(this); - } + invocation.addPreResultListener(this); return invocation.invoke(); } @Override public void beforeResult(ActionInvocation invocation, String resultCode) { - if (this.isDisabled(invocation)) { - return; - } HttpServletRequest request = invocation.getInvocationContext().getServletRequest(); String path = request.getContextPath(); diff --git a/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java index 9a3583607..0f46206f2 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java @@ -62,10 +62,6 @@ public class FetchMetadataInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { - if (this.isDisabled(invocation)) { - LOG.trace("Fetch Metadata interceptor has been disabled"); - return invocation.invoke(); - } ActionContext context = invocation.getInvocationContext(); HttpServletRequest request = context.getServletRequest(); diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java index 38b196514..5bae4f543 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspInterceptor.java @@ -21,8 +21,6 @@ package org.apache.struts2.interceptor.csp; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.PreResultListener; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -41,24 +39,15 @@ import java.util.Optional; **/ public final class CspInterceptor extends AbstractInterceptor implements PreResultListener { - private static final Logger LOG = LogManager.getLogger(CspInterceptor.class); - private final CspSettings settings = new DefaultCspSettings(); @Override public String intercept(ActionInvocation invocation) throws Exception { - if (this.isDisabled(invocation)) { - LOG.trace("CSP interceptor has been disabled"); - } else { - invocation.addPreResultListener(this); - } + invocation.addPreResultListener(this); return invocation.invoke(); } public void beforeResult(ActionInvocation invocation, String resultCode) { - if (this.isDisabled(invocation)) { - return; - } HttpServletRequest request = invocation.getInvocationContext().getServletRequest(); HttpServletResponse response = invocation.getInvocationContext().getServletResponse(); settings.addCspHeaders(request, response); diff --git a/core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java b/core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java index 0d2ca8918..6ad6b8422 100644 --- a/core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/DefaultActionInvocationTest.java @@ -22,7 +22,7 @@ import com.opensymphony.xwork2.config.entities.ActionConfig; import com.opensymphony.xwork2.config.entities.InterceptorMapping; import com.opensymphony.xwork2.config.entities.ResultConfig; import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider; -import com.opensymphony.xwork2.interceptor.PreResultListener; +import com.opensymphony.xwork2.interceptor.Interceptor; import com.opensymphony.xwork2.mock.MockActionProxy; import com.opensymphony.xwork2.mock.MockInterceptor; import com.opensymphony.xwork2.mock.MockResult; @@ -65,10 +65,18 @@ public class DefaultActionInvocationTest extends XWorkTestCase { interceptorMappings.add(new InterceptorMapping("test2", mockInterceptor2)); mockInterceptor2.setFoo("test2"); mockInterceptor2.setExpectedFoo("test2"); - MockInterceptor mockInterceptor3 = new MockInterceptor(); + MockInterceptor mockInterceptor3 = new MockInterceptor() { + @Override + public boolean shouldIntercept(ActionInvocation invocation) { + return false; + } + }; interceptorMappings.add(new InterceptorMapping("test3", mockInterceptor3)); - mockInterceptor3.setFoo("test3"); - mockInterceptor3.setExpectedFoo("test3"); + + MockInterceptor mockInterceptor4 = new MockInterceptor(); + interceptorMappings.add(new InterceptorMapping("test4", mockInterceptor4)); + mockInterceptor4.setFoo("test4"); + mockInterceptor4.setExpectedFoo("test4"); DefaultActionInvocation defaultActionInvocation = new DefaultActionInvocationTester(interceptorMappings); container.inject(defaultActionInvocation); @@ -78,7 +86,8 @@ public class DefaultActionInvocationTest extends XWorkTestCase { defaultActionInvocation.invoke(); assertTrue(mockInterceptor1.isExecuted()); assertTrue(mockInterceptor2.isExecuted()); - assertTrue(mockInterceptor3.isExecuted()); + assertFalse(mockInterceptor3.isExecuted()); + assertTrue(mockInterceptor4.isExecuted()); assertTrue(defaultActionInvocation.isExecuted()); try { defaultActionInvocation.setResultCode(""); @@ -92,6 +101,33 @@ public class DefaultActionInvocationTest extends XWorkTestCase { } } + public void testInvokeSimpleInterceptor() throws Exception { + List interceptorMappings = new ArrayList<>(); + Interceptor interceptor1 = new Interceptor() { + @Override + public void destroy() { + } + + @Override + public void init() { + } + + @Override + public String intercept(ActionInvocation invocation) throws Exception { + return "done"; + } + }; + interceptorMappings.add(new InterceptorMapping("test1", interceptor1)); + + DefaultActionInvocation defaultActionInvocation = new DefaultActionInvocationTester(interceptorMappings); + container.inject(defaultActionInvocation); + defaultActionInvocation.stack = container.getInstance(ValueStackFactory.class).createValueStack(); + + String result = defaultActionInvocation.invoke(); + assertTrue(defaultActionInvocation.isExecuted()); + assertEquals("done", result); + } + public void testInvokeWithDisabledInterceptors() throws Exception { // given List interceptorMappings = new ArrayList<>(); @@ -147,7 +183,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase { assertEquals("success", result); } - public void testInvokingMissingMethod() throws Exception { + public void testInvokingMissingMethod() { // given DefaultActionInvocation dai = new DefaultActionInvocation(ActionContext.getContext().getContextMap(), false); container.inject(dai); @@ -348,7 +384,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase { } public void testInvokeWithAsyncManager() throws Exception { - DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap(), false); + DefaultActionInvocation dai = new DefaultActionInvocation(new HashMap<>(), false); dai.stack = container.getInstance(ValueStackFactory.class).createValueStack(); final Semaphore lock = new Semaphore(1); @@ -377,29 +413,14 @@ public class DefaultActionInvocationTest extends XWorkTestCase { } }); - dai.action = new Callable>() { - @Override - public Callable call() throws Exception { - return new Callable() { - @Override - public String call() throws Exception { - return "success"; - } - }; - } - }; + dai.action = (Callable>) () -> (Callable) () -> "success"; MockActionProxy actionProxy = new MockActionProxy(); actionProxy.setMethod("call"); dai.proxy = actionProxy; final boolean[] preResultExecuted = new boolean[1]; - dai.addPreResultListener(new PreResultListener() { - @Override - public void beforeResult(ActionInvocation invocation, String resultCode) { - preResultExecuted[0] = true; - } - }); + dai.addPreResultListener((invocation, resultCode) -> preResultExecuted[0] = true); List interceptorMappings = new ArrayList<>(); MockInterceptor mockInterceptor1 = new MockInterceptor(); @@ -436,7 +457,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase { public void testActionEventListener() throws Exception { ActionProxy actionProxy = actionProxyFactory.createActionProxy("", - "ExceptionFoo", "exceptionMethod", new HashMap()); + "ExceptionFoo", "exceptionMethod", new HashMap<>()); DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation(); SimpleActionEventListener actionEventListener = new SimpleActionEventListener("prepared", "exceptionHandled"); @@ -461,8 +482,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase { } public void testActionChainResult() throws Exception { - ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null, - new HashMap()); + ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null, new HashMap<>()); DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation(); defaultActionInvocation.init(actionProxy); @@ -478,9 +498,8 @@ public class DefaultActionInvocationTest extends XWorkTestCase { assertTrue(result instanceof MockResult); } - public void testNoResultDefined() throws Exception { - ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null, - new HashMap()); + public void testNoResultDefined() { + ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "Foo", null, new HashMap<>()); DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation(); defaultActionInvocation.init(actionProxy); @@ -492,8 +511,7 @@ public class DefaultActionInvocationTest extends XWorkTestCase { } public void testNullResultPossible() throws Exception { - ActionProxy actionProxy = actionProxyFactory.createActionProxy("", - "NullFoo", "nullMethod", new HashMap()); + ActionProxy actionProxy = actionProxyFactory.createActionProxy("", "NullFoo", "nullMethod", new HashMap<>()); DefaultActionInvocation defaultActionInvocation = (DefaultActionInvocation) actionProxy.getInvocation(); defaultActionInvocation.init(actionProxy); diff --git a/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java index 1401951c2..72ea1a024 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java @@ -41,15 +41,6 @@ public class CoepInterceptorTest extends StrutsInternalTestCase { private final String HEADER_CONTENT = "require-corp"; - public void testDisabled() throws Exception { - interceptor.setDisabled("true"); - - interceptor.intercept(mai); - - String header = response.getHeader(COEP_ENFORCING_HEADER); - assertTrue("COEP is not disabled", Strings.isEmpty(header)); - } - public void testEnforcingHeader() throws Exception { interceptor.setEnforcingMode("true"); diff --git a/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java index 8e9e9d4ec..544e7b5d8 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java @@ -75,15 +75,6 @@ public class CoopInterceptorTest extends StrutsInternalTestCase { } } - public void testDisabled() throws Exception { - interceptor.setDisabled("true"); - - interceptor.intercept(mai); - - String header = response.getHeader(COOP_HEADER); - assertTrue("COOP is not disabled", Strings.isEmpty(header)); - } - @Override protected void setUp() throws Exception { super.setUp(); diff --git a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java index a091b93c7..5727a9dcf 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java @@ -145,17 +145,6 @@ public class CspInterceptorTest extends StrutsInternalTestCase { } } - public void testDisabled() throws Exception { - interceptor.setDisabled("true"); - - interceptor.intercept(mai); - - String header = response.getHeader(CSP_ENFORCE_HEADER); - assertTrue("CSP is not disabled", Strings.isEmpty(header)); - header = response.getHeader(CSP_REPORT_HEADER); - assertTrue("CSP is not disabled", Strings.isEmpty(header)); - } - public void checkHeader(String reportUri, String enforcingMode) { String expectedCspHeader; if (Strings.isEmpty(reportUri)) { diff --git a/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java index 4b8403c47..6426ebeaa 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java @@ -261,12 +261,4 @@ public class FetchMetadataInterceptorTest extends XWorkTestCase { assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedGlobalActionConfig.getName() + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); } - public void testDisabled() throws Exception { - interceptor.setDisabled("true"); - - interceptor.intercept(mai); - - String header = response.getHeader(VARY_HEADER); - assertTrue("Fetch Metadata is not disabled", Strings.isEmpty(header)); - } }