diff --git a/changelog.txt b/changelog.txt
index 369f4875e2..9dbc9d387d 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,9 +1,12 @@
Changes in version 0.5 (2004-xx-xx)
-----------------------------------
+
* AuthenticationProcessingFilter by default finds configuration context using Spring's WebApplicationContextUtils.getWebApplicationContext()
* AuthenticationProcessingFilter context may optionally be specified with 'contextConfigLocation' param (was previously 'appContextLocation')
* SecurityEnforcementFilter by default finds configuration context using Spring's WebApplicationContextUtils.getWebApplicationContext()
* SecurityEnforcementFilter context may optionally be specified with 'contextConfigLocation' param (was previously 'appContextLocation')
+* SecurityEnforcementFilter now supports URL definitions using the Apache Ant path syntax in addition to regular expressions
+* Documentation improvements
Changes in version 0.4 (2004-04-03)
-----------------------------------
diff --git a/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMap.java b/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMap.java
index f02394144a..bcdf4d6e75 100644
--- a/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMap.java
+++ b/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMap.java
@@ -17,164 +17,19 @@ package net.sf.acegisecurity.intercept.web;
import net.sf.acegisecurity.ConfigAttributeDefinition;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.oro.text.regex.MalformedPatternException;
-import org.apache.oro.text.regex.Pattern;
-import org.apache.oro.text.regex.PatternMatcher;
-import org.apache.oro.text.regex.Perl5Compiler;
-import org.apache.oro.text.regex.Perl5Matcher;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.Vector;
-
/**
- * Maintains a List of ConfigAttributeDefinitions
- * associated with different HTTP request URL patterns.
- *
- *
- * Regular expressions are used to match a HTTP request URL against a
- * ConfigAttributeDefinition.
- *
- *
- *
- * 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 no registered regular expressions match the HTTP URL, null
- * is returned.
- *
+ * Exposes methods required so that a property editor can populate the relevant
+ * {@link FilterInvocationDefinitionSource}.
+ *
+ * @author Ben Alex
+ * @version $Id$
*/
-public class FilterInvocationDefinitionMap
- extends AbstractFilterInvocationDefinitionSource {
- //~ Static fields/initializers =============================================
-
- private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionMap.class);
-
- //~ Instance fields ========================================================
-
- private List requestMap = new Vector();
- private boolean convertUrlToLowercaseBeforeComparison = false;
-
+public interface FilterInvocationDefinitionMap {
//~ Methods ================================================================
- public Iterator getConfigAttributeDefinitions() {
- Set set = new HashSet();
- Iterator iter = requestMap.iterator();
-
- while (iter.hasNext()) {
- EntryHolder entryHolder = (EntryHolder) iter.next();
- set.add(entryHolder.getConfigAttributeDefinition());
- }
-
- return set.iterator();
- }
-
public void setConvertUrlToLowercaseBeforeComparison(
- boolean convertUrlToLowercaseBeforeComparison) {
- this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
- }
+ boolean convertUrlToLowercaseBeforeComparison);
- public boolean isConvertUrlToLowercaseBeforeComparison() {
- return convertUrlToLowercaseBeforeComparison;
- }
-
- public int getMapSize() {
- return this.requestMap.size();
- }
-
- public void addSecureUrl(String perl5RegExp, ConfigAttributeDefinition attr) {
- Pattern compiledPattern;
- Perl5Compiler compiler = new Perl5Compiler();
-
- try {
- compiledPattern = compiler.compile(perl5RegExp,
- Perl5Compiler.READ_ONLY_MASK);
- } catch (MalformedPatternException mpe) {
- throw new IllegalArgumentException("Malformed regular expression: "
- + perl5RegExp);
- }
-
- requestMap.add(new EntryHolder(compiledPattern, attr));
-
- if (logger.isDebugEnabled()) {
- logger.debug("Added regular expression: "
- + compiledPattern.getPattern().toString() + "; attributes: "
- + attr.toString());
- }
- }
-
- protected ConfigAttributeDefinition lookupAttributes(
- FilterInvocation filterInvocation) {
- PatternMatcher matcher = new Perl5Matcher();
-
- Iterator iter = requestMap.iterator();
-
- String url = filterInvocation.getRequestUrl();
-
- if (convertUrlToLowercaseBeforeComparison) {
- url = url.toLowerCase();
-
- if (logger.isDebugEnabled()) {
- logger.debug("Converted URL to lowercase, from: '"
- + filterInvocation.getRequest() + "'; to: '" + url + "'");
- }
- }
-
- while (iter.hasNext()) {
- EntryHolder entryHolder = (EntryHolder) iter.next();
-
- boolean matched = matcher.matches(url,
- entryHolder.getCompiledPattern());
-
- if (logger.isDebugEnabled()) {
- logger.debug("Candidate is: '" + url + "'; pattern is "
- + entryHolder.getCompiledPattern().getPattern()
- + "; matched=" + matched);
- }
-
- if (matched) {
- return entryHolder.getConfigAttributeDefinition();
- }
- }
-
- return null;
- }
-
- //~ Inner Classes ==========================================================
-
- protected class EntryHolder {
- private ConfigAttributeDefinition configAttributeDefinition;
- private Pattern compiledPattern;
-
- public EntryHolder(Pattern compiledPattern,
- ConfigAttributeDefinition attr) {
- this.compiledPattern = compiledPattern;
- this.configAttributeDefinition = attr;
- }
-
- protected EntryHolder() {
- throw new IllegalArgumentException("Cannot use default constructor");
- }
-
- public Pattern getCompiledPattern() {
- return compiledPattern;
- }
-
- public ConfigAttributeDefinition getConfigAttributeDefinition() {
- return configAttributeDefinition;
- }
- }
+ public void addSecureUrl(String expression, ConfigAttributeDefinition attr);
}
diff --git a/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditor.java b/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditor.java
index 3f4c10ba43..58c75f1f24 100644
--- a/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditor.java
+++ b/core/src/main/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditor.java
@@ -31,11 +31,21 @@ import java.io.StringReader;
/**
- * Property editor to assist with the setup of {@link
+ * Property editor to assist with the setup of a {@link
* FilterInvocationDefinitionSource}.
*
*
- * The class creates and populates a {@link FilterInvocationDefinitionMap}.
+ * 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 PATTERN_TYPE_APACHE_ANT is present (case
+ * sensitive), patterns will be treated as Apache Ant paths rather than
+ * regular expressions.
*
*
* @author Ben Alex
@@ -50,11 +60,20 @@ public class FilterInvocationDefinitionSourceEditor
//~ Methods ================================================================
public void setAsText(String s) throws IllegalArgumentException {
- FilterInvocationDefinitionMap source = new FilterInvocationDefinitionMap();
+ FilterInvocationDefinitionMap source = new RegExpBasedFilterInvocationDefinitionMap();
if ((s == null) || "".equals(s)) {
- // Leave value in property editor null
+ // Leave target object empty
} else {
+ // Check if we need to override the default definition map
+ if (s.lastIndexOf("PATTERN_TYPE_APACHE_ANT") != -1) {
+ source = new PathBasedFilterInvocationDefinitionMap();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(("Detected PATTERN_TYPE_APACHE_ANT directive; using Apache Ant style path expressions"));
+ }
+ }
+
BufferedReader br = new BufferedReader(new StringReader(s));
int counter = 0;
String line;
diff --git a/core/src/main/java/org/acegisecurity/intercept/web/PathBasedFilterInvocationDefinitionMap.java b/core/src/main/java/org/acegisecurity/intercept/web/PathBasedFilterInvocationDefinitionMap.java
new file mode 100644
index 0000000000..e26d604175
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/intercept/web/PathBasedFilterInvocationDefinitionMap.java
@@ -0,0 +1,158 @@
+/* Copyright 2004 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 net.sf.acegisecurity.intercept.web;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.util.PathMatcher;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+
+/**
+ * Maintains a List of ConfigAttributeDefinitions
+ * associated with different HTTP request URL Apache Ant path-based patterns.
+ *
+ *
+ * Apache Ant path expressions are used to match a HTTP request URL against a
+ * ConfigAttributeDefinition.
+ *
+ *
+ *
+ * The order of registering the Ant paths using the {@link
+ * #addSecureUrl(String, ConfigAttributeDefinition)} is very important. The
+ * system will identify the first matching path for a given HTTP URL.
+ * It will not proceed to evaluate later paths if a match has already been
+ * found. Accordingly, the most specific paths should be registered first,
+ * with the most general paths registered last.
+ *
+ *
+ *
+ * If no registered paths match the HTTP URL, null is returned.
+ *
+ */
+public class PathBasedFilterInvocationDefinitionMap
+ extends AbstractFilterInvocationDefinitionSource
+ implements FilterInvocationDefinitionMap {
+ //~ Static fields/initializers =============================================
+
+ private static final Log logger = LogFactory.getLog(PathBasedFilterInvocationDefinitionMap.class);
+
+ //~ Instance fields ========================================================
+
+ private List requestMap = new Vector();
+ private boolean convertUrlToLowercaseBeforeComparison = false;
+
+ //~ Methods ================================================================
+
+ public Iterator getConfigAttributeDefinitions() {
+ Set set = new HashSet();
+ Iterator iter = requestMap.iterator();
+
+ while (iter.hasNext()) {
+ EntryHolder entryHolder = (EntryHolder) iter.next();
+ set.add(entryHolder.getConfigAttributeDefinition());
+ }
+
+ return set.iterator();
+ }
+
+ public void setConvertUrlToLowercaseBeforeComparison(
+ boolean convertUrlToLowercaseBeforeComparison) {
+ this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
+ }
+
+ public boolean isConvertUrlToLowercaseBeforeComparison() {
+ return convertUrlToLowercaseBeforeComparison;
+ }
+
+ public int getMapSize() {
+ return this.requestMap.size();
+ }
+
+ public void addSecureUrl(String antPath, ConfigAttributeDefinition attr) {
+ requestMap.add(new EntryHolder(antPath, attr));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Added Ant path: " + antPath + "; attributes: "
+ + attr.toString());
+ }
+ }
+
+ protected ConfigAttributeDefinition lookupAttributes(
+ FilterInvocation filterInvocation) {
+ Iterator iter = requestMap.iterator();
+
+ String url = filterInvocation.getRequestUrl();
+
+ if (convertUrlToLowercaseBeforeComparison) {
+ url = url.toLowerCase();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Converted URL to lowercase, from: '"
+ + filterInvocation.getRequest() + "'; to: '" + url + "'");
+ }
+ }
+
+ while (iter.hasNext()) {
+ EntryHolder entryHolder = (EntryHolder) iter.next();
+
+ boolean matched = PathMatcher.match(entryHolder.getAntPath(), url);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Candidate is: '" + url + "'; pattern is "
+ + entryHolder.getAntPath() + "; matched=" + matched);
+ }
+
+ if (matched) {
+ return entryHolder.getConfigAttributeDefinition();
+ }
+ }
+
+ return null;
+ }
+
+ //~ Inner Classes ==========================================================
+
+ protected class EntryHolder {
+ private ConfigAttributeDefinition configAttributeDefinition;
+ private String antPath;
+
+ public EntryHolder(String antPath, ConfigAttributeDefinition attr) {
+ this.antPath = antPath;
+ this.configAttributeDefinition = attr;
+ }
+
+ protected EntryHolder() {
+ throw new IllegalArgumentException("Cannot use default constructor");
+ }
+
+ public String getAntPath() {
+ return antPath;
+ }
+
+ public ConfigAttributeDefinition getConfigAttributeDefinition() {
+ return configAttributeDefinition;
+ }
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java b/core/src/main/java/org/acegisecurity/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java
new file mode 100644
index 0000000000..801870b523
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/intercept/web/RegExpBasedFilterInvocationDefinitionMap.java
@@ -0,0 +1,181 @@
+/* Copyright 2004 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 net.sf.acegisecurity.intercept.web;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+
+/**
+ * Maintains a List of ConfigAttributeDefinitions
+ * associated with different HTTP request URL regular expression patterns.
+ *
+ *
+ * Regular expressions are used to match a HTTP request URL against a
+ * ConfigAttributeDefinition.
+ *
+ *
+ *
+ * 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 no registered regular expressions match the HTTP URL, null
+ * is returned.
+ *
+ */
+public class RegExpBasedFilterInvocationDefinitionMap
+ extends AbstractFilterInvocationDefinitionSource
+ implements FilterInvocationDefinitionMap {
+ //~ Static fields/initializers =============================================
+
+ private static final Log logger = LogFactory.getLog(RegExpBasedFilterInvocationDefinitionMap.class);
+
+ //~ Instance fields ========================================================
+
+ private List requestMap = new Vector();
+ private boolean convertUrlToLowercaseBeforeComparison = false;
+
+ //~ Methods ================================================================
+
+ public Iterator getConfigAttributeDefinitions() {
+ Set set = new HashSet();
+ Iterator iter = requestMap.iterator();
+
+ while (iter.hasNext()) {
+ EntryHolder entryHolder = (EntryHolder) iter.next();
+ set.add(entryHolder.getConfigAttributeDefinition());
+ }
+
+ return set.iterator();
+ }
+
+ public void setConvertUrlToLowercaseBeforeComparison(
+ boolean convertUrlToLowercaseBeforeComparison) {
+ this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
+ }
+
+ public boolean isConvertUrlToLowercaseBeforeComparison() {
+ return convertUrlToLowercaseBeforeComparison;
+ }
+
+ public int getMapSize() {
+ return this.requestMap.size();
+ }
+
+ public void addSecureUrl(String perl5RegExp, ConfigAttributeDefinition attr) {
+ Pattern compiledPattern;
+ Perl5Compiler compiler = new Perl5Compiler();
+
+ try {
+ compiledPattern = compiler.compile(perl5RegExp,
+ Perl5Compiler.READ_ONLY_MASK);
+ } catch (MalformedPatternException mpe) {
+ throw new IllegalArgumentException("Malformed regular expression: "
+ + perl5RegExp);
+ }
+
+ requestMap.add(new EntryHolder(compiledPattern, attr));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Added regular expression: "
+ + compiledPattern.getPattern().toString() + "; attributes: "
+ + attr.toString());
+ }
+ }
+
+ protected ConfigAttributeDefinition lookupAttributes(
+ FilterInvocation filterInvocation) {
+ PatternMatcher matcher = new Perl5Matcher();
+
+ Iterator iter = requestMap.iterator();
+
+ String url = filterInvocation.getRequestUrl();
+
+ if (convertUrlToLowercaseBeforeComparison) {
+ url = url.toLowerCase();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Converted URL to lowercase, from: '"
+ + filterInvocation.getRequest() + "'; to: '" + url + "'");
+ }
+ }
+
+ while (iter.hasNext()) {
+ EntryHolder entryHolder = (EntryHolder) iter.next();
+
+ boolean matched = matcher.matches(url,
+ entryHolder.getCompiledPattern());
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Candidate is: '" + url + "'; pattern is "
+ + entryHolder.getCompiledPattern().getPattern()
+ + "; matched=" + matched);
+ }
+
+ if (matched) {
+ return entryHolder.getConfigAttributeDefinition();
+ }
+ }
+
+ return null;
+ }
+
+ //~ Inner Classes ==========================================================
+
+ protected class EntryHolder {
+ private ConfigAttributeDefinition configAttributeDefinition;
+ private Pattern compiledPattern;
+
+ public EntryHolder(Pattern compiledPattern,
+ ConfigAttributeDefinition attr) {
+ this.compiledPattern = compiledPattern;
+ this.configAttributeDefinition = attr;
+ }
+
+ protected EntryHolder() {
+ throw new IllegalArgumentException("Cannot use default constructor");
+ }
+
+ public Pattern getCompiledPattern() {
+ return compiledPattern;
+ }
+
+ public ConfigAttributeDefinition getConfigAttributeDefinition() {
+ return configAttributeDefinition;
+ }
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java b/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java
index e4a4d27697..f169974a19 100644
--- a/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java
+++ b/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java
@@ -28,7 +28,7 @@ import java.util.Iterator;
/**
* Tests {@link FilterInvocationDefinitionSourceEditor} and its associated
- * {@link FilterInvocationDefinitionMap}.
+ * default {@link RegExpBasedFilterInvocationDefinitionMap}.
*
* @author Ben Alex
* @version $Id$
@@ -59,7 +59,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
}
@@ -69,16 +69,26 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\r\n\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
}
+ public void testDefaultIsRegularExpression() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ .getValue();
+ assertTrue(map instanceof RegExpBasedFilterInvocationDefinitionMap);
+ }
+
public void testEmptyStringReturnsEmptyMap() {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText("");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertEquals(0, map.getMapSize());
}
@@ -100,7 +110,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
Iterator iter = map.getConfigAttributeDefinitions();
int counter = 0;
@@ -117,7 +127,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText("\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
@@ -136,14 +146,14 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertEquals(2, map.getMapSize());
}
public void testNoArgsConstructor() {
try {
- new FilterInvocationDefinitionMap().new EntryHolder();
+ new RegExpBasedFilterInvocationDefinitionMap().new EntryHolder();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
@@ -154,7 +164,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText(null);
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertEquals(0, map.getMapSize());
}
@@ -164,7 +174,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
// Test ensures we match the first entry, not the second
@@ -188,7 +198,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
"\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER\r\n\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
@@ -210,7 +220,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText("\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
@@ -233,7 +243,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
editor.setAsText(
" \\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE \r\n \r\n \r\n // comment line \r\n \\A/testing.*\\Z=ROLE_TEST \r\n");
- FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ RegExpBasedFilterInvocationDefinitionMap map = (RegExpBasedFilterInvocationDefinitionMap) editor
.getValue();
assertEquals(2, map.getMapSize());
}
diff --git a/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorWithPathsTests.java b/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorWithPathsTests.java
new file mode 100644
index 0000000000..30512ff8e0
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorWithPathsTests.java
@@ -0,0 +1,223 @@
+/* Copyright 2004 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 net.sf.acegisecurity.intercept.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.SecurityConfig;
+
+import java.util.Iterator;
+
+
+/**
+ * Tests {@link FilterInvocationDefinitionSourceEditor} and its associated
+ * {@link PathBasedFilterInvocationDefinitionMap}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class FilterInvocationDefinitionSourceEditorWithPathsTests
+ extends TestCase {
+ //~ Constructors ===========================================================
+
+ public FilterInvocationDefinitionSourceEditorWithPathsTests() {
+ super();
+ }
+
+ public FilterInvocationDefinitionSourceEditorWithPathsTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(FilterInvocationDefinitionSourceEditorWithPathsTests.class);
+ }
+
+ public void testAntPathDirectiveIsDetected() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE\r\n/secure/*=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+ .getValue();
+ assertTrue(map instanceof PathBasedFilterInvocationDefinitionMap);
+ }
+
+ public void testConvertUrlToLowercaseDefaultSettingUnchangedByEditor() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE\r\n/secure/*=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testConvertUrlToLowercaseSettingApplied() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\r\nPATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE\r\n/secure/*=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+ assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testIterator() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE\r\n/secure/*=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+ Iterator iter = map.getConfigAttributeDefinitions();
+ int counter = 0;
+
+ while (iter.hasNext()) {
+ iter.next();
+ counter++;
+ }
+
+ assertEquals(2, counter);
+ }
+
+ public void testMapReturnsNullWhenNoMatchFound() throws Exception {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+ null);
+ httpRequest.setServletPath("/totally/different/path/index.html");
+
+ ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+ httpRequest, new MockHttpServletResponse(),
+ new MockFilterChain()));
+
+ assertEquals(null, returned);
+ }
+
+ public void testMultiUrlParsing() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE\r\n/secure/*=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+ assertEquals(2, map.getMapSize());
+ }
+
+ public void testNoArgsConstructor() {
+ try {
+ new PathBasedFilterInvocationDefinitionMap().new EntryHolder();
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+ }
+
+ public void testOrderOfEntriesIsPreservedOrderA() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/**=ROLE_WE_DONT_HAVE,ANOTHER_ROLE\r\n/secure/**=ROLE_SUPERVISOR,ROLE_TELLER");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+
+ // Test ensures we match the first entry, not the second
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+ null);
+ httpRequest.setServletPath("/secure/super/very_secret.html");
+
+ ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+ httpRequest, new MockHttpServletResponse(),
+ new MockFilterChain()));
+
+ ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+ expected.addConfigAttribute(new SecurityConfig("ROLE_WE_DONT_HAVE"));
+ expected.addConfigAttribute(new SecurityConfig("ANOTHER_ROLE"));
+
+ assertEquals(expected, returned);
+ }
+
+ public void testOrderOfEntriesIsPreservedOrderB() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/**=ROLE_SUPERVISOR,ROLE_TELLER\r\n/secure/super/**=ROLE_WE_DONT_HAVE");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+ null);
+ httpRequest.setServletPath("/secure/super/very_secret.html");
+
+ ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+ httpRequest, new MockHttpServletResponse(),
+ new MockFilterChain()));
+
+ ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+ expected.addConfigAttribute(new SecurityConfig("ROLE_SUPERVISOR"));
+ expected.addConfigAttribute(new SecurityConfig("ROLE_TELLER"));
+
+ assertEquals(expected, returned);
+ }
+
+ public void testSingleUrlParsing() throws Exception {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ "PATTERN_TYPE_APACHE_ANT\r\n/secure/super/*=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+ null);
+ httpRequest.setServletPath("/secure/super/very_secret.html");
+
+ ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+ httpRequest, new MockHttpServletResponse(),
+ new MockFilterChain()));
+
+ ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+ expected.addConfigAttribute(new SecurityConfig("ROLE_WE_DONT_HAVE"));
+ expected.addConfigAttribute(new SecurityConfig("ANOTHER_ROLE"));
+
+ assertEquals(expected, returned);
+ }
+
+ public void testWhitespaceAndCommentsAndLinesWithoutEqualsSignsAreIgnored() {
+ FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+ editor.setAsText(
+ " PATTERN_TYPE_APACHE_ANT\r\n /secure/super/*=ROLE_WE_DONT_HAVE\r\n /secure/*=ROLE_SUPERVISOR,ROLE_TELLER \r\n \r\n \r\n // comment line \r\n \r\n");
+
+ PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor
+ .getValue();
+ assertEquals(2, map.getMapSize());
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java b/core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java
index 420aa10a8f..4d60e7c26c 100644
--- a/core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java
+++ b/core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java
@@ -48,7 +48,7 @@ import javax.servlet.ServletResponse;
/**
- * Tests {@link FiltSecurityInterceptor}.
+ * Tests {@link FilterSecurityInterceptor}.
*
* @author Ben Alex
* @version $Id$
@@ -77,7 +77,7 @@ public class FilterSecurityInterceptorTests extends TestCase {
public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass() {
FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
interceptor.setAuthenticationManager(new MockAuthenticationManager());
- interceptor.setObjectDefinitionSource(new FilterInvocationDefinitionMap());
+ interceptor.setObjectDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
interceptor.setRunAsManager(new MockRunAsManager());
interceptor.setAccessDecisionManager(new AccessDecisionManager() {
@@ -110,7 +110,7 @@ public class FilterSecurityInterceptorTests extends TestCase {
FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
interceptor.setAuthenticationManager(new MockAuthenticationManager());
- interceptor.setObjectDefinitionSource(new FilterInvocationDefinitionMap());
+ interceptor.setObjectDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
interceptor.setRunAsManager(new RunAsManager() {
public boolean supports(Class clazz) {
@@ -143,7 +143,7 @@ public class FilterSecurityInterceptorTests extends TestCase {
interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
interceptor.setAuthenticationManager(new MockAuthenticationManager());
- FilterInvocationDefinitionMap fidp = new FilterInvocationDefinitionMap();
+ RegExpBasedFilterInvocationDefinitionMap fidp = new RegExpBasedFilterInvocationDefinitionMap();
interceptor.setObjectDefinitionSource(fidp);
interceptor.setRunAsManager(new MockRunAsManager());
interceptor.afterPropertiesSet();
diff --git a/core/src/test/java/org/acegisecurity/intercept/web/PathBasedFilterDefinitionMapTests.java b/core/src/test/java/org/acegisecurity/intercept/web/PathBasedFilterDefinitionMapTests.java
new file mode 100644
index 0000000000..3ca571a7e1
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/intercept/web/PathBasedFilterDefinitionMapTests.java
@@ -0,0 +1,123 @@
+/* Copyright 2004 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 net.sf.acegisecurity.intercept.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.SecurityConfig;
+
+
+/**
+ * Tests parts of {@link PathBasedFilterInvocationDefinitionMap} not tested by
+ * {@link FilterInvocationDefinitionSourceEditorWithPathsTests}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class PathBasedFilterDefinitionMapTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public PathBasedFilterDefinitionMapTests() {
+ super();
+ }
+
+ public PathBasedFilterDefinitionMapTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(PathBasedFilterDefinitionMapTests.class);
+ }
+
+ public void testConvertUrlToLowercaseIsFalseByDefault() {
+ PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testConvertUrlToLowercaseSetterRespected() {
+ PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
+ map.setConvertUrlToLowercaseBeforeComparison(true);
+ assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testLookupNotRequiringExactMatchSuccessIfNotMatching() {
+ PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
+ map.setConvertUrlToLowercaseBeforeComparison(true);
+ assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("/secure/super/**", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/SeCuRE/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(def, response);
+ }
+
+ public void testLookupRequiringExactMatchFailsIfNotMatching() {
+ PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("/secure/super/**", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/SeCuRE/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(null, response);
+ }
+
+ public void testLookupRequiringExactMatchIsSuccessful() {
+ PathBasedFilterInvocationDefinitionMap map = new PathBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("/secure/super/**", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/secure/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(def, response);
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/intercept/web/RegExpBasedFilterDefinitionMapTests.java b/core/src/test/java/org/acegisecurity/intercept/web/RegExpBasedFilterDefinitionMapTests.java
new file mode 100644
index 0000000000..c7480cbc4a
--- /dev/null
+++ b/core/src/test/java/org/acegisecurity/intercept/web/RegExpBasedFilterDefinitionMapTests.java
@@ -0,0 +1,123 @@
+/* Copyright 2004 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 net.sf.acegisecurity.intercept.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.SecurityConfig;
+
+
+/**
+ * Tests parts of {@link RegExpBasedFilterInvocationDefinitionMap} not tested by {@link
+ * FilterInvocationDefinitionSourceEditorTests}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class RegExpBasedFilterDefinitionMapTests extends TestCase {
+ //~ Constructors ===========================================================
+
+ public RegExpBasedFilterDefinitionMapTests() {
+ super();
+ }
+
+ public RegExpBasedFilterDefinitionMapTests(String arg0) {
+ super(arg0);
+ }
+
+ //~ Methods ================================================================
+
+ public final void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(RegExpBasedFilterDefinitionMapTests.class);
+ }
+
+ public void testConvertUrlToLowercaseIsFalseByDefault() {
+ RegExpBasedFilterInvocationDefinitionMap map = new RegExpBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testConvertUrlToLowercaseSetterRespected() {
+ RegExpBasedFilterInvocationDefinitionMap map = new RegExpBasedFilterInvocationDefinitionMap();
+ map.setConvertUrlToLowercaseBeforeComparison(true);
+ assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+ }
+
+ public void testLookupNotRequiringExactMatchSuccessIfNotMatching() {
+ RegExpBasedFilterInvocationDefinitionMap map = new RegExpBasedFilterInvocationDefinitionMap();
+ map.setConvertUrlToLowercaseBeforeComparison(true);
+ assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/SeCuRE/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(def, response);
+ }
+
+ public void testLookupRequiringExactMatchFailsIfNotMatching() {
+ RegExpBasedFilterInvocationDefinitionMap map = new RegExpBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/SeCuRE/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(null, response);
+ }
+
+ public void testLookupRequiringExactMatchIsSuccessful() {
+ RegExpBasedFilterInvocationDefinitionMap map = new RegExpBasedFilterInvocationDefinitionMap();
+ assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+ ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+ def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+ map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+ // Build a HTTP request
+ MockHttpServletRequest req = new MockHttpServletRequest(null);
+ req.setServletPath("/secure/super/somefile.html");
+
+ FilterInvocation fi = new FilterInvocation(req,
+ new MockHttpServletResponse(), new MockFilterChain());
+
+ ConfigAttributeDefinition response = map.lookupAttributes(fi);
+ assertEquals(def, response);
+ }
+}
diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml
index 8212fdc816..0650a3abf9 100644
--- a/docs/reference/src/index.xml
+++ b/docs/reference/src/index.xml
@@ -562,7 +562,7 @@
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
- \A/secure/super.*\Z=ROLE_WE_DONT_HAVE
+ \A/secure/super/.*\Z=ROLE_WE_DONT_HAVE
\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
</value>
</property>
@@ -611,23 +611,48 @@
created by the property editor,
FilterInvocationDefinitionSource, matches
configuration attributes against FilterInvocations
- based on regular expression evaluation of the request URL. It works
- down the list in the order they are defined. Thus it is important that
- more specific regular expressions are defined higher in the list than
- less specific regular expressions. This is reflected in our example
- above, where the more specific /secure/super
+ based on expression evaluation of the request URL. Two standard
+ expression syntaxes are supported. The default is to treat all
+ expressions as regular expressions. Alternatively, the presence of a
+ PATTERN_TYPE_APACHE_ANT directive will cause all
+ expressions to be treated as Apache Ant paths. It is not possible to
+ mix expression syntaxes within the same definition. For example, the
+ earlier configuration could be generated using Apache Ant paths as
+ follows:
+
+ <bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
+ <property name="authenticationManager"><ref bean="authenticationManager"/></property>
+ <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
+ <property name="runAsManager"><ref bean="runAsManager"/></property>
+ <property name="objectDefinitionSource">
+ <value>
+ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
+ PATTERN_TYPE_APACHE_ANT
+ /secure/super/**=ROLE_WE_DONT_HAVE
+ /secure/**=ROLE_SUPERVISOR,ROLE_TELLER
+ </value>
+ </property>
+</bean>
+
+ Irrespective of the type of expression syntax used, expressions
+ are always evaluated in the order they are defined. Thus it is
+ important that more specific expressions are defined higher in the
+ list than less specific expressions. This is reflected in our example
+ above, where the more specific /secure/super/
pattern appears higher than the less specific
- /super pattern. If they were reversed, the
- /super pattern would always match and the
- /secure/super pattern would never be evaluated. The
- special keyword
+ /super/ pattern. If they were reversed, the
+ /super/ pattern would always match and the
+ /secure/super/ pattern would never be evaluated.
+
+
+ The special keyword
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON causes
the FilterInvocationDefinitionSource to
automatically convert a request URL to lowercase before comparison
- against the regular expressions. Whilst by default the case of the
- request URL is not converted, it is generally recommended to use
+ against the expressions. Whilst by default the case of the request URL
+ is not converted, it is generally recommended to use
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON and
- write each regular expression assuming lowercase.
+ write each expression assuming lowercase.
As with other security interceptors, the
validateConfigAttributes property is observed. When