mirror of
https://github.com/apache/struts.git
synced 2026-08-06 07:06:58 +00:00
WW-5535 fix(core): enforce class-level HTTP method annotations for wildcard-resolved unannotated methods (#1693)
The WW-5535 change to DefaultActionProxy.resolveMethod() (which made
wildcard-resolved methods report isMethodSpecified()=true) interacted
with HttpMethodInterceptor's if/else-if so that the class-level
annotation branch became unreachable when the resolved method carried
no method-level annotation:
if (isMethodSpecified()) {
if (method has annotation) return doIntercept(method);
// unannotated method falls through silently
} else if (class has annotation) {
return doIntercept(class); // never reached when methodSpecified=true
}
Convert the else-if to a standalone if so the class-level check is
always evaluated as a fallback. Method-level annotations still take
precedence — they are checked first and return early.
Adds three tests:
- testWildcardResolvedUnannotatedMethodRespectsClassLevelAnnotation:
GET on a wildcard-resolved unannotated method is rejected when the
class is @AllowedHttpMethod(POST).
- testWildcardResolvedUnannotatedMethodAllowsPostWithClassLevelAnnotation:
POST on the same configuration succeeds.
- testWildcardResolvedExecuteRejectsGetThroughRealProxy: end-to-end
via a real DefaultActionProxy with <action name="Wild-*" method="{1}">,
resolving to ActionSupport.execute().
This commit is contained in:
+2
-1
@@ -90,7 +90,8 @@ public class HttpMethodInterceptor extends AbstractInterceptor {
|
||||
invocation.getProxy().getMethod(), AllowedHttpMethod.class.getSimpleName(), request.getMethod());
|
||||
return doIntercept(invocation, method);
|
||||
}
|
||||
} else if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
|
||||
}
|
||||
if (AnnotationUtils.isAnnotatedBy(action.getClass(), HTTP_METHOD_ANNOTATIONS)) {
|
||||
LOG.debug("Action: {} annotated with: {}, checking if request: {} meets allowed methods!",
|
||||
action, AllowedHttpMethod.class.getSimpleName(), request.getMethod());
|
||||
return doIntercept(invocation, action.getClass());
|
||||
|
||||
+71
@@ -19,13 +19,17 @@
|
||||
package org.apache.struts2.interceptor.httpmethod;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionProxy;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.mock.MockActionProxy;
|
||||
import org.apache.struts2.HttpMethodsTestAction;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
import org.apache.struts2.TestAction;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpMethodInterceptorTest extends StrutsInternalTestCase {
|
||||
|
||||
private HttpMethodInterceptor interceptor;
|
||||
@@ -254,6 +258,73 @@ public class HttpMethodInterceptorTest extends StrutsInternalTestCase {
|
||||
assertEquals(HttpMethod.POST, action.getHttpMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression for wildcard-resolved methods with no method-level HTTP annotation:
|
||||
* a class-level {@code @AllowedHttpMethod(POST)} must still cause GET to be rejected.
|
||||
* Previously the interceptor's {@code if/else-if} structure made the class-level
|
||||
* branch unreachable when {@code isMethodSpecified()=true} and the resolved method
|
||||
* carried no annotation of its own.
|
||||
*/
|
||||
public void testWildcardResolvedUnannotatedMethodRespectsClassLevelAnnotation() throws Exception {
|
||||
HttpMethodsTestAction action = new HttpMethodsTestAction();
|
||||
prepareActionInvocation(action);
|
||||
actionProxy.setMethod("execute");
|
||||
actionProxy.setMethodSpecified(true);
|
||||
|
||||
prepareRequest("get");
|
||||
|
||||
String resultName = interceptor.intercept(invocation);
|
||||
|
||||
assertEquals("bad-request", resultName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counterpart to the above: POST against a wildcard-resolved unannotated method must succeed
|
||||
* when the class allows POST via {@code @AllowedHttpMethod(POST)}.
|
||||
*/
|
||||
public void testWildcardResolvedUnannotatedMethodAllowsPostWithClassLevelAnnotation() throws Exception {
|
||||
HttpMethodsTestAction action = new HttpMethodsTestAction();
|
||||
prepareActionInvocation(action);
|
||||
actionProxy.setMethod("execute");
|
||||
actionProxy.setMethodSpecified(true);
|
||||
invocation.setResultCode("success");
|
||||
|
||||
prepareRequest("post");
|
||||
|
||||
String resultName = interceptor.intercept(invocation);
|
||||
|
||||
assertEquals("success", resultName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exercises the full wildcard resolution path through a real {@link com.opensymphony.xwork2.DefaultActionProxy}.
|
||||
* <p>
|
||||
* Config (from xwork-test-allowed-methods.xml):
|
||||
* {@code <action name="Wild-*" class="HttpMethodsTestAction" method="{1}">}.
|
||||
* URL {@code Wild-execute} resolves to {@code ActionSupport.execute()} — no method-level
|
||||
* HTTP annotation. {@code HttpMethodsTestAction} carries class-level
|
||||
* {@code @AllowedHttpMethod(POST)}, so GET must be rejected end-to-end.
|
||||
*/
|
||||
public void testWildcardResolvedExecuteRejectsGetThroughRealProxy() throws Exception {
|
||||
loadConfigurationProviders(new StrutsXmlConfigurationProvider(
|
||||
"com/opensymphony/xwork2/config/providers/xwork-test-allowed-methods.xml"));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/Wild-execute");
|
||||
Map<String, Object> extraContext = ActionContext.of()
|
||||
.withServletRequest(request)
|
||||
.getContextMap();
|
||||
|
||||
ActionProxy proxy = actionProxyFactory.createActionProxy("", "Wild-execute", null, extraContext);
|
||||
|
||||
assertEquals("execute", proxy.getMethod());
|
||||
assertTrue("Wildcard-resolved method must report isMethodSpecified()=true", proxy.isMethodSpecified());
|
||||
|
||||
HttpMethodInterceptor realInterceptor = new HttpMethodInterceptor();
|
||||
String result = realInterceptor.intercept(proxy.getInvocation());
|
||||
|
||||
assertEquals("bad-request", result);
|
||||
}
|
||||
|
||||
private void prepareActionInvocation(Object action) {
|
||||
interceptor = new HttpMethodInterceptor();
|
||||
invocation = new MockActionInvocation();
|
||||
|
||||
Reference in New Issue
Block a user