001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005:
006: package com.opensymphony.xwork.interceptor;
007:
008: import com.opensymphony.xwork.ActionInvocation;
009: import com.opensymphony.xwork.util.TextParseUtil;
010:
011: import java.util.*;
012:
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: /**
017: * <!-- START SNIPPET: description -->
018: *
019: * <p>The Parameter Filter Interceptor blocks parameters from getting
020: * to the rest of the stack or your action. You can use multiple
021: * parameter filter interceptors for a given action, so, for example,
022: * you could use one in your default stack that filtered parameters
023: * you wanted blocked from every action and those you wanted blocked
024: * from an individual action you could add an additional interceptor
025: * for each action.</p>
026: *
027: * <!-- END SNIPPET: description -->
028: *
029: * <!-- START SNIPPET: parameters -->
030: *
031: * <ul>
032: *
033: * <li>allowed - a comma delimited list of parameter prefixes
034: * that are allowed to pass to the action</li>
035: * <li>blocked - a comma delimited list of parameter prefixes
036: * that are not allowed to pass to the action</li>
037: * <li>defaultBlock - boolean (default to false) whether by
038: * default a given parameter is blocked. If true, then a parameter
039: * must have a prefix in the allowed list in order to be able
040: * to pass to the action
041: * </ul>
042: *
043: * <p>The way parameters are filtered for the least configuration is that
044: * if a string is in the allowed or blocked lists, then any parameter
045: * that is a member of the object represented by the parameter is allowed
046: * or blocked respectively.</p>
047: *
048: * <p>For example, if the parameters are:
049: * <ul>
050: * <li>blocked: person,person.address.createDate,personDao</li>
051: * <li>allowed: person.address</li>
052: * <li>defaultBlock: false</li>
053: * </ul>
054: * <br>
055: * The parameters person.name, person.phoneNum etc would be blocked
056: * because 'person' is in the blocked list. However, person.address.street
057: * and person.address.city would be allowed because person.address is
058: * in the allowed list (the longer string determines permissions).</p>
059: * <!-- END SNIPPET: parameters -->
060: *
061: * @author Gabe
062: */
063: public class ParameterFilterInterceptor implements Interceptor {
064:
065: private static final long serialVersionUID = -7467459931718970229L;
066:
067: private static final Log LOG = LogFactory
068: .getLog(ParameterFilterInterceptor.class);
069:
070: private Collection allowed;
071:
072: private Collection blocked;
073:
074: private Map includesExcludesMap;
075:
076: private boolean defaultBlock = false;
077:
078: public void destroy() {
079: }
080:
081: public void init() {
082: }
083:
084: public String intercept(ActionInvocation invocation)
085: throws Exception {
086:
087: Map parameters = invocation.getInvocationContext()
088: .getParameters();
089: HashSet paramsToRemove = new HashSet();
090:
091: Map includesExcludesMap = getIncludesExcludesMap();
092:
093: for (Iterator i = parameters.keySet().iterator(); i.hasNext();) {
094:
095: String param = (String) i.next();
096:
097: boolean currentAllowed = !isDefaultBlock();
098:
099: boolean foundApplicableRule = false;
100: for (Iterator j = includesExcludesMap.keySet().iterator(); j
101: .hasNext();) {
102: String currRule = (String) j.next();
103:
104: if (param.startsWith(currRule)
105: && (param.length() == currRule.length() || isPropSeperator(param
106: .charAt(currRule.length())))) {
107: currentAllowed = ((Boolean) includesExcludesMap
108: .get(currRule)).booleanValue();
109: } else {
110: if (foundApplicableRule) {
111: foundApplicableRule = false;
112: break;
113: }
114: }
115: }
116: if (!currentAllowed) {
117: paramsToRemove.add(param);
118: }
119: }
120:
121: if (LOG.isDebugEnabled()) {
122: LOG.debug("Params to remove: " + paramsToRemove);
123: }
124:
125: for (Iterator i = paramsToRemove.iterator(); i.hasNext();) {
126: parameters.remove(i.next());
127: }
128:
129: return invocation.invoke();
130: }
131:
132: /**
133: * @param c
134: * @return <tt>true</tt>, if char is property separator, <tt>false</tt> otherwise.
135: */
136: private boolean isPropSeperator(char c) {
137: return c == '.' || c == '(' || c == '[';
138: }
139:
140: private Map getIncludesExcludesMap() {
141: if (this .includesExcludesMap == null) {
142: this .includesExcludesMap = new TreeMap();
143:
144: if (getAllowedCollection() != null) {
145: for (Iterator i = getAllowedCollection().iterator(); i
146: .hasNext();) {
147: this .includesExcludesMap
148: .put(i.next(), Boolean.TRUE);
149: }
150: }
151: if (getBlockedCollection() != null) {
152: for (Iterator i = getBlockedCollection().iterator(); i
153: .hasNext();) {
154: this .includesExcludesMap.put(i.next(),
155: Boolean.FALSE);
156: }
157: }
158: }
159:
160: return this .includesExcludesMap;
161: }
162:
163: /**
164: * @return Returns the defaultBlock.
165: */
166: public boolean isDefaultBlock() {
167: return defaultBlock;
168: }
169:
170: /**
171: * @param defaultExclude The defaultExclude to set.
172: */
173: public void setDefaultBlock(boolean defaultExclude) {
174: this .defaultBlock = defaultExclude;
175: }
176:
177: /**
178: * @return Returns the blocked.
179: */
180: public Collection getBlockedCollection() {
181: return blocked;
182: }
183:
184: /**
185: * @param blocked The blocked to set.
186: */
187: public void setBlockedCollection(Collection blocked) {
188: this .blocked = blocked;
189: }
190:
191: /**
192: * @param blocked The blocked paramters as comma separated String.
193: */
194: public void setBlocked(String blocked) {
195: setBlockedCollection(asCollection(blocked));
196: }
197:
198: /**
199: * @return Returns the allowed.
200: */
201: public Collection getAllowedCollection() {
202: return allowed;
203: }
204:
205: /**
206: * @param allowed The allowed to set.
207: */
208: public void setAllowedCollection(Collection allowed) {
209: this .allowed = allowed;
210: }
211:
212: /**
213: * @param allowed The allowed paramters as comma separated String.
214: */
215: public void setAllowed(String allowed) {
216: setAllowedCollection(asCollection(allowed));
217: }
218:
219: /**
220: * Return a collection from the comma delimited String.
221: *
222: * @param commaDelim
223: * @return A collection from the comma delimited String.
224: */
225: private Collection asCollection(String commaDelim) {
226: if (commaDelim == null || commaDelim.trim().length() == 0) {
227: return null;
228: }
229: return TextParseUtil.commaDelimitedStringToSet(commaDelim);
230: }
231: }
|