|
@@ -16,25 +16,64 @@
|
|
package org.springframework.security.intercept.web;
|
|
package org.springframework.security.intercept.web;
|
|
|
|
|
|
import org.springframework.security.ConfigAttributeDefinition;
|
|
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.Map;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashMap;
|
|
|
|
+import java.util.Iterator;
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
* Abstract implementation of <Code>FilterInvocationDefinitionSource</code>.
|
|
* 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 Ben Alex
|
|
|
|
+ * @author Luke Taylor
|
|
* @version $Id$
|
|
* @version $Id$
|
|
*/
|
|
*/
|
|
public abstract class AbstractFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource {
|
|
public abstract class AbstractFilterInvocationDefinitionSource implements FilterInvocationDefinitionSource {
|
|
|
|
|
|
|
|
+ protected final Log logger = LogFactory.getLog(getClass());
|
|
|
|
+
|
|
private Map requestMap = new LinkedHashMap();
|
|
private Map requestMap = new LinkedHashMap();
|
|
|
|
|
|
- private boolean convertUrlToLowercaseBeforeComparison = false;
|
|
|
|
|
|
+ private UrlMatcher urlMatcher;
|
|
|
|
+
|
|
|
|
+ protected AbstractFilterInvocationDefinitionSource(UrlMatcher urlMatcher) {
|
|
|
|
+ this.urlMatcher = urlMatcher;
|
|
|
|
+ }
|
|
|
|
|
|
//~ Methods ========================================================================================================
|
|
//~ 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 {
|
|
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
|
|
if ((object == null) || !this.supports(object.getClass())) {
|
|
if ((object == null) || !this.supports(object.getClass())) {
|
|
throw new IllegalArgumentException("Object must be a FilterInvocation");
|
|
throw new IllegalArgumentException("Object must be a FilterInvocation");
|
|
@@ -42,24 +81,53 @@ public abstract class AbstractFilterInvocationDefinitionSource implements Filter
|
|
|
|
|
|
String url = ((FilterInvocation) object).getRequestUrl();
|
|
String url = ((FilterInvocation) object).getRequestUrl();
|
|
|
|
|
|
- return this.lookupAttributes(url);
|
|
|
|
|
|
+ return lookupAttributes(url);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Performs the actual lookup of the relevant <code>ConfigAttributeDefinition</code> for the specified
|
|
* Performs the actual lookup of the relevant <code>ConfigAttributeDefinition</code> for the specified
|
|
* <code>FilterInvocation</code>.
|
|
* <code>FilterInvocation</code>.
|
|
- * <p>Provided so subclasses need only to provide one basic method to properly interface with the
|
|
|
|
- * <code>FilterInvocationDefinitionSource</code>.
|
|
|
|
- * </p>
|
|
|
|
- * <p>Public visiblity so that tablibs or other view helper classes can access the
|
|
|
|
|
|
+ * <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>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.</p>
|
|
|
|
|
|
+ * <code>FilterInvocation</code> and retrieving the attibutes via the {@link #getAttributes(Object)} method.
|
|
*
|
|
*
|
|
* @param url the URI to retrieve configuration attributes for
|
|
* @param url the URI to retrieve configuration attributes for
|
|
*
|
|
*
|
|
* @return the <code>ConfigAttributeDefinition</code> that applies to the specified <code>FilterInvocation</code>
|
|
* @return the <code>ConfigAttributeDefinition</code> that applies to the specified <code>FilterInvocation</code>
|
|
|
|
+ * or null if no match is foud
|
|
*/
|
|
*/
|
|
- public abstract ConfigAttributeDefinition lookupAttributes(String url);
|
|
|
|
|
|
+ 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 patterns = requestMap.keySet().iterator();
|
|
|
|
+
|
|
|
|
+ while (patterns.hasNext()) {
|
|
|
|
+ Object p = patterns.next();
|
|
|
|
+ boolean matched = urlMatcher.pathMatchesUrl(p, url);
|
|
|
|
+
|
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
|
+ logger.debug("Candidate is: '" + url + "'; pattern is " + p + "; matched=" + matched);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (matched) {
|
|
|
|
+ return (ConfigAttributeDefinition) getRequestMap().get(p);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
|
|
public boolean supports(Class clazz) {
|
|
public boolean supports(Class clazz) {
|
|
return FilterInvocation.class.isAssignableFrom(clazz);
|
|
return FilterInvocation.class.isAssignableFrom(clazz);
|
|
@@ -69,15 +137,15 @@ public abstract class AbstractFilterInvocationDefinitionSource implements Filter
|
|
return this.requestMap.size();
|
|
return this.requestMap.size();
|
|
}
|
|
}
|
|
|
|
|
|
- public boolean isConvertUrlToLowercaseBeforeComparison() {
|
|
|
|
- return convertUrlToLowercaseBeforeComparison;
|
|
|
|
|
|
+ Map getRequestMap() {
|
|
|
|
+ return requestMap;
|
|
}
|
|
}
|
|
|
|
|
|
- public void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison) {
|
|
|
|
- this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
|
|
|
|
|
+ protected UrlMatcher getUrlMatcher() {
|
|
|
|
+ return urlMatcher;
|
|
}
|
|
}
|
|
|
|
|
|
- Map getRequestMap() {
|
|
|
|
- return requestMap;
|
|
|
|
|
|
+ public boolean isConvertUrlToLowercaseBeforeComparison() {
|
|
|
|
+ return urlMatcher.requiresLowerCaseUrl();
|
|
}
|
|
}
|
|
}
|
|
}
|