This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.security-1.0.10 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-security.git
commit 45dec5c3941a1c121ca0703aeaac522b6405e4f7 Author: Carsten Ziegeler <[email protected]> AuthorDate: Wed May 22 08:59:33 2013 +0000 FELIX-2870 : Support allowed hosts patterns in ReferrerFilter . Apply patch from Timothee Maret git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/security@1485123 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/sling/security/impl/ReferrerFilter.java | 130 ++++++++++++++++----- .../OSGI-INF/metatype/metatype.properties | 9 +- .../sling/security/impl/ReferrerFilterTest.java | 14 ++- 3 files changed, 116 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java b/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java index 000f463..e3dfa5d 100644 --- a/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java +++ b/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java @@ -26,12 +26,14 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -93,19 +95,30 @@ public class ReferrerFilter implements Filter { @Property(boolValue=DEFAULT_ALLOW_EMPTY) private static final String PROP_ALLOW_EMPTY = "allow.empty"; - /** Allow empty property. */ + private static final String[] DEFAULT_PROP_HOSTS = {}; + + /** Allow referrer uri hosts property. */ @Property(unbounded=PropertyUnbounded.ARRAY) private static final String PROP_HOSTS = "allow.hosts"; - /** Allow empty property. */ + /** Allow referrer regex hosts property */ + @Property(unbounded=PropertyUnbounded.ARRAY) + private static final String PROP_HOSTS_REGEX = "allow.hosts.regexp"; + + /** Filtered methods property */ @Property(unbounded=PropertyUnbounded.ARRAY, value={"POST", "PUT", "DELETE"}) private static final String PROP_METHODS = "filter.methods"; + + /** Do we allow empty referrer? */ private boolean allowEmpty; - /** Allowed referrers */ - private URL[] allowedReferrers; + /** Allowed uri referrers */ + private URL[] allowedUriReferrers; + + /** Allowed regexp referrers */ + private Pattern[] allowedRegexReferrers; /** Methods to be filtered. */ private String[] filterMethods; @@ -160,7 +173,7 @@ public class ReferrerFilter implements Filter { } /** - * Create URLs out of the referrer list + * Create URLs out of the uri referrer set */ private URL[] createReferrerUrls(final Set<String> referrers) { final List<URL> urls = new ArrayList<URL>(); @@ -179,27 +192,41 @@ public class ReferrerFilter implements Filter { } /** + * Create Patterns out of the regexp referrer list + */ + private Pattern[] createReferrerPatterns(final String[] regexps) { + final List<Pattern> patterns = new ArrayList<Pattern>(); + for(final String regexp : regexps) { + try { + final Pattern pattern = Pattern.compile(regexp); + patterns.add(pattern); + } catch (final Exception e) { + logger.warn("Unable to create Pattern from {} : {}", new String[]{regexp, e.getMessage()}); + } + } + return patterns.toArray(new Pattern[patterns.size()]); + } + + /** * Activate */ @Activate protected void activate(final ComponentContext ctx) { - this.allowEmpty = PropertiesUtil.toBoolean(ctx.getProperties().get(PROP_ALLOW_EMPTY), DEFAULT_ALLOW_EMPTY); - String[] allowHosts = PropertiesUtil.toStringArray(ctx.getProperties().get(PROP_HOSTS)); - if ( allowHosts != null ) { - if ( allowHosts.length == 0 ) { - allowHosts = null; - } else if ( allowHosts.length == 1 && allowHosts[0].trim().length() == 0 ) { - allowHosts = null; - } - } - final Set<String> allowedReferrers = this.getDefaultAllowedReferrers(); - if ( allowHosts != null ) { - for(final String host : allowHosts) { - allowedReferrers.add(host); - } - } - this.allowedReferrers = this.createReferrerUrls(allowedReferrers); - this.filterMethods = PropertiesUtil.toStringArray(ctx.getProperties().get(PROP_METHODS)); + final Dictionary props = ctx.getProperties(); + + this.allowEmpty = PropertiesUtil.toBoolean(props.get(PROP_ALLOW_EMPTY), DEFAULT_ALLOW_EMPTY); + + final String[] allowRegexHosts = defaultIfEmpty(PropertiesUtil.toStringArray(props.get(PROP_HOSTS_REGEX), + DEFAULT_PROP_HOSTS), DEFAULT_PROP_HOSTS); + this.allowedRegexReferrers = createReferrerPatterns(allowRegexHosts); + + final Set<String> allowUriReferrers = getDefaultAllowedReferrers(); + final String[] allowHosts = defaultIfEmpty(PropertiesUtil.toStringArray(props.get(PROP_HOSTS), + DEFAULT_PROP_HOSTS), DEFAULT_PROP_HOSTS); + allowUriReferrers.addAll(Arrays.asList(allowHosts)); + this.allowedUriReferrers = createReferrerUrls(allowUriReferrers); + + this.filterMethods = PropertiesUtil.toStringArray(props.get(PROP_METHODS)); if ( this.filterMethods != null && this.filterMethods.length == 1 && (this.filterMethods[0] == null || this.filterMethods[0].trim().length() == 0) ) { this.filterMethods = null; } @@ -267,6 +294,9 @@ public class ReferrerFilter implements Filter { public String host; public String scheme; public int port; + public String toURI() { + return scheme + "://" + host + ":" + port; + } } HostInfo getHost(final String referrer) { @@ -330,15 +360,9 @@ public class ReferrerFilter implements Filter { return true; } - boolean valid = false; - for(final URL ref : this.allowedReferrers) { - if ( info.host.equals(ref.getHost()) && info.scheme.equals(ref.getProtocol()) ) { - if ( ref.getPort() == 0 || info.port == ref.getPort() ) { - valid = true; - break; - } - } - } + // allow the request if the referrer matches any of the allowed referrers + boolean valid = isValidUriReferrer(info) || isValidRegexReferrer(info); + if ( !valid) { this.logger.info("Rejected referrer header for {} request to {} : {}", new Object[] {request.getMethod(), request.getRequestURI(), referrer}); @@ -361,6 +385,45 @@ public class ReferrerFilter implements Filter { } /** + * @param hostInfo The hostInfo to check for validity + * @return <code>true</code> if the hostInfo matches any of the allowed URI referrer. + */ + private boolean isValidUriReferrer(HostInfo hostInfo) { + for(final URL ref : this.allowedUriReferrers) { + if ( hostInfo.host.equals(ref.getHost()) && hostInfo.scheme.equals(ref.getProtocol()) ) { + if ( ref.getPort() == 0 || hostInfo.port == ref.getPort() ) { + return true; + } + } + } + return false; + } + + /** + * @param hostInfo The hostInfo to check for validity + * @return <code>true</code> if the hostInfo matches any of the allowed regexp referrer. + */ + private boolean isValidRegexReferrer(HostInfo hostInfo) { + for(final Pattern ref : this.allowedRegexReferrers) { + String url = hostInfo.toURI(); + if (ref.matcher(url).matches()) { + return true; + } + } + return false; + } + + /** + * @return The <code>defaultProperties</code> if <code>properties</code> contains a single empty string, + * <code>properties</code> otherwise. + */ + private String[] defaultIfEmpty(String[] properties, String[] defaultProperties) { + return properties.length == 1 && properties[0].trim().length() == 0 + ? defaultProperties + : properties; + } + + /** * Returns <code>true</code> if the given request can be assumed to be sent * by a client browser such as Firefix, Internet Explorer, etc. * <p> @@ -391,9 +454,12 @@ public class ReferrerFilter implements Filter { public void printConfiguration(final PrintWriter pw) { pw.println("Current Apache Sling Referrer Filter Allowed Referrers:"); pw.println(); - for (final URL url : allowedReferrers) { + for (final URL url : allowedUriReferrers) { pw.println(url.toString()); } + for (final Pattern pattern : allowedRegexReferrers) { + pw.println(pattern.toString()); + } } } diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties index f536075..0905fb1 100644 --- a/src/main/resources/OSGI-INF/metatype/metatype.properties +++ b/src/main/resources/OSGI-INF/metatype/metatype.properties @@ -25,14 +25,17 @@ # # Referrer Filter referrer.name = Apache Sling Referrer Filter -referrer.description = Request filter checking the referrer of modification requests. +referrer.description = Request filter checking the referrer of modification requests. allow.empty.name = Allow Empty allow.empty.description = Allow an empty or missing referrer allow.hosts.name = Allow Hosts -allow.hosts.description = List of allowed hosts for the referrer. If this is empty only the default\ - hosts are allowed. +allow.hosts.description = List of allowed hosts for the referrer which are added to the list of default hosts. + + +allow.hosts.regexp.name = Allow Regexp Host +allow.hosts.regexp.description = List of allowed regexp for the referrer. filter.methods.name = Filter Methods filter.methods.description = These methods are filtered by the filter. \ No newline at end of file diff --git a/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java b/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java index 136cb28..567246e 100644 --- a/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java +++ b/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java @@ -17,7 +17,6 @@ package org.apache.sling.security.impl; import static org.mockito.Mockito.*; -import static org.mockito.Matchers.*; import java.util.Dictionary; import java.util.Hashtable; @@ -40,7 +39,10 @@ public class ReferrerFilterTest { final ComponentContext ctx = mock(ComponentContext.class); final BundleContext bundleCtx = mock(BundleContext.class); final ServiceRegistration reg = mock(ServiceRegistration.class); - final Dictionary<String, Object> props = new Hashtable<String, Object>(); + final Dictionary<String, Object> props = new Hashtable<String, Object>(){{ + put("allow.hosts", new String[]{"relhost"}); + put("allow.hosts.regexp", new String[]{"http://([^.]*.)?abshost:80"}); + }}; doReturn(props).when(ctx).getProperties(); doReturn(bundleCtx).when(ctx).getBundleContext(); doReturn(reg).when(bundleCtx).registerService(any(String[].class), any(), any(Dictionary.class)); @@ -83,5 +85,13 @@ public class ReferrerFilterTest { Assert.assertEquals(true, filter.isValidRequest(getRequest("http://localhost"))); Assert.assertEquals(true, filter.isValidRequest(getRequest("http://127.0.0.1"))); Assert.assertEquals(false, filter.isValidRequest(getRequest("http://somehost/but/[illegal]"))); + Assert.assertEquals(true, filter.isValidRequest(getRequest("http://relhost"))); + Assert.assertEquals(true, filter.isValidRequest(getRequest("http://relhost:9001"))); + Assert.assertEquals(false, filter.isValidRequest(getRequest("http://abshost:9001"))); + Assert.assertEquals(false, filter.isValidRequest(getRequest("https://abshost:80"))); + Assert.assertEquals(true, filter.isValidRequest(getRequest("http://abshost:80"))); + Assert.assertEquals(false, filter.isValidRequest(getRequest("http://abshost:9001"))); + Assert.assertEquals(true, filter.isValidRequest(getRequest("http://another.abshost:80"))); + Assert.assertEquals(false, filter.isValidRequest(getRequest("http://yet.another.abshost:80"))); } } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
