Support configuration via Apache Ant paths (not only regular expressions).

This commit is contained in:
Ben Alex 2004-04-09 09:51:23 +00:00
parent 5488bf4ca8
commit bd35a47233
11 changed files with 907 additions and 187 deletions

View File

@ -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)
-----------------------------------

View File

@ -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 <Code>List</code> of <code>ConfigAttributeDefinition</code>s
* associated with different HTTP request URL patterns.
*
* <P>
* Regular expressions are used to match a HTTP request URL against a
* <code>ConfigAttributeDefinition</code>.
* </p>
*
* <p>
* The order of registering the regular expressions using the {@link
* #addSecureUrl(String, ConfigAttributeDefinition)} is very important. The
* system will identify the <B>first</B> matching regular expression for a
* given HTTP URL. It will not proceed to evaluate later regular expressions
* if a match has already been found. Accordingly, the most specific regular
* expressions should be registered first, with the most general regular
* expressions registered last.
* </p>
*
* <P>
* If no registered regular expressions match the HTTP URL, <code>null</code>
* is returned.
* </p>
* 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);
}

View File

@ -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}.
*
* <p>
* 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).
* </p>
*
* <P>
* By default the class treats presented patterns as regular expressions. If
* the keyword <code>PATTERN_TYPE_APACHE_ANT</code> is present (case
* sensitive), patterns will be treated as Apache Ant paths rather than
* regular expressions.
* </p>
*
* @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;

View File

@ -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 <Code>List</code> of <code>ConfigAttributeDefinition</code>s
* associated with different HTTP request URL Apache Ant path-based patterns.
*
* <P>
* Apache Ant path expressions are used to match a HTTP request URL against a
* <code>ConfigAttributeDefinition</code>.
* </p>
*
* <p>
* The order of registering the Ant paths using the {@link
* #addSecureUrl(String, ConfigAttributeDefinition)} is very important. The
* system will identify the <B>first</B> 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.
* </p>
*
* <P>
* If no registered paths match the HTTP URL, <code>null</code> is returned.
* </p>
*/
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;
}
}
}

View File

@ -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 <Code>List</code> of <code>ConfigAttributeDefinition</code>s
* associated with different HTTP request URL regular expression patterns.
*
* <P>
* Regular expressions are used to match a HTTP request URL against a
* <code>ConfigAttributeDefinition</code>.
* </p>
*
* <p>
* The order of registering the regular expressions using the {@link
* #addSecureUrl(String, ConfigAttributeDefinition)} is very important. The
* system will identify the <B>first</B> matching regular expression for a
* given HTTP URL. It will not proceed to evaluate later regular expressions
* if a match has already been found. Accordingly, the most specific regular
* expressions should be registered first, with the most general regular
* expressions registered last.
* </p>
*
* <P>
* If no registered regular expressions match the HTTP URL, <code>null</code>
* is returned.
* </p>
*/
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;
}
}
}

View File

@ -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());
}

View File

@ -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());
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -562,7 +562,7 @@
&lt;property name="objectDefinitionSource"&gt;
&lt;value&gt;
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
&lt;/value&gt;
&lt;/property&gt;
@ -611,23 +611,48 @@
created by the property editor,
<literal>FilterInvocationDefinitionSource</literal>, matches
configuration attributes against <literal>FilterInvocations</literal>
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 <literal>/secure/super</literal>
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
<literal>PATTERN_TYPE_APACHE_ANT</literal> 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: </para>
<para><programlisting>&lt;bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
&lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;
&lt;property name="accessDecisionManager"&gt;&lt;ref bean="accessDecisionManager"/&gt;&lt;/property&gt;
&lt;property name="runAsManager"&gt;&lt;ref bean="runAsManager"/&gt;&lt;/property&gt;
&lt;property name="objectDefinitionSource"&gt;
&lt;value&gt;
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/secure/super/**=ROLE_WE_DONT_HAVE
/secure/**=ROLE_SUPERVISOR,ROLE_TELLER
&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>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 <literal>/secure/super/</literal>
pattern appears higher than the less specific
<literal>/super</literal> pattern. If they were reversed, the
<literal>/super</literal> pattern would always match and the
/<literal>secure/super</literal> pattern would never be evaluated. The
special keyword
<literal>/super/</literal> pattern. If they were reversed, the
<literal>/super/</literal> pattern would always match and the
<literal>/secure/super/</literal> pattern would never be evaluated.
</para>
<para>The special keyword
<literal>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON</literal> causes
the <literal>FilterInvocationDefinitionSource</literal> 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
<literal>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON</literal> and
write each regular expression assuming lowercase.</para>
write each expression assuming lowercase.</para>
<para>As with other security interceptors, the
<literal>validateConfigAttributes</literal> property is observed. When