diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
index 4d44fa564e..866b81e0f7 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
@@ -18,11 +18,8 @@ import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
-import org.springframework.security.intercept.web.AbstractFilterInvocationDefinitionSource;
-import org.springframework.security.intercept.web.FilterInvocationDefinitionMap;
+import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
-import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap;
-import org.springframework.security.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
import org.springframework.security.securechannel.ChannelDecisionManagerImpl;
import org.springframework.security.securechannel.ChannelProcessingFilter;
import org.springframework.security.securechannel.InsecureChannelProcessor;
@@ -33,6 +30,7 @@ import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.util.RegexUrlPathMatcher;
import org.springframework.security.util.AntUrlPathMatcher;
+import org.springframework.security.util.UrlMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -65,6 +63,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String OPT_REQUIRES_HTTPS = "https";
static final String OPT_ANY_CHANNEL = "any";
+ static final String ATT_HTTP_METHOD = "method";
+
static final String ATT_CREATE_SESSION = "create-session";
static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
static final String OPT_CREATE_SESSION_ALWAYS = "always";
@@ -118,8 +118,13 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
patternType = DEF_PATH_TYPE_ANT;
}
- FilterInvocationDefinitionMap interceptorFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap();
- FilterInvocationDefinitionMap channelFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap();
+ boolean useRegex = patternType.equals(OPT_PATH_TYPE_REGEX);
+
+ UrlMatcher matcher = new AntUrlPathMatcher();
+
+ if (useRegex) {
+ matcher = new RegexUrlPathMatcher();
+ }
// Deal with lowercase conversion requests
String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS);
@@ -127,32 +132,26 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
lowercaseComparisons = null;
}
+
// Only change from the defaults if the attribute has been set
if ("true".equals(lowercaseComparisons)) {
- interceptorFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(true);
- channelFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(true);
+ if (useRegex) {
+ ((RegexUrlPathMatcher)matcher).setRequiresLowerCaseUrl(true);
+ }
+ // Default for ant is already to force lower case
} else if ("false".equals(lowercaseComparisons)) {
- interceptorFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(false);
- channelFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(false);
- }
-
- if (patternType.equals(OPT_PATH_TYPE_REGEX)) {
- RegexUrlPathMatcher matcher = new RegexUrlPathMatcher();
-
- if (lowercaseComparisons != null) {
- matcher.setRequiresLowerCaseUrl("true".equals(lowercaseComparisons));
+ if (!useRegex) {
+ ((AntUrlPathMatcher)matcher).setRequiresLowerCaseUrl(false);
}
+ // Default for regex is no change
+ }
- filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
-
- interceptorFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap();
- channelFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap();
- } else if (lowercaseComparisons != null) {
- AntUrlPathMatcher matcher = new AntUrlPathMatcher();
- matcher.setRequiresLowerCaseUrl("true".equals(lowercaseComparisons));
+ DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource =
+ new DefaultFilterInvocationDefinitionSource(matcher);
+ DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
+ new DefaultFilterInvocationDefinitionSource(matcher);
- filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
- }
+ filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
// Add servlet-api integration filter if required
String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
@@ -181,11 +180,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterSecurityInterceptorBuilder.addPropertyValue("authenticationManager",
ConfigUtils.registerProviderManagerIfNecessary(parserContext));
+ // SEC-501 - should paths stored in request maps be converted to lower case
+ // true if Ant path and using lower case
+ boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
+
parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"),
- filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext);
+ filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource,
+ convertPathsToLowerCase, parserContext);
// Check if we need to register the channel processing beans
- if (((AbstractFilterInvocationDefinitionSource)channelFilterInvDefSource).getMapSize() > 0) {
+ if (((DefaultFilterInvocationDefinitionSource)channelFilterInvDefSource).getMapSize() > 0) {
// At least one channel requirement has been specified
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
@@ -268,8 +272,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
* FilterInvocationDefinitionSource used in FilterSecurityInterceptor.
*/
private void parseInterceptUrls(List urlElts, Map filterChainMap,
- FilterInvocationDefinitionMap interceptorFilterInvDefSource,
- FilterInvocationDefinitionMap channelFilterInvDefSource, ParserContext parserContext) {
+ DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource,
+ DefaultFilterInvocationDefinitionSource channelFilterInvDefSource,
+ boolean useLowerCasePaths, ParserContext parserContext) {
Iterator urlEltsIterator = urlElts.iterator();
@@ -279,6 +284,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
Element urlElt = (Element) urlEltsIterator.next();
String path = urlElt.getAttribute(ATT_PATH_PATTERN);
+ if (useLowerCasePaths) {
+ path = path.toLowerCase();
+ }
+
+ String method = urlElt.getAttribute(ATT_HTTP_METHOD);
+ if (!StringUtils.hasText(method)) {
+ method = null;
+ }
Assert.hasText(path, "path attribute cannot be empty or null");
@@ -287,7 +300,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
// Convert the comma-separated list of access attributes to a ConfigAttributeDefinition
if (StringUtils.hasText(access)) {
editor.setAsText(access);
- interceptorFilterInvDefSource.addSecureUrl(path, (ConfigAttributeDefinition) editor.getValue());
+ interceptorFilterInvDefSource.addSecureUrl(path, method, (ConfigAttributeDefinition) editor.getValue());
}
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
diff --git a/core/src/main/java/org/springframework/security/intercept/web/AbstractFilterInvocationDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/web/AbstractFilterInvocationDefinitionSource.java
deleted file mode 100644
index ffd5872725..0000000000
--- a/core/src/main/java/org/springframework/security/intercept/web/AbstractFilterInvocationDefinitionSource.java
+++ /dev/null
@@ -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 FilterInvocationDefinitionSource.
- *
- * Stores an ordered map of compiled URL paths to ConfigAttributeDefinitions and provides URL matching - * against the items stored in this map using the confgured UrlMatcher. - *
- * The order of registering the regular expressions using the {@link #addSecureUrl(String,
- * ConfigAttributeDefinition)} is very important. The system will identify the first 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 UrlMatcher to
- * process the pattern if required, using its compile method. The returned object will be used as the key
- * to the request map and will be passed back to the UrlMatcher 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 ConfigAttributeDefinition for the specified
- * FilterInvocation.
- *
- * By default, iterates through the stored URL map and calls the - * {@link UrlMatcher#pathMatchesUrl(Object path, String url)} method until a match is found. - *
- * Subclasses can override if required to perform any modifications to the URL. - *
- * Public visiblity so that tablibs or other view helper classes can access the
- * ConfigAttributeDefinition applying to a given URI pattern without needing to construct a mock
- * FilterInvocation and retrieving the attibutes via the {@link #getAttributes(Object)} method.
- *
- * @param url the URI to retrieve configuration attributes for
- *
- * @return the ConfigAttributeDefinition that applies to the specified FilterInvocation
- * 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();
- }
-}
diff --git a/core/src/main/java/org/springframework/security/intercept/web/DefaultFilterInvocationDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/web/DefaultFilterInvocationDefinitionSource.java
new file mode 100644
index 0000000000..e1d77b3115
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/intercept/web/DefaultFilterInvocationDefinitionSource.java
@@ -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 FilterInvocationDefinitionSource.
+ *
+ * Stores an ordered map of compiled URL paths to ConfigAttributeDefinitions and provides URL matching + * against the items stored in this map using the configured UrlMatcher. + *
+ * The order of registering the regular expressions using the + * {@link #addSecureUrl(String, ConfigAttributeDefinition)} is very important. + * The system will identify the first 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. + *
+ * 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 ConfigAttributeDefinitions
+ * 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 UrlMatcher to
+ * process the pattern if required, using its compile method. The returned object will be used as the key
+ * to the request map and will be passed back to the UrlMatcher 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 ConfigAttributeDefinitions 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 ConfigAttributeDefinition for the specified
+ * FilterInvocation.
+ *
+ * By default, iterates through the stored URL map and calls the + * {@link UrlMatcher#pathMatchesUrl(Object path, String url)} method until a match is found. + *
+ * 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 ConfigAttributeDefinition that applies to the specified FilterInvocation
+ * 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;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java b/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
index 5951c35196..1e8ca652ca 100644
--- a/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
+++ b/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
@@ -29,13 +29,11 @@ public class FIDSToFilterChainMapConverter {
// TODO: Check if this is necessary. Retained from refactoring of FilterChainProxy
Assert.notNull(source.getConfigAttributeDefinitions(), "FilterChainProxy requires the " +
"FilterInvocationDefinitionSource to return a non-null response to getConfigAttributeDefinitions()");
- Assert.isTrue(
- source instanceof PathBasedFilterInvocationDefinitionMap ||
- source instanceof RegExpBasedFilterInvocationDefinitionMap,
+ Assert.isTrue(source instanceof DefaultFilterInvocationDefinitionSource,
"Can't handle FilterInvocationDefinitionSource type " + source.getClass());
- AbstractFilterInvocationDefinitionSource fids = (AbstractFilterInvocationDefinitionSource)source;
+ DefaultFilterInvocationDefinitionSource fids = (DefaultFilterInvocationDefinitionSource)source;
Map requestMap = fids.getRequestMap();
Iterator paths = requestMap.keySet().iterator();
diff --git a/core/src/main/java/org/springframework/security/intercept/web/FilterInvocationDefinitionSourceEditor.java b/core/src/main/java/org/springframework/security/intercept/web/FilterInvocationDefinitionSourceEditor.java
index 5e2c16ee70..2d0965f973 100644
--- a/core/src/main/java/org/springframework/security/intercept/web/FilterInvocationDefinitionSourceEditor.java
+++ b/core/src/main/java/org/springframework/security/intercept/web/FilterInvocationDefinitionSourceEditor.java
@@ -23,20 +23,31 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.security.util.StringSplitUtils;
+import org.springframework.security.util.RegexUrlPathMatcher;
+import org.springframework.security.util.UrlMatcher;
+import org.springframework.security.util.AntUrlPathMatcher;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
- * Property editor to assist with the setup of a {@link FilterInvocationDefinitionSource}.
The class creates and - * populates a {@link RegExpBasedFilterInvocationDefinitionMap} or {@link PathBasedFilterInvocationDefinitionMap} - * (depending on the type of patterns presented).
- *By default the class treats presented patterns as regular expressions. If the keyword + * Property editor to assist with the setup of a {@link FilterInvocationDefinitionSource}. + *
+ * Note that from version 2.0, the use of property-editor based configuration is deprecated in favour of namespace + * configuration options. + *
+ * The class creates and populates a + * {@link org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource} + * using either an Ant or Regular Expression URL matching strategy depending on the type of patterns presented. + *
+ * By default the class treats presented patterns as regular expressions. If the keyword
* PATTERN_TYPE_APACHE_ANT is present (case sensitive), patterns will be treated as Apache Ant paths
- * rather than regular expressions.
* Apache Ant path expressions are used to match a HTTP request URL against a ConfigAttributeDefinition.
@@ -39,42 +36,29 @@ import java.util.Iterator;
*
* @author Ben Alex
* @author Luke taylor
+ * @deprecated DefaultFilterInvocationDefinitionSource should now be used with an AntUrlPathMatcher instead.
* @version $Id$
*/
-public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvocationDefinitionSource
+public class PathBasedFilterInvocationDefinitionMap extends DefaultFilterInvocationDefinitionSource
implements FilterInvocationDefinition {
//~ Constructors ===================================================================================================
public PathBasedFilterInvocationDefinitionMap() {
super(new AntUrlPathMatcher());
+ setStripQueryStringFromUrls(true);
}
//~ Methods ========================================================================================================
- public void addSecureUrl(String antPath, ConfigAttributeDefinition attr) {
+ public void addSecureUrl(String antPath, String method, ConfigAttributeDefinition attr) {
// SEC-501: If using lower case comparison, we should convert the paths to lower case
// as any upper case characters included by mistake will prevent the URL from ever being matched.
if (getUrlMatcher().requiresLowerCaseUrl()) {
antPath = antPath.toLowerCase();
}
- super.addSecureUrl(antPath, attr);
- }
-
- public Iterator getConfigAttributeDefinitions() {
- return getRequestMap().values().iterator();
- }
-
- public ConfigAttributeDefinition lookupAttributes(String url) {
- // 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);
- }
-
- return super.lookupAttributes(url);
+ super.addSecureUrl(antPath, method, attr);
}
public void setConvertUrlToLowercaseBeforeComparison(boolean bool) {
diff --git a/core/src/main/java/org/springframework/security/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java b/core/src/main/java/org/springframework/security/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java
index 731f191fd8..0887e7f9fc 100644
--- a/core/src/main/java/org/springframework/security/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java
+++ b/core/src/main/java/org/springframework/security/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java
@@ -16,18 +16,18 @@
package org.springframework.security.intercept.web;
import org.springframework.security.util.RegexUrlPathMatcher;
-import org.springframework.security.util.AntUrlPathMatcher;
/**
- * Configures an {@link AbstractFilterInvocationDefinitionSource} with a regular expression URL matching strategy
+ * Configures an {@link DefaultFilterInvocationDefinitionSource} with a regular expression URL matching strategy
* {@link RegexUrlPathMatcher}.
*
* @author Ben Alex
* @author Luke Taylor
+ * @deprecated
* @version $Id$
*/
-public class RegExpBasedFilterInvocationDefinitionMap extends AbstractFilterInvocationDefinitionSource
+public class RegExpBasedFilterInvocationDefinitionMap extends DefaultFilterInvocationDefinitionSource
implements FilterInvocationDefinition {
//~ Constructors ===================================================================================================
diff --git a/core/src/main/java/org/springframework/security/util/FilterInvocationUtils.java b/core/src/main/java/org/springframework/security/util/FilterInvocationUtils.java
index 77c2fdf98f..5c4a287b9b 100644
--- a/core/src/main/java/org/springframework/security/util/FilterInvocationUtils.java
+++ b/core/src/main/java/org/springframework/security/util/FilterInvocationUtils.java
@@ -48,7 +48,7 @@ public final class FilterInvocationUtils {
/**
* Creates a FilterInvocation for the specified contextPath and Uri.
- * Note the normal subclasses of AbstractFilterInvocationDefinitionSource disregard the
+ * Note the normal subclasses of DefaultFilterInvocationDefinitionSource disregard the
* contextPath when evaluating which secure object metadata applies to a given
* FilterInvocation, so generally the contextPath is unimportant unless you are using a
* custom FilterInvocationDefinitionSource.
diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
index a8cfe14b31..ca20abcae7 100644
--- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
+++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc
@@ -132,6 +132,10 @@ intercept-url.attlist &=
intercept-url.attlist &=
## The access configuration attributes that apply for the configured path.
attribute access {xsd:string}?
+intercept-url.attlist &=
+ ## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method.
+ attribute method {"GET" | "PUT" | "POST" | "DELETE"}?
+
intercept-url.attlist &=
## The filter list for the path. Currently can be set to "none" to remove a path from having any filters applied. The full filter stack (consisting of all defined filters, will be applied to any other paths).
attribute filters {"none"}?
diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
index 85b62961ba..c3682123d3 100644
--- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
+++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd
@@ -355,6 +355,19 @@