16 changed files with 515 additions and 299 deletions
@ -1,152 +0,0 @@
@@ -1,152 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
||||
* |
||||
* 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.springframework.security.intercept.web; |
||||
|
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
import org.springframework.security.util.UrlMatcher; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import java.util.Map; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Iterator; |
||||
|
||||
|
||||
/** |
||||
* Abstract implementation of <Code>FilterInvocationDefinitionSource</code>. |
||||
* <p> |
||||
* Stores an ordered map of compiled URL paths to <tt>ConfigAttributeDefinition</tt>s and provides URL matching |
||||
* against the items stored in this map using the confgured <tt>UrlMatcher</tt>. |
||||
* <p> |
||||
* The order of registering the regular expressions using the {@link #addSecureUrl(String, |
||||
* ConfigAttributeDefinition)} is very important. The system will identify the <b>first</b> matching regular |
||||
* expression for a given HTTP URL. It will not proceed to evaluate later regular expressions if a match has already |
||||
* been found. Accordingly, the most specific regular expressions should be registered first, with the most general |
||||
* regular expressions registered last. |
||||
* |
||||
* @author Ben Alex |
||||
* @author Luke Taylor |
||||
* @version $Id$ |
||||
*/ |
||||
public abstract class AbstractFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource { |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private Map requestMap = new LinkedHashMap(); |
||||
|
||||
private UrlMatcher urlMatcher; |
||||
|
||||
protected AbstractFilterInvocationDefinitionSource(UrlMatcher urlMatcher) { |
||||
this.urlMatcher = urlMatcher; |
||||
} |
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/** |
||||
* Adds a URL-ConfigAttributeDefinition pair to the request map, first allowing the <tt>UrlMatcher</tt> to |
||||
* process the pattern if required, using its <tt>compile</tt> method. The returned object will be used as the key |
||||
* to the request map and will be passed back to the <tt>UrlMatcher</tt> when iterating through the map to find |
||||
* a match for a particular URL. |
||||
*/ |
||||
public void addSecureUrl(String pattern, ConfigAttributeDefinition attr) { |
||||
requestMap.put(urlMatcher.compile(pattern), attr); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Added URL pattern: " + pattern + "; attributes: " + attr); |
||||
} |
||||
} |
||||
|
||||
public Iterator getConfigAttributeDefinitions() { |
||||
return getRequestMap().values().iterator(); |
||||
} |
||||
|
||||
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { |
||||
if ((object == null) || !this.supports(object.getClass())) { |
||||
throw new IllegalArgumentException("Object must be a FilterInvocation"); |
||||
} |
||||
|
||||
String url = ((FilterInvocation) object).getRequestUrl(); |
||||
|
||||
return lookupAttributes(url); |
||||
} |
||||
|
||||
/** |
||||
* Performs the actual lookup of the relevant <code>ConfigAttributeDefinition</code> for the specified |
||||
* <code>FilterInvocation</code>. |
||||
* <p> |
||||
* By default, iterates through the stored URL map and calls the |
||||
* {@link UrlMatcher#pathMatchesUrl(Object path, String url)} method until a match is found. |
||||
* <p> |
||||
* Subclasses can override if required to perform any modifications to the URL. |
||||
* <p> |
||||
* Public visiblity so that tablibs or other view helper classes can access the |
||||
* <code>ConfigAttributeDefinition</code> applying to a given URI pattern without needing to construct a mock |
||||
* <code>FilterInvocation</code> and retrieving the attibutes via the {@link #getAttributes(Object)} method. |
||||
* |
||||
* @param url the URI to retrieve configuration attributes for |
||||
* |
||||
* @return the <code>ConfigAttributeDefinition</code> that applies to the specified <code>FilterInvocation</code> |
||||
* or null if no match is foud |
||||
*/ |
||||
public ConfigAttributeDefinition lookupAttributes(String url) { |
||||
if (urlMatcher.requiresLowerCaseUrl()) { |
||||
url = url.toLowerCase(); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'"); |
||||
} |
||||
} |
||||
|
||||
Iterator entries = requestMap.entrySet().iterator(); |
||||
|
||||
while (entries.hasNext()) { |
||||
Map.Entry entry = (Map.Entry) entries.next(); |
||||
Object p = entry.getKey(); |
||||
boolean matched = urlMatcher.pathMatchesUrl(p, url); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Candidate is: '" + url + "'; pattern is " + p + "; matched=" + matched); |
||||
} |
||||
|
||||
if (matched) { |
||||
return (ConfigAttributeDefinition) entry.getValue(); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public boolean supports(Class clazz) { |
||||
return FilterInvocation.class.isAssignableFrom(clazz); |
||||
} |
||||
|
||||
public int getMapSize() { |
||||
return this.requestMap.size(); |
||||
} |
||||
|
||||
Map getRequestMap() { |
||||
return requestMap; |
||||
} |
||||
|
||||
protected UrlMatcher getUrlMatcher() { |
||||
return urlMatcher; |
||||
} |
||||
|
||||
public boolean isConvertUrlToLowercaseBeforeComparison() { |
||||
return urlMatcher.requiresLowerCaseUrl(); |
||||
} |
||||
} |
||||
@ -0,0 +1,263 @@
@@ -0,0 +1,263 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
||||
* |
||||
* 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.springframework.security.intercept.web; |
||||
|
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
import org.springframework.security.SecurityConfig; |
||||
import org.springframework.security.util.UrlMatcher; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import java.util.Map; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Iterator; |
||||
import java.util.HashMap; |
||||
import java.util.Set; |
||||
import java.util.HashSet; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* Default implementation of <tt>FilterInvocationDefinitionSource</tt>. |
||||
* <p> |
||||
* Stores an ordered map of compiled URL paths to <tt>ConfigAttributeDefinition</tt>s and provides URL matching |
||||
* against the items stored in this map using the configured <tt>UrlMatcher</tt>. |
||||
* <p> |
||||
* The order of registering the regular expressions using the |
||||
* {@link #addSecureUrl(String, ConfigAttributeDefinition)} is very important. |
||||
* The system will identify the <b>first</b> matching regular |
||||
* expression for a given HTTP URL. It will not proceed to evaluate later regular expressions if a match has already |
||||
* been found. Accordingly, the most specific regular expressions should be registered first, with the most general |
||||
* regular expressions registered last. |
||||
* <p> |
||||
* If URLs are registered for a particular HTTP method using |
||||
* {@link #addSecureUrl(String, String, ConfigAttributeDefinition)}, then the method-specific matches will take |
||||
* precedence over any URLs which are registered without an HTTP method. |
||||
* |
||||
* @author Ben Alex |
||||
* @author Luke Taylor |
||||
* @version $Id$ |
||||
*/ |
||||
public class DefaultFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource { |
||||
|
||||
private static final Set HTTP_METHODS = new HashSet(Arrays.asList(new String[]{ "GET", "PUT", "DELETE", "POST" })); |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
/** |
||||
* Non method-specific map of URL patterns to <tt>ConfigAttributeDefinition</tt>s |
||||
* TODO: Store in the httpMethod map with null key. |
||||
*/ |
||||
private Map requestMap = new LinkedHashMap(); |
||||
/** Stores request maps keyed by specific HTTP methods */ |
||||
private Map httpMethodMap = new HashMap(); |
||||
|
||||
private UrlMatcher urlMatcher; |
||||
|
||||
private boolean stripQueryStringFromUrls; |
||||
|
||||
/** |
||||
* Creates a FilterInvocationDefinitionSource with the supplied URL matching strategy. |
||||
* @param urlMatcher |
||||
*/ |
||||
public DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher) { |
||||
this.urlMatcher = urlMatcher; |
||||
} |
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void addSecureUrl(String pattern, ConfigAttributeDefinition attr) { |
||||
addSecureUrl(pattern, null, attr); |
||||
} |
||||
|
||||
/** |
||||
* Adds a URL-ConfigAttributeDefinition pair to the request map, first allowing the <tt>UrlMatcher</tt> to |
||||
* process the pattern if required, using its <tt>compile</tt> method. The returned object will be used as the key |
||||
* to the request map and will be passed back to the <tt>UrlMatcher</tt> when iterating through the map to find |
||||
* a match for a particular URL. |
||||
*/ |
||||
public void addSecureUrl(String pattern, String method, ConfigAttributeDefinition attr) { |
||||
Map mapToUse = getRequestMapForHttpMethod(method); |
||||
|
||||
mapToUse.put(urlMatcher.compile(pattern), attr); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Added URL pattern: " + pattern + "; attributes: " + attr + |
||||
(method == null ? "" : " for HTTP method '" + method + "'")); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return the HTTP method specific request map, creating it if it doesn't already exist. |
||||
* @param method GET, POST etc |
||||
* @return map of URL patterns to <tt>ConfigAttributeDefinition</tt>s for this method. |
||||
*/ |
||||
private Map getRequestMapForHttpMethod(String method) { |
||||
if (method == null) { |
||||
return requestMap; |
||||
} |
||||
if (!HTTP_METHODS.contains(method)) { |
||||
throw new IllegalArgumentException("Unrecognised HTTP method: '" + method + "'"); |
||||
} |
||||
|
||||
Map methodRequestmap = (Map) httpMethodMap.get(method); |
||||
|
||||
if (methodRequestmap == null) { |
||||
methodRequestmap = new LinkedHashMap(); |
||||
httpMethodMap.put(method, methodRequestmap); |
||||
} |
||||
|
||||
return methodRequestmap; |
||||
} |
||||
|
||||
public Iterator getConfigAttributeDefinitions() { |
||||
return getRequestMap().values().iterator(); |
||||
} |
||||
|
||||
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { |
||||
if ((object == null) || !this.supports(object.getClass())) { |
||||
throw new IllegalArgumentException("Object must be a FilterInvocation"); |
||||
} |
||||
|
||||
String url = ((FilterInvocation) object).getRequestUrl(); |
||||
String method = ((FilterInvocation) object).getHttpRequest().getMethod(); |
||||
|
||||
return lookupAttributes(url, method); |
||||
} |
||||
|
||||
protected ConfigAttributeDefinition lookupAttributes(String url) { |
||||
return lookupAttributes(url, null); |
||||
} |
||||
|
||||
/** |
||||
* Performs the actual lookup of the relevant <code>ConfigAttributeDefinition</code> for the specified |
||||
* <code>FilterInvocation</code>. |
||||
* <p> |
||||
* By default, iterates through the stored URL map and calls the |
||||
* {@link UrlMatcher#pathMatchesUrl(Object path, String url)} method until a match is found. |
||||
* <p> |
||||
* Subclasses can override if required to perform any modifications to the URL. |
||||
* |
||||
* @param url the URI to retrieve configuration attributes for |
||||
* @param method the HTTP method (GET, POST, DELETE...). |
||||
* |
||||
* @return the <code>ConfigAttributeDefinition</code> that applies to the specified <code>FilterInvocation</code> |
||||
* or null if no match is foud |
||||
*/ |
||||
protected ConfigAttributeDefinition lookupAttributes(String url, String method) { |
||||
if (stripQueryStringFromUrls) { |
||||
// Strip anything after a question mark symbol, as per SEC-161. See also SEC-321
|
||||
int firstQuestionMarkIndex = url.indexOf("?"); |
||||
|
||||
if (firstQuestionMarkIndex != -1) { |
||||
url = url.substring(0, firstQuestionMarkIndex); |
||||
} |
||||
} |
||||
|
||||
if (urlMatcher.requiresLowerCaseUrl()) { |
||||
url = url.toLowerCase(); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'"); |
||||
} |
||||
} |
||||
|
||||
ConfigAttributeDefinition attributes = null; |
||||
|
||||
Map methodSpecificMap = (Map) httpMethodMap.get(method); |
||||
|
||||
if (methodSpecificMap != null) { |
||||
attributes = lookupUrlInMap(methodSpecificMap, url); |
||||
} |
||||
|
||||
if (attributes == null) { |
||||
attributes = lookupUrlInMap(requestMap, url); |
||||
} |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
private ConfigAttributeDefinition lookupUrlInMap(Map requestMap, String url) { |
||||
Iterator entries = requestMap.entrySet().iterator(); |
||||
|
||||
while (entries.hasNext()) { |
||||
Map.Entry entry = (Map.Entry) entries.next(); |
||||
Object p = entry.getKey(); |
||||
boolean matched = urlMatcher.pathMatchesUrl(p, url); |
||||
|
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Candidate is: '" + url + "'; pattern is " + p + "; matched=" + matched); |
||||
} |
||||
|
||||
if (matched) { |
||||
return (ConfigAttributeDefinition) entry.getValue(); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Allows or easier configuration using {@link FilterInvocationDefinitionSourceMapping}. |
||||
* |
||||
* @param mappings |
||||
* {@link java.util.List} of |
||||
* {@link FilterInvocationDefinitionSourceMapping} objects. |
||||
*/ |
||||
void setMappings(List mappings) { |
||||
Iterator it = mappings.iterator(); |
||||
|
||||
while (it.hasNext()) { |
||||
FilterInvocationDefinitionSourceMapping mapping = (FilterInvocationDefinitionSourceMapping) it.next(); |
||||
ConfigAttributeDefinition configDefinition = new ConfigAttributeDefinition(); |
||||
|
||||
Iterator configAttributesIt = mapping.getConfigAttributes().iterator(); |
||||
while (configAttributesIt.hasNext()) { |
||||
String s = (String) configAttributesIt.next(); |
||||
configDefinition.addConfigAttribute(new SecurityConfig(s)); |
||||
} |
||||
|
||||
addSecureUrl(mapping.getUrl(), configDefinition); |
||||
} |
||||
} |
||||
|
||||
|
||||
public boolean supports(Class clazz) { |
||||
return FilterInvocation.class.isAssignableFrom(clazz); |
||||
} |
||||
|
||||
public int getMapSize() { |
||||
return this.requestMap.size(); |
||||
} |
||||
|
||||
Map getRequestMap() { |
||||
return requestMap; |
||||
} |
||||
|
||||
protected UrlMatcher getUrlMatcher() { |
||||
return urlMatcher; |
||||
} |
||||
|
||||
public boolean isConvertUrlToLowercaseBeforeComparison() { |
||||
return urlMatcher.requiresLowerCaseUrl(); |
||||
} |
||||
|
||||
protected void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) { |
||||
this.stripQueryStringFromUrls = stripQueryStringFromUrls; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue