Commits

Anonymous committed 0d36e86

XW-637 Patches for WW-2203 prevented action parameters from other places than the request parameters from being included in ParameterAware map.
o added new config param to allow configuration of parameter overwrite mode.
o partialy applied patch submitted by Dale Newfield

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@1826e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (3)

src/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java

  * calling {@link ValueStack#setValue(String, Object)}, typically resulting in the values submitted in a form
  * request being applied to an action in the value stack. Note that the parameter map must contain a String key and
  * often containers a String[] for the value.
- * 
+ * <p/>
  * <p/> The interceptor takes one parameter named 'ordered'. When set to true action properties are guaranteed to be
  * set top-down which means that top action's properties are set first. Then it's subcomponents properties are set.
- * The reason for this order is to enable a 'factory' pattern. For example, let's assume that one has an action 
+ * The reason for this order is to enable a 'factory' pattern. For example, let's assume that one has an action
  * that contains a property named 'modelClass' that allows to choose what is the underlying implementation of model.
  * By assuring that modelClass property is set before any model properties are set, it's possible to choose model
- * implementation during action.setModelClass() call. Similiarily it's possible to use action.setPrimaryKey() 
- * property set call to actually load the model class from persistent storage. Without any assumption on parameter 
+ * implementation during action.setModelClass() call. Similiarily it's possible to use action.setPrimaryKey()
+ * property set call to actually load the model class from persistent storage. Without any assumption on parameter
  * order you have to use patterns like 'Preparable'.
- *  
+ * <p/>
  * <p/> Because parameter names are effectively OGNL statements, it is important that security be taken in to account.
  * This interceptor will not apply any values in the parameters map if the expression contains an assignment (=),
  * multiple expressions (,), or references any objects in the context (#). This is all done in the {@link
  * #acceptableName(String)} method. In addition to this method, if the action being invoked implements the {@link
  * ParameterNameAware} interface, the action will be consulted to determine if the parameter should be set.
- *
+ * <p/>
  * <p/> In addition to these restrictions, a flag ({@link ReflectionContextState#DENY_METHOD_EXECUTION}) is set such that
  * no methods are allowed to be invoked. That means that any expression such as <i>person.doSomething()</i> or
  * <i>person.getName()</i> will be explicitely forbidden. This is needed to make sure that your application is not
  * exposed to attacks by malicious users.
- *
+ * <p/>
  * <p/> While this interceptor is being invoked, a flag ({@link ReflectionContextState#CREATE_NULL_OBJECTS}) is turned
  * on to ensure that any null reference is automatically created - if possible. See the type conversion documentation
  * and the {@link InstantiatingNullHandler} javadocs for more information.
- *
+ * <p/>
  * <p/> Finally, a third flag ({@link XWorkConverter#REPORT_CONVERSION_ERRORS}) is set that indicates any errors when
  * converting the the values to their final data type (String[] -&gt; int) an unrecoverable error occured. With this
  * flag set, the type conversion errors will be reported in the action context. See the type conversion documentation
  * and the {@link XWorkConverter} javadocs for more information.
- *
+ * <p/>
  * <p/> If you are looking for detailed logging information about your parameters, turn on DEBUG level logging for this
  * interceptor. A detailed log of all the parameter keys and values will be reported.
- *
+ * <p/>
  * <p/>
  * <b>Note:</b> Since XWork 2.0.2, this interceptor extends {@link MethodFilterInterceptor}, therefore being
  * able to deal with excludeMethods / includeMethods parameters. See [Workflow Interceptor]
  * (class {@link DefaultWorkflowInterceptor}) for documentation and examples on how to use this feature.
- *
+ * <p/>
  * <!-- END SNIPPET: description -->
- *
+ * <p/>
  * <p/> <u>Interceptor parameters:</u>
- *
+ * <p/>
  * <!-- START SNIPPET: parameters -->
- *
+ * <p/>
  * <ul>
- *
+ * <p/>
  * <li>ordered - set to true if you want the top-down property setter behaviour</li>
- *
+ * <p/>
  * </ul>
- *
+ * <p/>
  * <!-- END SNIPPET: parameters -->
- *
+ * <p/>
  * <p/> <u>Extending the interceptor:</u>
- *
+ * <p/>
  * <!-- START SNIPPET: extending -->
- *
+ * <p/>
  * <p/> The best way to add behavior to this interceptor is to utilize the {@link ParameterNameAware} interface in your
  * actions. However, if you wish to apply a global rule that isn't implemented in your action, then you could extend
  * this interceptor and override the {@link #acceptableName(String)} method.
- *
+ * <p/>
  * <!-- END SNIPPET: extending -->
- *
+ * <p/>
  * <p/> <u>Example code:</u>
- *
+ * <p/>
  * <pre>
  * <!-- START SNIPPET: example -->
  * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
             }
         }
     }
-    
-    /** Compares based on number of '.' characters (fewer is higher) */
+
+    /**
+     * Compares based on number of '.' characters (fewer is higher)
+     */
     static final Comparator rbCollator = new Comparator() {
         public int compare(Object arg0, Object arg1) {
             String s1 = (String) arg0;
             String s2 = (String) arg1;
-            int l1=0, l2=0;
-            for( int i=s1.length()-1; i>=0; i--) {
-                if( s1.charAt(i) == '.') l1++;
+            int l1 = 0, l2 = 0;
+            for (int i = s1.length() - 1; i >= 0; i--) {
+                if (s1.charAt(i) == '.') l1++;
             }
-            for( int i=s2.length()-1; i>=0; i--) {
-                if( s2.charAt(i) == '.') l2++;
+            for (int i = s2.length() - 1; i >= 0; i--) {
+                if (s2.charAt(i) == '.') l2++;
             }
-            return l1 < l2 ? -1 : ( l2 < l1 ? 1 : s1.compareTo(s2));
+            return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
         }
     };
-    
+
     public String doIntercept(ActionInvocation invocation) throws Exception {
         Object action = invocation.getAction();
         if (!(action instanceof NoParameters)) {
             ActionContext ac = invocation.getInvocationContext();
-            final Map parameters = retrieveParametersFromContext(ac);
+            final Map parameters = retrieveParameters(ac);
 
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Setting params " + getParameterLogMap(parameters));
             }
 
             if (parameters != null) {
-            	Map contextMap = ac.getContextMap();
+                Map contextMap = ac.getContextMap();
                 try {
-                	ReflectionContextState.setCreatingNullObjects(contextMap, true);
-                	ReflectionContextState.setDenyMethodExecution(contextMap, true);
-                	ReflectionContextState.setReportingConversionErrors(contextMap, true);
+                    ReflectionContextState.setCreatingNullObjects(contextMap, true);
+                    ReflectionContextState.setDenyMethodExecution(contextMap, true);
+                    ReflectionContextState.setReportingConversionErrors(contextMap, true);
 
                     ValueStack stack = ac.getValueStack();
                     setParameters(action, stack, parameters);
                 } finally {
-                	ReflectionContextState.setCreatingNullObjects(contextMap, false);
-                	ReflectionContextState.setDenyMethodExecution(contextMap, false);
-                	ReflectionContextState.setReportingConversionErrors(contextMap, false);
+                    ReflectionContextState.setCreatingNullObjects(contextMap, false);
+                    ReflectionContextState.setDenyMethodExecution(contextMap, false);
+                    ReflectionContextState.setReportingConversionErrors(contextMap, false);
                 }
             }
         }
     }
 
     /**
-     * Gets the parameter map to apply from the context
+     * Gets the parameter map to apply from wherever appropriate
+     *
      * @param ac The action context
      * @return The parameter map to apply
      */
-    protected Map retrieveParametersFromContext(ActionContext ac) {
+    protected Map retrieveParameters(ActionContext ac) {
         return ac.getParameters();
     }
 
+
+    /**
+     * Adds the parameters into context's ParameterMap
+     *
+     * @param ac        The action context
+     * @param newParams The parameter map to apply
+     *                  <p/>
+     *                  In this class this is a no-op, since the parameters were fetched from the same location.
+     *                  In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
+     */
+    protected void addParametersToContext(ActionContext ac, Map newParams) {
+    }
+
     protected void setParameters(Object action, ValueStack stack, final Map parameters) {
         ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware)
                 ? (ParameterNameAware) action : null;
 
         Map params = null;
-        if( ordered ) {
+        Map acceptableParameters = null;
+        if (ordered) {
             params = new TreeMap(getOrderedComparator());
+            acceptableParameters = new TreeMap(getOrderedComparator());
             params.putAll(parameters);
         } else {
-            params = new TreeMap(parameters); 
+            params = new TreeMap(parameters);
+            acceptableParameters = new TreeMap();
         }
-        
+
         for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) {
             Map.Entry entry = (Map.Entry) iterator.next();
             String name = entry.getKey().toString();
                     || parameterNameAware.acceptableParameterName(name));
 
             if (acceptableName) {
-                Object value = entry.getValue();
-                try {
-                    stack.setValue(name, value);
-                } catch (RuntimeException e) {
-                    if (devMode) {
-                        String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
-                                e.getMessage()
-                        });
-                        LOG.error(developerNotification);
-                        if (action instanceof ValidationAware) {
-                            ((ValidationAware) action).addActionMessage(developerNotification);
-                        }
-                    } else {
-                        LOG.error("ParametersInterceptor - [setParameters]: Unexpected Exception caught setting '"+name+"' on '"+action.getClass()+": " + e.getMessage());
+                acceptableParameters.put(name, entry.getValue());
+            }
+        }
+
+        for (Iterator iterator = acceptableParameters.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String name = entry.getKey().toString();
+            Object value = entry.getValue();
+            try {
+                stack.setValue(name, value);
+            } catch (RuntimeException e) {
+                if (devMode) {
+                    String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
+                            e.getMessage()
+                    });
+                    LOG.error(developerNotification);
+                    if (action instanceof ValidationAware) {
+                        ((ValidationAware) action).addActionMessage(developerNotification);
                     }
+                } else {
+                    LOG.error("ParametersInterceptor - [setParameters]: Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage());
                 }
             }
         }
+        addParametersToContext(ActionContext.getContext(), acceptableParameters);
     }
 
     /**
      * Gets an instance of the comparator to use for the ordered sorting.  Override this
-     * method to customize the ordering of the parameters as they are set to the 
+     * method to customize the ordering of the parameters as they are set to the
      * action.
-     * 
+     *
      * @return A comparator to sort the parameters
      */
     protected Comparator getOrderedComparator() {
     }
 
     protected boolean acceptableName(String name) {
-        if ( isAccepted(name) && !isExcluded(name)) {
+        if (isAccepted(name) && !isExcluded(name)) {
             return true;
         }
         return false;
     }
-    
+
     protected boolean isAccepted(String paramName) {
         if (!this.acceptedParams.isEmpty()) {
             for (Pattern pattern : acceptedParams) {
 
     /**
      * Whether to order the parameters or not
-     * 
+     *
      * @return True to order
      */
     public boolean isOrdered() {
 
     /**
      * Set whether to order the parameters by object depth or not
-     * 
+     *
      * @param ordered True to order them
      */
     public void setOrdered(boolean ordered) {
         this.ordered = ordered;
     }
-    
+
     /**
      * Gets a set of regular expressions of parameters to remove
      * from the parameter map
-     * 
+     *
      * @return A set of compiled regular expression patterns
      */
     protected Set getExcludeParamsSet() {
     }
 
     /**
-     * Sets a comma-delimited list of regular expressions to match 
+     * Sets a comma-delimited list of regular expressions to match
      * parameters that should be removed from the parameter map.
-     * 
+     *
      * @param commaDelim A comma-delimited list of regular expressions
      */
     public void setExcludeParams(String commaDelim) {

src/java/com/opensymphony/xwork2/interceptor/StaticParametersInterceptor.java

 
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Collections;
+import java.util.TreeMap;
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ActionInvocation;
  * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
  *     &lt;interceptor-ref name="staticParams"&gt;
  *          &lt;param name="parse"&gt;true&lt;/param&gt;
+ *          &lt;param name="overwrite"&gt;false&lt;/param&gt;
  *     &lt;/interceptor-ref&gt;
  *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
  * &lt;/action&gt;
 public class StaticParametersInterceptor extends AbstractInterceptor {
 
     private boolean parse;
-    
+    private boolean overwrite;
+
     private static final Logger LOG = LoggerFactory.getLogger(StaticParametersInterceptor.class);
 
     public void setParse(String value) {
         this.parse = Boolean.valueOf(value).booleanValue();
     }
 
+    /**
+     * Overwrites already existing parameters from other sources.
+     * Static parameters are the successor over previously set parameters, if true.
+     *
+     * @param value
+     */
+    public void setOverwrite(String value) {
+        this.overwrite = Boolean.valueOf(value).booleanValue();
+    }
+
     public String intercept(ActionInvocation invocation) throws Exception {
         ActionConfig config = invocation.getProxy().getConfig();
         Object action = invocation.getAction();
         }
 
         if (parameters != null) {
-            final ValueStack stack = ActionContext.getContext().getValueStack();
+            ActionContext ac = ActionContext.getContext();
+            final ValueStack stack = ac.getValueStack();
 
             for (Iterator iterator = parameters.entrySet().iterator();
                  iterator.hasNext();) {
                 }
                 stack.setValue(entry.getKey().toString(), val);
             }
+            addParametersToContext(ac, parameters);
         }
         return invocation.invoke();
     }
+
+
+    /**
+     * @param ac The action context
+     * @return the parameters from the action mapping in the context.  If none found, returns
+     *         an empty map.
+     */
+    protected Map retrieveParameters(ActionContext ac) {
+        ActionConfig config = ac.getActionInvocation().getProxy().getConfig();
+        if (config != null) {
+            return config.getParams();
+        } else {
+            return Collections.EMPTY_MAP;
+        }
+    }
+
+    /**
+     * Adds the parameters into context's ParameterMap.
+     * As default, static parameters will not overwrite existing paramaters from other sources.
+     * If you want the static parameters as successor over already existing parameters, set overwrite to <tt>true</tt>.
+     *
+     * @param ac        The action context
+     * @param newParams The parameter map to apply
+     */
+    protected void addParametersToContext(ActionContext ac, Map newParams) {
+        Map previousParams = ac.getParameters();
+
+        Map combinedParams;
+        if ( overwrite ) {
+            if (previousParams != null) {
+                combinedParams = new TreeMap(previousParams);
+            } else {
+                combinedParams = new TreeMap();
+            }
+            if ( newParams != null) {
+                combinedParams.putAll(newParams);
+            }
+        } else {
+            if (newParams != null) {
+                combinedParams = new TreeMap(newParams);
+            } else {
+                combinedParams = new TreeMap();
+            }
+            if ( previousParams != null) {
+                combinedParams.putAll(previousParams);
+            }
+        }
+        ac.setParameters(combinedParams);
+    }
 }

src/test/com/opensymphony/xwork2/validator/SimpleActionValidationTest.java

 package com.opensymphony.xwork2.validator;
 
 import com.opensymphony.xwork2.*;
-import com.opensymphony.xwork2.config.ConfigurationManager;
 import com.opensymphony.xwork2.config.providers.MockConfigurationProvider;
 import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
 import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.ValueStackFactory;
 import com.opensymphony.xwork2.validator.validators.ValidatorSupport;
 
 import java.util.*;
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.