| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 com.opensymphony.xwork2.interceptor; |
| |
| import com.opensymphony.xwork2.ActionInvocation; |
| import com.opensymphony.xwork2.util.TextParseUtil; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.struts2.dispatcher.HttpParameters; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| /** |
| * <!-- START SNIPPET: description --> |
| * |
| * The Parameter Filter Interceptor blocks parameters from getting |
| * to the rest of the stack or your action. You can use multiple |
| * parameter filter interceptors for a given action, so, for example, |
| * you could use one in your default stack that filtered parameters |
| * you wanted blocked from every action and those you wanted blocked |
| * from an individual action you could add an additional interceptor |
| * for each action. |
| * |
| * <!-- END SNIPPET: description --> |
| * |
| * <!-- START SNIPPET: parameters --> |
| * |
| * <ul> |
| * <li>allowed - a comma delimited list of parameter prefixes |
| * that are allowed to pass to the action</li> |
| * <li>blocked - a comma delimited list of parameter prefixes |
| * that are not allowed to pass to the action</li> |
| * <li>defaultBlock - boolean (default to false) whether by |
| * default a given parameter is blocked. If true, then a parameter |
| * must have a prefix in the allowed list in order to be able |
| * to pass to the action |
| * </ul> |
| * |
| * <p>The way parameters are filtered for the least configuration is that |
| * if a string is in the allowed or blocked lists, then any parameter |
| * that is a member of the object represented by the parameter is allowed |
| * or blocked respectively.</p> |
| * |
| * <p>For example, if the parameters are: |
| * <ul> |
| * <li>blocked: person,person.address.createDate,personDao</li> |
| * <li>allowed: person.address</li> |
| * <li>defaultBlock: false</li> |
| * </ul> |
| * |
| * <p> |
| * The parameters person.name, person.phoneNum etc would be blocked |
| * because 'person' is in the blocked list. However, person.address.street |
| * and person.address.city would be allowed because person.address is |
| * in the allowed list (the longer string determines permissions).</p> |
| * <!-- END SNIPPET: parameters --> |
| * |
| * <!-- START SNIPPET: extending --> |
| * There are no known extension points to this interceptor. |
| * <!-- END SNIPPET: extending --> |
| * |
| * <pre> |
| * <!-- START SNIPPET: example --> |
| * <interceptors> |
| * ... |
| * <interceptor name="parameterFilter" class="com.opensymphony.xwork2.interceptor.ParameterFilterInterceptor"/> |
| * ... |
| * </interceptors> |
| * |
| * <action ....> |
| * ... |
| * <interceptor-ref name="parameterFilter"> |
| * <param name="blocked">person,person.address.createDate,personDao</param> |
| * </interceptor-ref> |
| * ... |
| * </action> |
| * <!-- END SNIPPET: example --> |
| * </pre> |
| * |
| * @author Gabe |
| */ |
| public class ParameterFilterInterceptor extends AbstractInterceptor { |
| |
| private static final Logger LOG = LogManager.getLogger(ParameterFilterInterceptor.class); |
| |
| private Collection<String> allowed; |
| private Collection<String> blocked; |
| private Map<String, Boolean> includesExcludesMap; |
| private boolean defaultBlock = false; |
| |
| @Override |
| public String intercept(ActionInvocation invocation) throws Exception { |
| |
| HttpParameters parameters = invocation.getInvocationContext().getParameters(); |
| |
| Map<String, Boolean> includesExcludesMap = getIncludesExcludesMap(); |
| |
| for (String param : parameters.keySet()) { |
| boolean currentAllowed = !isDefaultBlock(); |
| |
| for (Map.Entry<String, Boolean> entry : includesExcludesMap.entrySet()) { |
| String currRule = entry.getKey(); |
| |
| if (param.startsWith(currRule) && |
| (param.length() == currRule.length() || isPropertySeparator(param.charAt(currRule.length()))) |
| ) { |
| currentAllowed = entry.getValue(); |
| } |
| } |
| if (!currentAllowed) { |
| LOG.debug("Removing param: {}", param); |
| parameters = parameters.remove(param); |
| } |
| } |
| |
| invocation.getInvocationContext().setParameters(parameters); |
| |
| return invocation.invoke(); |
| } |
| |
| /** |
| * Tests if the given char is a property separator char <code>.([</code>. |
| * |
| * @param c the char |
| * @return <tt>true</tt>, if char is property separator, <tt>false</tt> otherwise. |
| */ |
| private boolean isPropertySeparator(char c) { |
| return c == '.' || c == '(' || c == '['; |
| } |
| |
| private Map<String, Boolean> getIncludesExcludesMap() { |
| if (this.includesExcludesMap == null) { |
| this.includesExcludesMap = new TreeMap<>(); |
| |
| if (getAllowedCollection() != null) { |
| for (String e : getAllowedCollection()) { |
| this.includesExcludesMap.put(e, Boolean.TRUE); |
| } |
| } |
| if (getBlockedCollection() != null) { |
| for (String b : getBlockedCollection()) { |
| this.includesExcludesMap.put(b, Boolean.FALSE); |
| } |
| } |
| } |
| |
| return this.includesExcludesMap; |
| } |
| |
| /** |
| * @return Returns the defaultBlock. |
| */ |
| public boolean isDefaultBlock() { |
| return defaultBlock; |
| } |
| |
| /** |
| * @param defaultExclude The defaultExclude to set. |
| */ |
| public void setDefaultBlock(boolean defaultExclude) { |
| this.defaultBlock = defaultExclude; |
| } |
| |
| /** |
| * @return Returns the blocked. |
| */ |
| 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 Returns the allowed. |
| */ |
| 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 (StringUtils.isBlank(commaDelim)) { |
| return null; |
| } |
| return TextParseUtil.commaDelimitedStringToSet(commaDelim); |
| } |
| |
| } |