blob: fa3d514099be1b6ca17fbdffddab99ab269dbc91 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultFilterChainManager.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">Apache Shiro :: Test Coverage</a> &gt; <a href="../index.html" class="el_bundle">shiro-web</a> &gt; <a href="index.source.html" class="el_package">org.apache.shiro.web.filter.mgt</a> &gt; <span class="el_source">DefaultFilterChainManager.java</span></div><h1>DefaultFilterChainManager.java</h1><pre class="source lang-java linenums">/*
* 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
* &quot;License&quot;); 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
* &quot;AS IS&quot; 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.shiro.web.filter.mgt;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.Nameable;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.PathConfigProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Default {@link FilterChainManager} implementation maintaining a map of {@link Filter Filter} instances
* (key: filter name, value: Filter) as well as a map of {@link NamedFilterList NamedFilterList}s created from these
* {@code Filter}s (key: filter chain name, value: NamedFilterList). The {@code NamedFilterList} is essentially a
* {@link FilterChain} that also has a name property by which it can be looked up.
*
* @see NamedFilterList
* @since 1.0
*/
public class DefaultFilterChainManager implements FilterChainManager {
<span class="fc" id="L49"> private static transient final Logger log = LoggerFactory.getLogger(DefaultFilterChainManager.class);</span>
private FilterConfig filterConfig;
private Map&lt;String, Filter&gt; filters; //pool of filters available for creating chains
private Map&lt;String, NamedFilterList&gt; filterChains; //key: chain name, value: chain
<span class="fc" id="L57"> public DefaultFilterChainManager() {</span>
<span class="fc" id="L58"> this.filters = new LinkedHashMap&lt;String, Filter&gt;();</span>
<span class="fc" id="L59"> this.filterChains = new LinkedHashMap&lt;String, NamedFilterList&gt;();</span>
<span class="fc" id="L60"> addDefaultFilters(false);</span>
<span class="fc" id="L61"> }</span>
<span class="fc" id="L63"> public DefaultFilterChainManager(FilterConfig filterConfig) {</span>
<span class="fc" id="L64"> this.filters = new LinkedHashMap&lt;String, Filter&gt;();</span>
<span class="fc" id="L65"> this.filterChains = new LinkedHashMap&lt;String, NamedFilterList&gt;();</span>
<span class="fc" id="L66"> setFilterConfig(filterConfig);</span>
<span class="fc" id="L67"> addDefaultFilters(true);</span>
<span class="fc" id="L68"> }</span>
/**
* Returns the {@code FilterConfig} provided by the Servlet container at webapp startup.
*
* @return the {@code FilterConfig} provided by the Servlet container at webapp startup.
*/
public FilterConfig getFilterConfig() {
<span class="fc" id="L76"> return filterConfig;</span>
}
/**
* Sets the {@code FilterConfig} provided by the Servlet container at webapp startup.
*
* @param filterConfig the {@code FilterConfig} provided by the Servlet container at webapp startup.
*/
public void setFilterConfig(FilterConfig filterConfig) {
<span class="fc" id="L85"> this.filterConfig = filterConfig;</span>
<span class="fc" id="L86"> }</span>
public Map&lt;String, Filter&gt; getFilters() {
<span class="fc" id="L89"> return filters;</span>
}
@SuppressWarnings({&quot;UnusedDeclaration&quot;})
public void setFilters(Map&lt;String, Filter&gt; filters) {
<span class="fc" id="L94"> this.filters = filters;</span>
<span class="fc" id="L95"> }</span>
public Map&lt;String, NamedFilterList&gt; getFilterChains() {
<span class="fc" id="L98"> return filterChains;</span>
}
@SuppressWarnings({&quot;UnusedDeclaration&quot;})
public void setFilterChains(Map&lt;String, NamedFilterList&gt; filterChains) {
<span class="fc" id="L103"> this.filterChains = filterChains;</span>
<span class="fc" id="L104"> }</span>
public Filter getFilter(String name) {
<span class="fc" id="L107"> return this.filters.get(name);</span>
}
public void addFilter(String name, Filter filter) {
<span class="fc" id="L111"> addFilter(name, filter, false);</span>
<span class="fc" id="L112"> }</span>
public void addFilter(String name, Filter filter, boolean init) {
<span class="fc" id="L115"> addFilter(name, filter, init, true);</span>
<span class="fc" id="L116"> }</span>
public void createChain(String chainName, String chainDefinition) {
<span class="fc bfc" id="L119" title="All 2 branches covered."> if (!StringUtils.hasText(chainName)) {</span>
<span class="fc" id="L120"> throw new NullPointerException(&quot;chainName cannot be null or empty.&quot;);</span>
}
<span class="fc bfc" id="L122" title="All 2 branches covered."> if (!StringUtils.hasText(chainDefinition)) {</span>
<span class="fc" id="L123"> throw new NullPointerException(&quot;chainDefinition cannot be null or empty.&quot;);</span>
}
<span class="fc bfc" id="L126" title="All 2 branches covered."> if (log.isDebugEnabled()) {</span>
<span class="fc" id="L127"> log.debug(&quot;Creating chain [&quot; + chainName + &quot;] from String definition [&quot; + chainDefinition + &quot;]&quot;);</span>
}
//parse the value by tokenizing it to get the resulting filter-specific config entries
//
//e.g. for a value of
//
// &quot;authc, roles[admin,user], perms[file:edit]&quot;
//
// the resulting token array would equal
//
// { &quot;authc&quot;, &quot;roles[admin,user]&quot;, &quot;perms[file:edit]&quot; }
//
<span class="fc" id="L140"> String[] filterTokens = splitChainDefinition(chainDefinition);</span>
//each token is specific to each filter.
//strip the name and extract any filter-specific config between brackets [ ]
<span class="fc bfc" id="L144" title="All 2 branches covered."> for (String token : filterTokens) {</span>
<span class="fc" id="L145"> String[] nameConfigPair = toNameConfigPair(token);</span>
//now we have the filter name, path and (possibly null) path-specific config. Let's apply them:
<span class="fc" id="L148"> addToChain(chainName, nameConfigPair[0], nameConfigPair[1]);</span>
}
<span class="fc" id="L150"> }</span>
/**
* Splits the comma-delimited filter chain definition line into individual filter definition tokens.
* &lt;p/&gt;
* Example Input:
* &lt;pre&gt;
* foo, bar[baz], blah[x, y]
* &lt;/pre&gt;
* Resulting Output:
* &lt;pre&gt;
* output[0] == foo
* output[1] == bar[baz]
* output[2] == blah[x, y]
* &lt;/pre&gt;
* @param chainDefinition the comma-delimited filter chain definition.
* @return an array of filter definition tokens
* @since 1.2
* @see &lt;a href=&quot;https://issues.apache.org/jira/browse/SHIRO-205&quot;&gt;SHIRO-205&lt;/a&gt;
*/
protected String[] splitChainDefinition(String chainDefinition) {
<span class="fc" id="L171"> return StringUtils.split(chainDefinition, StringUtils.DEFAULT_DELIMITER_CHAR, '[', ']', true, true);</span>
}
/**
* Based on the given filter chain definition token (e.g. 'foo' or 'foo[bar, baz]'), this will return the token
* as a name/value pair, removing any brackets as necessary. Examples:
* &lt;table&gt;
* &lt;tr&gt;
* &lt;th&gt;Input&lt;/th&gt;
* &lt;th&gt;Result&lt;/th&gt;
* &lt;/tr&gt;
* &lt;tr&gt;
* &lt;td&gt;{@code foo}&lt;/td&gt;
* &lt;td&gt;returned[0] == {@code foo}&lt;br/&gt;returned[1] == {@code null}&lt;/td&gt;
* &lt;/tr&gt;
* &lt;tr&gt;
* &lt;td&gt;{@code foo[bar, baz]}&lt;/td&gt;
* &lt;td&gt;returned[0] == {@code foo}&lt;br/&gt;returned[1] == {@code bar, baz}&lt;/td&gt;
* &lt;/tr&gt;
* &lt;/table&gt;
* @param token the filter chain definition token
* @return A name/value pair representing the filter name and a (possibly null) config value.
* @throws ConfigurationException if the token cannot be parsed
* @since 1.2
* @see &lt;a href=&quot;https://issues.apache.org/jira/browse/SHIRO-205&quot;&gt;SHIRO-205&lt;/a&gt;
*/
protected String[] toNameConfigPair(String token) throws ConfigurationException {
try {
<span class="fc" id="L200"> String[] pair = token.split(&quot;\\[&quot;, 2);</span>
<span class="fc" id="L201"> String name = StringUtils.clean(pair[0]);</span>
<span class="pc bpc" id="L203" title="1 of 2 branches missed."> if (name == null) {</span>
<span class="nc" id="L204"> throw new IllegalArgumentException(&quot;Filter name not found for filter chain definition token: &quot; + token);</span>
}
<span class="fc" id="L206"> String config = null;</span>
<span class="fc bfc" id="L208" title="All 2 branches covered."> if (pair.length == 2) {</span>
<span class="fc" id="L209"> config = StringUtils.clean(pair[1]);</span>
//if there was an open bracket, it assumed there is a closing bracket, so strip it too:
<span class="fc" id="L211"> config = config.substring(0, config.length() - 1);</span>
<span class="fc" id="L212"> config = StringUtils.clean(config);</span>
//backwards compatibility prior to implementing SHIRO-205:
//prior to SHIRO-205 being implemented, it was common for end-users to quote the config inside brackets
//if that config required commas. We need to strip those quotes to get to the interior quoted definition
//to ensure any existing quoted definitions still function for end users:
<span class="fc bfc" id="L218" title="All 6 branches covered."> if (config != null &amp;&amp; config.startsWith(&quot;\&quot;&quot;) &amp;&amp; config.endsWith(&quot;\&quot;&quot;)) {</span>
<span class="fc" id="L219"> String stripped = config.substring(1, config.length() - 1);</span>
<span class="fc" id="L220"> stripped = StringUtils.clean(stripped);</span>
//if the stripped value does not have any internal quotes, we can assume that the entire config was
//quoted and we can use the stripped value.
<span class="pc bpc" id="L224" title="1 of 4 branches missed."> if (stripped != null &amp;&amp; stripped.indexOf('&quot;') == -1) {</span>
<span class="fc" id="L225"> config = stripped;</span>
}
//else:
//the remaining config does have internal quotes, so we need to assume that each comma delimited
//pair might be quoted, in which case we need the leading and trailing quotes that we stripped
//So we ignore the stripped value.
}
}
<span class="fc" id="L234"> return new String[]{name, config};</span>
<span class="nc" id="L236"> } catch (Exception e) {</span>
<span class="nc" id="L237"> String msg = &quot;Unable to parse filter chain definition token: &quot; + token;</span>
<span class="nc" id="L238"> throw new ConfigurationException(msg, e);</span>
}
}
protected void addFilter(String name, Filter filter, boolean init, boolean overwrite) {
<span class="fc" id="L243"> Filter existing = getFilter(name);</span>
<span class="pc bpc" id="L244" title="1 of 4 branches missed."> if (existing == null || overwrite) {</span>
<span class="fc bfc" id="L245" title="All 2 branches covered."> if (filter instanceof Nameable) {</span>
<span class="fc" id="L246"> ((Nameable) filter).setName(name);</span>
}
<span class="fc bfc" id="L248" title="All 2 branches covered."> if (init) {</span>
<span class="fc" id="L249"> initFilter(filter);</span>
}
<span class="fc" id="L251"> this.filters.put(name, filter);</span>
}
<span class="fc" id="L253"> }</span>
public void addToChain(String chainName, String filterName) {
<span class="fc" id="L256"> addToChain(chainName, filterName, null);</span>
<span class="fc" id="L257"> }</span>
public void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) {
<span class="fc bfc" id="L260" title="All 2 branches covered."> if (!StringUtils.hasText(chainName)) {</span>
<span class="fc" id="L261"> throw new IllegalArgumentException(&quot;chainName cannot be null or empty.&quot;);</span>
}
<span class="fc" id="L263"> Filter filter = getFilter(filterName);</span>
<span class="fc bfc" id="L264" title="All 2 branches covered."> if (filter == null) {</span>
<span class="fc" id="L265"> throw new IllegalArgumentException(&quot;There is no filter with name '&quot; + filterName +</span>
&quot;' to apply to chain [&quot; + chainName + &quot;] in the pool of available Filters. Ensure a &quot; +
&quot;filter with that name/path has first been registered with the addFilter method(s).&quot;);
}
<span class="fc" id="L270"> applyChainConfig(chainName, filter, chainSpecificFilterConfig);</span>
<span class="fc" id="L272"> NamedFilterList chain = ensureChain(chainName);</span>
<span class="fc" id="L273"> chain.add(filter);</span>
<span class="fc" id="L274"> }</span>
protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
<span class="fc bfc" id="L277" title="All 2 branches covered."> if (log.isDebugEnabled()) {</span>
<span class="fc" id="L278"> log.debug(&quot;Attempting to apply path [&quot; + chainName + &quot;] to filter [&quot; + filter + &quot;] &quot; +</span>
&quot;with config [&quot; + chainSpecificFilterConfig + &quot;]&quot;);
}
<span class="fc bfc" id="L281" title="All 2 branches covered."> if (filter instanceof PathConfigProcessor) {</span>
<span class="fc" id="L282"> ((PathConfigProcessor) filter).processPathConfig(chainName, chainSpecificFilterConfig);</span>
} else {
<span class="fc bfc" id="L284" title="All 2 branches covered."> if (StringUtils.hasText(chainSpecificFilterConfig)) {</span>
//they specified a filter configuration, but the Filter doesn't implement PathConfigProcessor
//this is an erroneous config:
<span class="fc" id="L287"> String msg = &quot;chainSpecificFilterConfig was specified, but the underlying &quot; +</span>
&quot;Filter instance is not an 'instanceof' &quot; +
<span class="fc" id="L289"> PathConfigProcessor.class.getName() + &quot;. This is required if the filter is to accept &quot; +</span>
&quot;chain-specific configuration.&quot;;
<span class="fc" id="L291"> throw new ConfigurationException(msg);</span>
}
}
<span class="fc" id="L294"> }</span>
protected NamedFilterList ensureChain(String chainName) {
<span class="fc" id="L297"> NamedFilterList chain = getChain(chainName);</span>
<span class="fc bfc" id="L298" title="All 2 branches covered."> if (chain == null) {</span>
<span class="fc" id="L299"> chain = new SimpleNamedFilterList(chainName);</span>
<span class="fc" id="L300"> this.filterChains.put(chainName, chain);</span>
}
<span class="fc" id="L302"> return chain;</span>
}
public NamedFilterList getChain(String chainName) {
<span class="fc" id="L306"> return this.filterChains.get(chainName);</span>
}
public boolean hasChains() {
<span class="fc bfc" id="L310" title="All 2 branches covered."> return !CollectionUtils.isEmpty(this.filterChains);</span>
}
public Set&lt;String&gt; getChainNames() {
//noinspection unchecked
<span class="pc bpc" id="L315" title="1 of 2 branches missed."> return this.filterChains != null ? this.filterChains.keySet() : Collections.EMPTY_SET;</span>
}
public FilterChain proxy(FilterChain original, String chainName) {
<span class="fc" id="L319"> NamedFilterList configured = getChain(chainName);</span>
<span class="fc bfc" id="L320" title="All 2 branches covered."> if (configured == null) {</span>
<span class="fc" id="L321"> String msg = &quot;There is no configured chain under the name/key [&quot; + chainName + &quot;].&quot;;</span>
<span class="fc" id="L322"> throw new IllegalArgumentException(msg);</span>
}
<span class="fc" id="L324"> return configured.proxy(original);</span>
}
/**
* Initializes the filter by calling &lt;code&gt;filter.init( {@link #getFilterConfig() getFilterConfig()} );&lt;/code&gt;.
*
* @param filter the filter to initialize with the {@code FilterConfig}.
*/
protected void initFilter(Filter filter) {
<span class="fc" id="L333"> FilterConfig filterConfig = getFilterConfig();</span>
<span class="pc bpc" id="L334" title="1 of 2 branches missed."> if (filterConfig == null) {</span>
<span class="nc" id="L335"> throw new IllegalStateException(&quot;FilterConfig attribute has not been set. This must occur before filter &quot; +</span>
&quot;initialization can occur.&quot;);
}
try {
<span class="fc" id="L339"> filter.init(filterConfig);</span>
<span class="nc" id="L340"> } catch (ServletException e) {</span>
<span class="nc" id="L341"> throw new ConfigurationException(e);</span>
<span class="fc" id="L342"> }</span>
<span class="fc" id="L343"> }</span>
protected void addDefaultFilters(boolean init) {
<span class="fc bfc" id="L346" title="All 2 branches covered."> for (DefaultFilter defaultFilter : DefaultFilter.values()) {</span>
<span class="fc" id="L347"> addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);</span>
}
<span class="fc" id="L349"> }</span>
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.eclemma.org/jacoco">JaCoCo</a> 0.7.7.201606060606</span></div></body></html>