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