1 /* 2 * Copyright (c) 2002-2007 by OpenSymphony 3 * All rights reserved. 4 */ 5 package com.opensymphony.xwork2.interceptor; 6 7 import com.opensymphony.xwork2.ActionInvocation; 8 import com.opensymphony.xwork2.util.TextParseUtil; 9 import com.opensymphony.xwork2.util.logging.Logger; 10 import com.opensymphony.xwork2.util.logging.LoggerFactory; 11 12 import java.util.Collection; 13 import java.util.HashSet; 14 import java.util.Map; 15 import java.util.TreeMap; 16 17 /** 18 * <!-- START SNIPPET: description --> 19 * 20 * The Parameter Filter Interceptor blocks parameters from getting 21 * to the rest of the stack or your action. You can use multiple 22 * parameter filter interceptors for a given action, so, for example, 23 * you could use one in your default stack that filtered parameters 24 * you wanted blocked from every action and those you wanted blocked 25 * from an individual action you could add an additional interceptor 26 * for each action. 27 * 28 * <!-- END SNIPPET: description --> 29 * 30 * <!-- START SNIPPET: parameters --> 31 * 32 * <ul> 33 * <li>allowed - a comma delimited list of parameter prefixes 34 * that are allowed to pass to the action</li> 35 * <li>blocked - a comma delimited list of parameter prefixes 36 * that are not allowed to pass to the action</li> 37 * <li>defaultBlock - boolean (default to false) whether by 38 * default a given parameter is blocked. If true, then a parameter 39 * must have a prefix in the allowed list in order to be able 40 * to pass to the action 41 * </ul> 42 * 43 * <p>The way parameters are filtered for the least configuration is that 44 * if a string is in the allowed or blocked lists, then any parameter 45 * that is a member of the object represented by the parameter is allowed 46 * or blocked respectively.</p> 47 * 48 * <p>For example, if the parameters are: 49 * <ul> 50 * <li>blocked: person,person.address.createDate,personDao</li> 51 * <li>allowed: person.address</li> 52 * <li>defaultBlock: false</li> 53 * </ul> 54 * <br> 55 * The parameters person.name, person.phoneNum etc would be blocked 56 * because 'person' is in the blocked list. However, person.address.street 57 * and person.address.city would be allowed because person.address is 58 * in the allowed list (the longer string determines permissions).</p> 59 * <!-- END SNIPPET: parameters --> 60 * 61 * <!-- START SNIPPET: extending --> 62 * There are no known extension points to this interceptor. 63 * <!-- END SNIPPET: extending --> 64 * 65 * <pre> 66 * <!-- START SNIPPET: example --> 67 * <interceptors> 68 * ... 69 * <interceptor name="parameterFilter" class="com.opensymphony.xwork2.interceptor.ParameterFilterInterceptor"/> 70 * ... 71 * </interceptors> 72 * 73 * <action ....> 74 * ... 75 * <interceptor-ref name="parameterFilter"> 76 * <param name="blocked">person,person.address.createDate,personDao</param> 77 * </interceptor-ref> 78 * ... 79 * </action> 80 * <!-- END SNIPPET: example --> 81 * </pre> 82 * 83 * @author Gabe 84 */ 85 public class ParameterFilterInterceptor extends AbstractInterceptor { 86 87 private static final Logger LOG = LoggerFactory.getLogger(ParameterFilterInterceptor.class); 88 89 private Collection<String> allowed; 90 private Collection<String> blocked; 91 private Map<String, Boolean> includesExcludesMap; 92 private boolean defaultBlock = false; 93 94 @Override 95 public String intercept(ActionInvocation invocation) throws Exception { 96 97 Map<String, Object> parameters = invocation.getInvocationContext().getParameters(); 98 HashSet<String> paramsToRemove = new HashSet<String>(); 99 100 Map<String, Boolean> includesExcludesMap = getIncludesExcludesMap(); 101 102 for (Object o : parameters.keySet()) { 103 String param = o.toString(); 104 105 boolean currentAllowed = !isDefaultBlock(); 106 107 boolean foundApplicableRule = false; 108 for (Object o1 : includesExcludesMap.keySet()) { 109 String currRule = (String) o1; 110 111 if (param.startsWith(currRule) 112 && (param.length() == currRule.length() 113 || isPropSeperator(param.charAt(currRule.length())))) { 114 currentAllowed = includesExcludesMap.get(currRule).booleanValue(); 115 } else { 116 if (foundApplicableRule) { 117 foundApplicableRule = false; 118 break; 119 } 120 } 121 } 122 if (!currentAllowed) { 123 paramsToRemove.add(param); 124 } 125 } 126 127 if (LOG.isDebugEnabled()) { 128 LOG.debug("Params to remove: " + paramsToRemove); 129 } 130 131 for (Object aParamsToRemove : paramsToRemove) { 132 parameters.remove(aParamsToRemove); 133 } 134 135 return invocation.invoke(); 136 } 137 138 /** 139 * Tests if the given char is a property seperator char <code>.([</code>. 140 * 141 * @param c the char 142 * @return <tt>true</tt>, if char is property separator, <tt>false</tt> otherwise. 143 */ 144 private static boolean isPropSeperator(char c) { 145 return c == '.' || c == '(' || c == '['; 146 } 147 148 private Map<String, Boolean> getIncludesExcludesMap() { 149 if (this.includesExcludesMap == null) { 150 this.includesExcludesMap = new TreeMap<String, Boolean>(); 151 152 if (getAllowedCollection() != null) { 153 for (String e : getAllowedCollection()) { 154 this.includesExcludesMap.put(e, Boolean.TRUE); 155 } 156 } 157 if (getBlockedCollection() != null) { 158 for (String b : getBlockedCollection()) { 159 this.includesExcludesMap.put(b, Boolean.FALSE); 160 } 161 } 162 } 163 164 return this.includesExcludesMap; 165 } 166 167 /** 168 * @return Returns the defaultBlock. 169 */ 170 public boolean isDefaultBlock() { 171 return defaultBlock; 172 } 173 174 /** 175 * @param defaultExclude The defaultExclude to set. 176 */ 177 public void setDefaultBlock(boolean defaultExclude) { 178 this.defaultBlock = defaultExclude; 179 } 180 181 /** 182 * @return Returns the blocked. 183 */ 184 public Collection<String> getBlockedCollection() { 185 return blocked; 186 } 187 188 /** 189 * @param blocked The blocked to set. 190 */ 191 public void setBlockedCollection(Collection<String> blocked) { 192 this.blocked = blocked; 193 } 194 195 /** 196 * @param blocked The blocked paramters as comma separated String. 197 */ 198 public void setBlocked(String blocked) { 199 setBlockedCollection(asCollection(blocked)); 200 } 201 202 /** 203 * @return Returns the allowed. 204 */ 205 public Collection<String> getAllowedCollection() { 206 return allowed; 207 } 208 209 /** 210 * @param allowed The allowed to set. 211 */ 212 public void setAllowedCollection(Collection<String> allowed) { 213 this.allowed = allowed; 214 } 215 216 /** 217 * @param allowed The allowed paramters as comma separated String. 218 */ 219 public void setAllowed(String allowed) { 220 setAllowedCollection(asCollection(allowed)); 221 } 222 223 /** 224 * Return a collection from the comma delimited String. 225 * 226 * @param commaDelim the comma delimited String. 227 * @return A collection from the comma delimited String. Returns <tt>null</tt> if the string is empty. 228 */ 229 private Collection<String> asCollection(String commaDelim) { 230 if (commaDelim == null || commaDelim.trim().length() == 0) { 231 return null; 232 } 233 return TextParseUtil.commaDelimitedStringToSet(commaDelim); 234 } 235 236 }