mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
WW-3514 fix/enhance support for include/exclude parameters
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1066613 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -21,14 +21,51 @@
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.WildcardUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Isolate the process of cleaning JSON data from the Interceptor class itself.
|
||||
* <p>Isolate the process of cleaning JSON data from the Interceptor class
|
||||
* itself.</p>
|
||||
*
|
||||
* <p>The allowed and blocked wildcard patterns, combined with
|
||||
* defaultBlock, let you filter out values that should not be injected, in
|
||||
* the same way that ParameterFilterInterceptor does. Note that you can
|
||||
* only remove values from a Map. Removing values from a List is dangerous
|
||||
* because it could change the meaning of the data!</p>
|
||||
*/
|
||||
public abstract class JSONCleaner {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JSONCleaner.class);
|
||||
|
||||
public static class Filter
|
||||
{
|
||||
public Pattern pattern;
|
||||
public boolean allow;
|
||||
|
||||
public Filter(String pattern, boolean allow)
|
||||
{
|
||||
this.pattern = WildcardUtil.compileWildcardPattern(pattern);
|
||||
this.allow = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean defaultBlock = false;
|
||||
private Collection<String> allowed;
|
||||
private Collection<String> blocked;
|
||||
private Map<String, Filter> includesExcludesMap;
|
||||
|
||||
public Object clean(String ognlPrefix, Object data) throws JSONException {
|
||||
if (data == null)
|
||||
return null;
|
||||
@@ -54,11 +91,172 @@ public abstract class JSONCleaner {
|
||||
Iterator iter = map.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry e = (Map.Entry) iter.next();
|
||||
e.setValue(clean((ognlPrefix.length() > 0 ? ognlPrefix + "." : "") + e.getKey(), e.getValue()));
|
||||
String key = (ognlPrefix.length() > 0 ? ognlPrefix + "." : "") + e.getKey();
|
||||
if (allow(key)) {
|
||||
e.setValue(clean(key, e.getValue()));
|
||||
} else {
|
||||
LOG.debug("blocked: " + key);
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
protected abstract Object cleanValue(String ognlName, Object data) throws JSONException;
|
||||
|
||||
private boolean allow(String ognl) {
|
||||
Map<String, Filter> includesExcludesMap = getIncludesExcludesMap();
|
||||
|
||||
boolean allow = !isDefaultBlock();
|
||||
|
||||
if (includesExcludesMap != null) {
|
||||
for (String currRule : includesExcludesMap.keySet()) {
|
||||
Filter f = includesExcludesMap.get(currRule);
|
||||
if (f.pattern.matcher(ognl).matches()) {
|
||||
allow = f.allow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the compiled list of includes and excludes
|
||||
*/
|
||||
public Map<String, Filter> getIncludesExcludesMap() {
|
||||
if (allowed == null && blocked == null) {
|
||||
return includesExcludesMap;
|
||||
}
|
||||
|
||||
if (includesExcludesMap == null) {
|
||||
includesExcludesMap = new TreeMap<String, Filter>();
|
||||
|
||||
Map<String, Boolean> existingExpr = new HashMap<String, Boolean>();
|
||||
|
||||
Map<String, Map<String, String>> includePatternData = JSONUtil.getIncludePatternData();
|
||||
String splitPattern = includePatternData.get(JSONUtil.SPLIT_PATTERN).get(JSONUtil.WILDCARD_PATTERN);
|
||||
String joinString = includePatternData.get(JSONUtil.JOIN_STRING).get(JSONUtil.WILDCARD_PATTERN);
|
||||
String arrayBegin = includePatternData.get(JSONUtil.ARRAY_BEGIN_STRING).get(JSONUtil.WILDCARD_PATTERN);
|
||||
String arrayEnd = includePatternData.get(JSONUtil.ARRAY_END_STRING).get(JSONUtil.WILDCARD_PATTERN);
|
||||
|
||||
if (allowed != null) {
|
||||
for (String a : allowed) {
|
||||
// Compile a pattern for each level of the object hierarchy
|
||||
// so cleanMap() won't short-circuit too early.
|
||||
|
||||
String expr = "";
|
||||
for (String piece : a.split(splitPattern)) {
|
||||
if (expr.length() > 0) {
|
||||
expr += joinString;
|
||||
}
|
||||
expr += piece;
|
||||
|
||||
if (!existingExpr.containsKey(expr)) {
|
||||
existingExpr.put(expr, Boolean.TRUE);
|
||||
|
||||
String s = expr;
|
||||
if (piece.endsWith(arrayEnd)) {
|
||||
s = expr.substring(0, expr.lastIndexOf(arrayBegin));
|
||||
}
|
||||
|
||||
if (s.length() > 0) {
|
||||
includesExcludesMap.put(s, new Filter(s, true));
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding include wildcard expression: " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blocked != null) {
|
||||
for (String b : blocked) {
|
||||
includesExcludesMap.put(b, new Filter(b, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return includesExcludesMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow external caching of the compiled result.
|
||||
*
|
||||
* @param map the compiled list of includes and excludes
|
||||
*/
|
||||
public void setIncludesExcludesMap(Map<String, Filter> map) {
|
||||
includesExcludesMap = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return value of defaultBlock
|
||||
*/
|
||||
public boolean isDefaultBlock() {
|
||||
return defaultBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param defaultExclude The defaultExclude to set.
|
||||
*/
|
||||
public void setDefaultBlock(boolean defaultExclude) {
|
||||
this.defaultBlock = defaultExclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of blocked wildcard patterns
|
||||
*/
|
||||
public Collection<String> getBlockedCollection() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blocked The blocked to set.
|
||||
*/
|
||||
public void setBlockedCollection(Collection<String> blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blocked The blocked paramters as comma separated String.
|
||||
*/
|
||||
public void setBlocked(String blocked) {
|
||||
setBlockedCollection(asCollection(blocked));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of allowed wildcard patterns
|
||||
*/
|
||||
public Collection<String> getAllowedCollection() {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowed The allowed to set.
|
||||
*/
|
||||
public void setAllowedCollection(Collection<String> allowed) {
|
||||
this.allowed = allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowed The allowed paramters as comma separated String.
|
||||
*/
|
||||
public void setAllowed(String allowed) {
|
||||
setAllowedCollection(asCollection(allowed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a collection from the comma delimited String.
|
||||
*
|
||||
* @param commaDelim the comma delimited String.
|
||||
* @return A collection from the comma delimited String. Returns <tt>null</tt> if the string is empty.
|
||||
*/
|
||||
private Collection<String> asCollection(String commaDelim) {
|
||||
if (commaDelim == null || commaDelim.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return TextParseUtil.commaDelimitedStringToSet(commaDelim);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -45,6 +47,7 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.WildcardUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
@@ -142,14 +145,6 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
rpcResponse.setError(new RPCError(message, RPCErrorCode.INVALID_PROCEDURE_CALL));
|
||||
result = rpcResponse;
|
||||
}
|
||||
|
||||
String json = JSONUtil.serialize(result, excludeProperties, includeProperties,
|
||||
ignoreHierarchy, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
|
||||
this.wrapWithComments, json, true, false, noCache, -1, -1, prefix, contentType));
|
||||
|
||||
return Action.NONE;
|
||||
} else {
|
||||
String message = "Request with content type of 'application/json-rpc' was received but SMD is "
|
||||
+ "not enabled for this interceptor. Set 'enableSMD' to true to enable it";
|
||||
@@ -159,8 +154,8 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
result = rpcResponse;
|
||||
}
|
||||
|
||||
String json = JSONUtil.serialize(result, excludeProperties, includeProperties, ignoreHierarchy,
|
||||
excludeNullProperties);
|
||||
String json = JSONUtil.serialize(result, excludeProperties, getIncludeProperties(),
|
||||
ignoreHierarchy, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
|
||||
JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
|
||||
@@ -400,7 +395,7 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
* A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeProperties(String commaDelim) {
|
||||
List<String> excludePatterns = JSONUtil.asList(commaDelim);
|
||||
Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
|
||||
if (excludePatterns != null) {
|
||||
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
|
||||
for (String pattern : excludePatterns) {
|
||||
@@ -417,12 +412,44 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
* A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setIncludeProperties(String commaDelim) {
|
||||
List<String> includePatterns = JSONUtil.asList(commaDelim);
|
||||
if (includePatterns != null) {
|
||||
this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
|
||||
for (String pattern : includePatterns) {
|
||||
this.includeProperties.add(Pattern.compile(pattern));
|
||||
}
|
||||
includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.REGEXP_PATTERN, JSONUtil.getIncludePatternData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of wildcard expressions to match
|
||||
* properties that should be included from the JSON output. Since the
|
||||
* patterns are only used for the JSON-RPC response, you only need to
|
||||
* specify the elements inside your result object (and "result." is
|
||||
* automatically prepended).
|
||||
*
|
||||
* @param commaDelim
|
||||
* A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setIncludeWildcards(String commaDelim) {
|
||||
Map<String, Map<String, String>> includePatternData = JSONUtil.getIncludePatternData();
|
||||
includePatternData.get(JSONUtil.PATTERN_PREFIX).put(JSONUtil.WILDCARD_PATTERN, "result.");
|
||||
includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.WILDCARD_PATTERN, includePatternData);
|
||||
|
||||
if (includeProperties != null) {
|
||||
includeProperties.add(Pattern.compile("id"));
|
||||
includeProperties.add(Pattern.compile("result"));
|
||||
includeProperties.add(Pattern.compile("error"));
|
||||
includeProperties.add(WildcardUtil.compileWildcardPattern("error.code"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate set of includes.
|
||||
*/
|
||||
private List getIncludeProperties()
|
||||
{
|
||||
if (includeProperties != null && getDebug()) {
|
||||
List<Pattern> list = new ArrayList<Pattern>(includeProperties);
|
||||
list.add(WildcardUtil.compileWildcardPattern("error.*"));
|
||||
return list;
|
||||
}
|
||||
else {
|
||||
return includeProperties;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.WildcardUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
@@ -36,8 +37,8 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -108,7 +109,7 @@ public class JSONResult implements Result {
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeProperties(String commaDelim) {
|
||||
List<String> excludePatterns = JSONUtil.asList(commaDelim);
|
||||
Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
|
||||
if (excludePatterns != null) {
|
||||
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
|
||||
for (String pattern : excludePatterns) {
|
||||
@@ -117,6 +118,22 @@ public class JSONResult implements Result {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of wildcard expressions to match properties
|
||||
* that should be excluded from the JSON output.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of wildcard patterns
|
||||
*/
|
||||
public void setExcludeWildcards(String commaDelim) {
|
||||
Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
|
||||
if (excludePatterns != null) {
|
||||
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
|
||||
for (String pattern : excludePatterns) {
|
||||
this.excludeProperties.add(WildcardUtil.compileWildcardPattern(pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the includeProperties
|
||||
*/
|
||||
@@ -125,63 +142,23 @@ public class JSONResult implements Result {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param commaDelim comma delimited include string patterns
|
||||
* Sets a comma-delimited list of regular expressions to match properties
|
||||
* that should be included in the JSON output.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setIncludeProperties(String commaDelim) {
|
||||
List<String> includePatterns = JSONUtil.asList(commaDelim);
|
||||
if (includePatterns != null) {
|
||||
processIncludePatterns(includePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
private void processIncludePatterns(List<String> includePatterns) {
|
||||
includeProperties = new ArrayList<Pattern>(includePatterns.size());
|
||||
Map<String, String> existingPatterns = new HashMap<String, String>();
|
||||
for (String pattern : includePatterns) {
|
||||
processPattern(existingPatterns, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
private void processPattern(Map<String, String> existingPatterns, String pattern) {
|
||||
// Compile a pattern for each *unique* "level" of the object
|
||||
// hierarchy specified in the regex.
|
||||
String[] patternPieces = pattern.split("\\\\\\.");
|
||||
|
||||
String patternExpr = "";
|
||||
for (String patternPiece : patternPieces) {
|
||||
patternExpr = processPatternPiece(existingPatterns, patternExpr, patternPiece);
|
||||
}
|
||||
}
|
||||
|
||||
private String processPatternPiece(Map<String, String> existingPatterns, String patternExpr, String patternPiece) {
|
||||
if (patternExpr.length() > 0) {
|
||||
patternExpr += "\\.";
|
||||
}
|
||||
patternExpr += patternPiece;
|
||||
|
||||
// Check for duplicate patterns so that there is no overlap.
|
||||
if (!existingPatterns.containsKey(patternExpr)) {
|
||||
existingPatterns.put(patternExpr, patternExpr);
|
||||
if (isIndexedProperty(patternPiece)) {
|
||||
addPattern(patternExpr.substring(0, patternExpr.lastIndexOf("\\[")));
|
||||
}
|
||||
addPattern(patternExpr);
|
||||
}
|
||||
return patternExpr;
|
||||
includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.REGEXP_PATTERN, JSONUtil.getIncludePatternData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pattern that does not have the indexed property matching (ie. list\[\d+\] becomes list).
|
||||
* Sets a comma-delimited list of wildcard expressions to match properties
|
||||
* that should be included in the JSON output.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of wildcard patterns
|
||||
*/
|
||||
private boolean isIndexedProperty(String patternPiece) {
|
||||
return patternPiece.endsWith("\\]");
|
||||
}
|
||||
|
||||
private void addPattern(String pattern) {
|
||||
this.includeProperties.add(Pattern.compile(pattern));
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding include property expression: " + pattern);
|
||||
}
|
||||
public void setIncludeWildcards(String commaDelim) {
|
||||
includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.WILDCARD_PATTERN, JSONUtil.getIncludePatternData());
|
||||
}
|
||||
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
|
||||
@@ -31,7 +31,10 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@@ -41,6 +44,8 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang.xwork.StringUtils;
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.WildcardUtil;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
@@ -256,18 +261,10 @@ public class JSONUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> asList(String commaDelim) {
|
||||
public static Set<String> asSet(String commaDelim) {
|
||||
if ((commaDelim == null) || (commaDelim.trim().length() == 0))
|
||||
return null;
|
||||
List<String> list = new ArrayList<String>();
|
||||
String[] split = commaDelim.split(",");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
String trimmed = split[i].trim();
|
||||
if (trimmed.length() > 0) {
|
||||
list.add(trimmed);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
return TextParseUtil.commaDelimitedStringToSet(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,4 +389,103 @@ public class JSONUtil {
|
||||
String header = request.getHeader("Accept-Encoding");
|
||||
return (header != null) && (header.indexOf("gzip") >= 0);
|
||||
}
|
||||
|
||||
/* package */ static final String REGEXP_PATTERN = "regexp";
|
||||
/* package */ static final String WILDCARD_PATTERN = "wildcard";
|
||||
/* package */ static final String SPLIT_PATTERN = "split";
|
||||
/* package */ static final String JOIN_STRING = "join";
|
||||
/* package */ static final String ARRAY_BEGIN_STRING = "array-begin";
|
||||
/* package */ static final String ARRAY_END_STRING = "array-end";
|
||||
/* package */ static final String PATTERN_PREFIX = "pattern-prefix";
|
||||
|
||||
/* package */ static Map<String, Map<String, String>> getIncludePatternData()
|
||||
{
|
||||
Map<String, Map<String, String>> includePatternData = new HashMap<String, Map<String, String>>();
|
||||
|
||||
Map<String, String> data = new HashMap<String, String>();
|
||||
data.put(REGEXP_PATTERN, "\\\\\\.");
|
||||
data.put(WILDCARD_PATTERN, "\\.");
|
||||
includePatternData.put(SPLIT_PATTERN, data);
|
||||
|
||||
data = new HashMap<String, String>();
|
||||
data.put(REGEXP_PATTERN, "\\.");
|
||||
data.put(WILDCARD_PATTERN, ".");
|
||||
includePatternData.put(JOIN_STRING, data);
|
||||
|
||||
data = new HashMap<String, String>();
|
||||
data.put(REGEXP_PATTERN, "\\[");
|
||||
data.put(WILDCARD_PATTERN, "[");
|
||||
includePatternData.put(ARRAY_BEGIN_STRING, data);
|
||||
|
||||
data = new HashMap<String, String>();
|
||||
data.put(REGEXP_PATTERN, "\\]");
|
||||
data.put(WILDCARD_PATTERN, "]");
|
||||
includePatternData.put(ARRAY_END_STRING, data);
|
||||
|
||||
data = new HashMap<String, String>();
|
||||
data.put(REGEXP_PATTERN, "");
|
||||
data.put(WILDCARD_PATTERN, "");
|
||||
includePatternData.put(PATTERN_PREFIX, data);
|
||||
|
||||
return includePatternData;
|
||||
}
|
||||
|
||||
/* package */ static List<Pattern> processIncludePatterns(Set<String> includePatterns, String type, Map<String, Map<String, String>> includePatternData) {
|
||||
if (includePatterns != null) {
|
||||
List<Pattern> results = new ArrayList<Pattern>(includePatterns.size());
|
||||
Map<String, String> existingPatterns = new HashMap<String, String>();
|
||||
for (String pattern : includePatterns) {
|
||||
processPattern(results, existingPatterns, pattern, type, includePatternData);
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void processPattern(List<Pattern> results, Map<String, String> existingPatterns, String pattern, String type, Map<String, Map<String, String>> includePatternData) {
|
||||
// Compile a pattern for each *unique* "level" of the object
|
||||
// hierarchy specified in the regex.
|
||||
String[] patternPieces = pattern.split(includePatternData.get(SPLIT_PATTERN).get(type));
|
||||
|
||||
String patternExpr = "";
|
||||
for (String patternPiece : patternPieces) {
|
||||
patternExpr = processPatternPiece(results, existingPatterns, patternExpr, patternPiece, type, includePatternData);
|
||||
}
|
||||
}
|
||||
|
||||
private static String processPatternPiece(List<Pattern> results, Map<String, String> existingPatterns, String patternExpr, String patternPiece, String type, Map<String, Map<String, String>> includePatternData) {
|
||||
if (patternExpr.length() > 0) {
|
||||
patternExpr += includePatternData.get(JOIN_STRING).get(type);
|
||||
}
|
||||
patternExpr += patternPiece;
|
||||
|
||||
// Check for duplicate patterns so that there is no overlap.
|
||||
if (!existingPatterns.containsKey(patternExpr)) {
|
||||
existingPatterns.put(patternExpr, patternExpr);
|
||||
if (isIndexedProperty(patternPiece, type, includePatternData)) {
|
||||
addPattern(results, patternExpr.substring(0, patternExpr.lastIndexOf(includePatternData.get(ARRAY_BEGIN_STRING).get(type))), type, includePatternData);
|
||||
}
|
||||
addPattern(results, patternExpr, type, includePatternData);
|
||||
}
|
||||
return patternExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pattern that does not have the indexed property matching (ie. list\[\d+\] becomes list).
|
||||
*/
|
||||
private static boolean isIndexedProperty(String patternPiece, String type, Map<String, Map<String, String>> includePatternData) {
|
||||
return patternPiece.endsWith(includePatternData.get(ARRAY_END_STRING).get(type));
|
||||
}
|
||||
|
||||
private static void addPattern(List<Pattern> results, String pattern, String type, Map<String, Map<String, String>> includePatternData) {
|
||||
pattern = includePatternData.get(PATTERN_PREFIX).get(type) + pattern;
|
||||
results.add(
|
||||
type == REGEXP_PATTERN ?
|
||||
Pattern.compile(pattern) :
|
||||
WildcardUtil.compileWildcardPattern(pattern));
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding include " + (type == REGEXP_PATTERN ? "property" : "wildcard") + " expression: " + pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright 2003-2004 The Apache Software Foundation.
|
||||
*
|
||||
* Licensed 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 org.apache.struts2.json;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class JSONCleanerTest extends TestCase {
|
||||
|
||||
public void testDefaultBlock1() throws JSONException {
|
||||
|
||||
JSONCleaner cleaner = getCleaner();
|
||||
cleaner.setDefaultBlock(true);
|
||||
cleaner.setAllowed("a,c");
|
||||
|
||||
Map data = getData();
|
||||
cleaner.clean("", data);
|
||||
assertEquals(2, data.size());
|
||||
assertEquals("x", data.get("a"));
|
||||
assertNull(data.get("b"));
|
||||
assertNotNull(data.get("c"));
|
||||
assertNull(data.get("d"));
|
||||
|
||||
}
|
||||
|
||||
public void testDefaultBlock2() throws JSONException {
|
||||
|
||||
JSONCleaner cleaner = getCleaner();
|
||||
cleaner.setDefaultBlock(true);
|
||||
cleaner.setAllowed("a,c,d.x");
|
||||
|
||||
Map data = getData();
|
||||
cleaner.clean("", data);
|
||||
assertEquals(3, data.size());
|
||||
assertEquals("x", data.get("a"));
|
||||
assertNull(data.get("b"));
|
||||
assertNotNull(data.get("c"));
|
||||
assertNotNull(data.get("d"));
|
||||
assertEquals(1, ((Map) data.get("d")).size());
|
||||
assertEquals("a", ((Map) data.get("d")).get("x"));
|
||||
assertNull(((Map) data.get("d")).get("y"));
|
||||
|
||||
}
|
||||
|
||||
public void testDefaultAllow1() throws JSONException {
|
||||
|
||||
JSONCleaner cleaner = getCleaner();
|
||||
cleaner.setDefaultBlock(false);
|
||||
cleaner.setBlocked("b,d");
|
||||
|
||||
Map data = getData();
|
||||
cleaner.clean("", data);
|
||||
assertEquals(2, data.size());
|
||||
assertEquals("x", data.get("a"));
|
||||
assertNull(data.get("b"));
|
||||
assertNotNull(data.get("c"));
|
||||
assertNull(data.get("d"));
|
||||
|
||||
}
|
||||
|
||||
public void testDefaultAllow2() throws JSONException {
|
||||
|
||||
JSONCleaner cleaner = getCleaner();
|
||||
cleaner.setDefaultBlock(false);
|
||||
cleaner.setBlocked("b,d.y");
|
||||
|
||||
Map data = getData();
|
||||
cleaner.clean("", data);
|
||||
assertEquals(3, data.size());
|
||||
assertEquals("x", data.get("a"));
|
||||
assertNull(data.get("b"));
|
||||
assertNotNull(data.get("c"));
|
||||
assertNotNull(data.get("d"));
|
||||
assertEquals(1, ((Map) data.get("d")).size());
|
||||
assertEquals("a", ((Map) data.get("d")).get("x"));
|
||||
assertNull(((Map) data.get("d")).get("y"));
|
||||
|
||||
}
|
||||
|
||||
private JSONCleaner getCleaner() {
|
||||
|
||||
return new JSONCleaner() {
|
||||
protected Object cleanValue(String ognlName, Object data) throws JSONException {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private Map getData() {
|
||||
|
||||
Map data = new HashMap();
|
||||
data.put("a", "x");
|
||||
data.put("b", "y");
|
||||
|
||||
List list = new ArrayList();
|
||||
list.add("p");
|
||||
list.add("q");
|
||||
list.add("r");
|
||||
data.put("c", list);
|
||||
|
||||
Map map = new HashMap();
|
||||
map.put("x", "a");
|
||||
map.put("y", "b");
|
||||
data.put("d", map);
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user