You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:13:20 UTC

[sling-org-apache-sling-security] 21/30: FELIX-2870 : Support allowed hosts patterns in ReferrerFilter . Apply patch from Timothee Maret

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 <cz...@apache.org>
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
"commits@sling.apache.org" <co...@sling.apache.org>.