diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index 96eb6b765..d818a85f8 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -271,4 +271,10 @@ public final class StrutsConstants { /** Security firewall **/ public static final String STRUTS_SECURITY_GATE = "struts.securityGate"; + /** enables action: prefix **/ + public static final String STRUTS_MAPPER_ACTION_PREFIX_ENABLED = "struts.mapper.action.prefix.enabled"; + + /** enables access to actions in other namespaces than current with action: prefix **/ + public static final String STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES = "struts.mapper.action.prefix.crossNamespaces"; + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java index e774a48c5..3657bd9fa 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapper.java @@ -33,7 +33,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.struts2.RequestUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsConstants; -import org.apache.struts2.dispatcher.ServletDispatcherResult; import org.apache.struts2.util.PrefixTrie; import javax.servlet.http.HttpServletRequest; @@ -115,13 +114,14 @@ public class DefaultActionMapper implements ActionMapper { protected static final String METHOD_PREFIX = "method:"; protected static final String ACTION_PREFIX = "action:"; - private static final String STRUTS2_ACTION_PREFIX_PARSED = "_struts2_action_prefix_parsed"; protected boolean allowDynamicMethodCalls = false; protected boolean allowSlashesInActionNames = false; protected boolean alwaysSelectFullNamespace = false; protected PrefixTrie prefixTrie = null; protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*"); + private boolean allowActionPrefix = false; + private boolean allowActionCrossNamespaceAccess = false; protected List extensions = new ArrayList() {{ add("action"); @@ -134,7 +134,7 @@ public class DefaultActionMapper implements ActionMapper { prefixTrie = new PrefixTrie() { { put(METHOD_PREFIX, new ParameterAction() { - public void execute(String key, ActionMapping mapping, HttpServletRequest request) { + public void execute(String key, ActionMapping mapping) { if (allowDynamicMethodCalls) { mapping.setMethod(key.substring(METHOD_PREFIX.length())); } @@ -142,9 +142,8 @@ public class DefaultActionMapper implements ActionMapper { }); put(ACTION_PREFIX, new ParameterAction() { - public void execute(final String key, ActionMapping mapping, HttpServletRequest request) { - if (request != null && request.getAttribute(STRUTS2_ACTION_PREFIX_PARSED) == null) { - request.setAttribute(STRUTS2_ACTION_PREFIX_PARSED, true); + public void execute(final String key, ActionMapping mapping) { + if (allowActionPrefix) { String name = key.substring(ACTION_PREFIX.length()); if (allowDynamicMethodCalls) { int bang = name.indexOf('!'); @@ -155,11 +154,17 @@ public class DefaultActionMapper implements ActionMapper { } } String actionName = cleanupActionName(name); - mapping.setName(actionName); - if (getDefaultExtension() != null) { - actionName = actionName + "." + getDefaultExtension(); + if (allowSlashesInActionNames && !allowActionCrossNamespaceAccess) { + if (actionName.startsWith("/")) { + actionName = actionName.substring(1); + } } - mapping.setResult(new ServletDispatcherResult(actionName)); + if (!allowSlashesInActionNames && !allowActionCrossNamespaceAccess) { + if (actionName.lastIndexOf("/") != -1) { + actionName = actionName.substring(actionName.lastIndexOf("/") + 1); + } + } + mapping.setName(actionName); } } }); @@ -199,6 +204,16 @@ public class DefaultActionMapper implements ActionMapper { this.allowedActionNames = Pattern.compile(allowedActionNames); } + @Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_ENABLED) + public void setAllowActionPrefix(String allowActionPrefix) { + this.allowActionPrefix = "true".equalsIgnoreCase(allowActionPrefix); + } + + @Inject(value = StrutsConstants.STRUTS_MAPPER_ACTION_PREFIX_CROSSNAMESPACES) + public void setAllowActionCrossNamespaceAccess(String allowActionCrossNamespaceAccess) { + this.allowActionCrossNamespaceAccess = "true".equalsIgnoreCase(allowActionCrossNamespaceAccess); + } + @Inject public void setContainer(Container container) { this.container = container; @@ -291,7 +306,7 @@ public class DefaultActionMapper implements ActionMapper { if (!uniqueParameters.contains(key)) { ParameterAction parameterAction = (ParameterAction) prefixTrie.get(key); if (parameterAction != null) { - parameterAction.execute(key, mapping, request); + parameterAction.execute(key, mapping); uniqueParameters.add(key); break; } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ParameterAction.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ParameterAction.java index a3c7bdf26..e03b821ca 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/ParameterAction.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/ParameterAction.java @@ -21,8 +21,6 @@ package org.apache.struts2.dispatcher.mapper; -import javax.servlet.http.HttpServletRequest; - /** * Defines a parameter action prefix. This is executed when the configured prefix key is matched in a parameter * name, allowing the implementation to manipulate the action mapping accordingly. For example, if the "action:foo" @@ -32,5 +30,7 @@ import javax.servlet.http.HttpServletRequest; * @since 2.1.0 */ public interface ParameterAction { - void execute(String key, ActionMapping mapping, HttpServletRequest request); + + void execute(String key, ActionMapping mapping); + } diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index 2d2ad1199..1da53b1f4 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -116,6 +116,12 @@ struts.enable.DynamicMethodInvocation = false ### "/foo/save". struts.enable.SlashesInActionNames = false +### Disables support for action: prefix +struts.mapper.action.prefix.enabled = false + +### Blocks access to actions in other namespace than current with action: prefix +struts.mapper.action.prefix.crossNamespaces = false + ### use alternative syntax that requires %{} in most places ### to evaluate expressions for String attributes for tags struts.tag.altSyntax=true diff --git a/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java b/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java index 93b5a9a86..00703404f 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/mapper/DefaultActionMapperTest.java @@ -34,7 +34,6 @@ import org.apache.struts2.StrutsTestCase; import org.apache.struts2.dispatcher.StrutsResultSupport; import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest; -import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -400,7 +399,7 @@ public class DefaultActionMapperTest extends StrutsTestCase { // === test special prefix === // =========================== - public void testActionPrefix() throws Exception { + public void testActionPrefixWhenDisabled() throws Exception { Map parameterMap = new HashMap(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); @@ -411,9 +410,89 @@ public class DefaultActionMapperTest extends StrutsTestCase { DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + assertEquals("someServletPath", actionMapping.getName()); + } + + public void testActionPrefixWhenEnabled() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setParameterMap(parameterMap); + request.setupGetServletPath("/someServletPath.action"); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + assertEquals("myAction", actionMapping.getName()); } + public void testActionPrefixWhenSlashesAndCrossNamespaceDisabled() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setParameterMap(parameterMap); + request.setupGetServletPath("/someServletPath.action"); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); + defaultActionMapper.setSlashesInActionNames("true"); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + assertEquals("my/Action", actionMapping.getName()); + } + + public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespaceDisabled() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setParameterMap(parameterMap); + request.setupGetServletPath("/someServletPath.action"); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); + defaultActionMapper.setSlashesInActionNames("false"); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + assertEquals("Action", actionMapping.getName()); + } + + public void testActionPrefixWhenSlashesButSlashesDisabledAndCrossNamespace() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "my/Action", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setParameterMap(parameterMap); + request.setupGetServletPath("/someServletPath.action"); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); + defaultActionMapper.setAllowActionCrossNamespaceAccess("true"); + defaultActionMapper.setSlashesInActionNames("false"); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + assertEquals("my/Action", actionMapping.getName()); + } + + public void testActionPrefixWhenCrossNamespace() throws Exception { + Map parameterMap = new HashMap(); + parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "/my/Action", ""); + + StrutsMockHttpServletRequest request = new StrutsMockHttpServletRequest(); + request.setParameterMap(parameterMap); + request.setupGetServletPath("/someServletPath.action"); + + DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); + defaultActionMapper.setAllowActionCrossNamespaceAccess("true"); + ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); + + assertEquals("/my/Action", actionMapping.getName()); + } + public void testActionPrefix_fromImageButton() throws Exception { Map parameterMap = new HashMap(); parameterMap.put(DefaultActionMapper.ACTION_PREFIX + "myAction", ""); @@ -425,6 +504,7 @@ public class DefaultActionMapperTest extends StrutsTestCase { request.setupGetServletPath("/someServletPath.action"); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); assertEquals("myAction", actionMapping.getName()); @@ -440,6 +520,7 @@ public class DefaultActionMapperTest extends StrutsTestCase { request.setupGetServletPath("/someServletPath.action"); DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); + defaultActionMapper.setAllowActionPrefix("true"); ActionMapping actionMapping = defaultActionMapper.getMapping(request, configManager); assertEquals("myAction", actionMapping.getName()); @@ -539,7 +620,7 @@ public class DefaultActionMapperTest extends StrutsTestCase { DefaultActionMapper defaultActionMapper = new DefaultActionMapper(); defaultActionMapper.addParameterAction("foo", new ParameterAction() { - public void execute(String key, ActionMapping mapping, HttpServletRequest request) { + public void execute(String key, ActionMapping mapping) { mapping.setName("myAction"); } }); diff --git a/src/site/resources/archetype-catalog.xml b/src/site/resources/archetype-catalog.xml index 62d13357f..b4b786a1f 100644 --- a/src/site/resources/archetype-catalog.xml +++ b/src/site/resources/archetype-catalog.xml @@ -7,42 +7,42 @@ org.apache.struts struts2-archetype-blank - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Blank org.apache.struts struts2-archetype-convention - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Blank Convention org.apache.struts struts2-archetype-dbportlet - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Database Portlet org.apache.struts struts2-archetype-plugin - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Plugin org.apache.struts struts2-archetype-portlet - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Portlet org.apache.struts struts2-archetype-starter - 2.3.15 + 2.3.15.3 http://repo1.maven.org/maven2/ Struts 2 Archetypes - Starter