This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-security.git


The following commit(s) were added to refs/heads/master by this push:
     new ed34399  SLING-9061  - Adding support for evaluating the origin as 
well as the referrer and pluggable referrer configurations (#9)
ed34399 is described below

commit ed343999a43e709000b239d57065d1fb89e7c832
Author: Dan Klco <klcod...@users.noreply.github.com>
AuthorDate: Thu May 18 21:19:35 2023 -0400

    SLING-9061  - Adding support for evaluating the origin as well as the 
referrer and pluggable referrer configurations (#9)
    
    * SLING-11871 - Adding support for bypassing the ReferrerFilter for 
requests with an Origin header
    
    * SLING-9061 - Adding support for evaluating the origin as well as the 
referrer and pluggable referrer configurations
    
    * Changing mergeValues to return a collection rather an an array
---
 .../apache/sling/security/impl/ReferrerFilter.java | 41 ++++++++--
 .../security/impl/ReferrerFilterAmendment.java     | 43 +++++++++++
 .../security/impl/ReferrerFilterAmendmentImpl.java | 90 ++++++++++++++++++++++
 .../sling/security/impl/ReferrerFilterTest.java    | 87 +++++++++++++++++----
 4 files changed, 237 insertions(+), 24 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 5125e57..f563234 100644
--- a/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java
+++ b/src/main/java/org/apache/sling/security/impl/ReferrerFilter.java
@@ -27,11 +27,14 @@ import java.net.SocketException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.regex.Pattern;
+
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
@@ -42,6 +45,9 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 import org.osgi.service.http.whiteboard.Preprocessor;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
@@ -245,7 +251,7 @@ public class ReferrerFilter implements Preprocessor {
     /**
      * Create Patterns out of the regular expression referrer list
      */
-    private Pattern[] createRegexPatterns(final String[] regexps) {
+    private Pattern[] createRegexPatterns(final Collection<String> regexps) {
         final List<Pattern> patterns = new ArrayList<>();
         if (regexps != null) {
             for (final String regexp : regexps) {
@@ -260,16 +266,32 @@ public class ReferrerFilter implements Preprocessor {
         return patterns.toArray(new Pattern[0]);
     }
 
+    private Collection<String> mergeValues(String[] primary, 
List<ReferrerFilterAmendment> amendments,
+            Function<ReferrerFilterAmendment, String[]> extractor) {
+        Set<String> consolidated = new HashSet<>();
+        if (primary != null) {
+            Arrays.stream(primary).forEach(consolidated::add);
+        }
+        if (amendments != null) {
+            amendments.stream().map(extractor::apply).forEach(v -> 
Arrays.stream(v).forEach(consolidated::add));
+        }
+        return consolidated;
+    }
+
     @Activate
-    public ReferrerFilter(final Config config) {
+    public ReferrerFilter(final Config config,
+            @Reference(policyOption = ReferencePolicyOption.GREEDY, 
cardinality = ReferenceCardinality.MULTIPLE, 
service=ReferrerFilterAmendment.class) List<ReferrerFilterAmendment> 
amendments) {
         this.allowEmpty = config.allow_empty();
-        this.allowedRegexReferrers = 
createRegexPatterns(config.allow_hosts_regexp());
-        this.excludedRegexUserAgents = 
createRegexPatterns(config.exclude_agents_regexp());
-        this.excludedPaths = config.exclude_paths();
+        this.allowedRegexReferrers = createRegexPatterns(
+                mergeValues(config.allow_hosts_regexp(), amendments, a -> 
a.allowHostsRegex()));
+        this.excludedRegexUserAgents = createRegexPatterns(
+                mergeValues(config.exclude_agents_regexp(), amendments, a -> 
a.excludeAgentsRegex()));
+        this.excludedPaths = mergeValues(config.exclude_paths(), amendments, a 
-> a.excludePaths()).toArray(new String[0]);
 
         final Set<String> allowUriReferrers = getDefaultAllowedReferrers();
         if (config.allow_hosts() != null) {
-            allowUriReferrers.addAll(Arrays.asList(config.allow_hosts()));
+            allowUriReferrers.addAll(
+                    mergeValues(config.allow_hosts(), amendments, a -> 
a.allowHosts()));
         }
         this.allowedUriReferrers = createReferrerUrls(allowUriReferrers);
 
@@ -368,7 +390,12 @@ public class ReferrerFilter implements Preprocessor {
             return true;
         }
         
-        final String referrer = request.getHeader("referer");
+        String referrer = request.getHeader("referer");
+        // use the origin if the referrer is not set
+        if (referrer == null || referrer.trim().length() == 0) {
+            referrer = request.getHeader("origin");
+        }
+
         // check for missing/empty referrer
         if (referrer == null || referrer.trim().length() == 0) {
             if (!this.allowEmpty) {
diff --git 
a/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendment.java 
b/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendment.java
new file mode 100644
index 0000000..09f6fcb
--- /dev/null
+++ b/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendment.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sling.security.impl;
+
+/**
+ * Amends the primary configuration of Referrer Filter
+ */
+public interface ReferrerFilterAmendment {
+    /**
+     * @return List of allowed hosts for the referrer which are added to the 
list of
+     *         default hosts.
+     */
+    String[] allowHosts();
+
+    /**
+     * @return List of allowed regular expression for the referrer.
+     */
+    String[] allowHostsRegex();
+
+    /**
+     * @return List of regexp for user agents not to check the referrer
+     */
+    String[] excludeAgentsRegex();
+
+    /**
+     * @return List of paths for which not to check the referrer
+     */
+    String[] excludePaths();
+}
diff --git 
a/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendmentImpl.java 
b/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendmentImpl.java
new file mode 100644
index 0000000..4bdcb43
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/security/impl/ReferrerFilterAmendmentImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sling.security.impl;
+
+import java.util.Optional;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@Component(service = ReferrerFilterAmendment.class)
+@Designate(factory = true, ocd = ReferrerFilterAmendmentImpl.Config.class)
+public class ReferrerFilterAmendmentImpl implements ReferrerFilterAmendment {
+
+    private final Config config;
+
+    @Activate
+    public ReferrerFilterAmendmentImpl(Config config) {
+        this.config = config;
+    }
+
+    @Override
+    public String[] allowHosts() {
+        return Optional.ofNullable(config.allow_hosts()).orElse(new String[0]);
+    }
+
+    @Override
+    public String[] allowHostsRegex() {
+        return Optional.ofNullable(config.allow_hosts_regexp()).orElse(new 
String[0]);
+    }
+
+    @Override
+    public String[] excludeAgentsRegex() {
+        return Optional.ofNullable(config.exclude_agents_regexp()).orElse(new 
String[0]);
+    }
+
+    @Override
+    public String[] excludePaths() {
+        return Optional.ofNullable(config.exclude_paths()).orElse(new 
String[0]);
+    }
+
+    @ObjectClassDefinition(name = "Apache Sling Referrer Filter Amendment", 
description = "Amend the primary list of Referrer Filter allow hosts with 
additional hosts")
+    public @interface Config {
+
+        @AttributeDefinition(name = "Allow Hosts", description = "List of 
allowed hosts for the referrer which are added to the list of default hosts. "
+                + "It is matched against the full referrer URL in the format 
\"<scheme>://<host>:<port>\". "
+                + "If port is 0, it is not taken into consideration. The 
default list contains all host names "
+                + "and IPs bound to all NICs found in the system plus 
\"localhost\", \"127.0.0.1\", \"[::1]\" for protocols \"http\" and \"https\". "
+                + "If given value does not have a \":\" entries for both http 
and https are transparently generated.")
+        String[] allow_hosts() default {};
+
+        /**
+         * Allow referrer regex hosts property
+         */
+        @AttributeDefinition(name = "Allow Regexp Host", description = "List 
of allowed regular expression for the referrer. "
+                + "It is matched against the full referrer URL in the format 
\"<scheme>://<host>:<port>\". "
+                + "Evaluated in addition to the default list and the given 
allowed hosts (see above)!")
+        String[] allow_hosts_regexp() default {};
+
+        /**
+         * Excluded regexp user agents property
+         */
+        @AttributeDefinition(name = "Exclude Regexp User Agent", description = 
"List of regexp for user agents not to check the referrer")
+        String[] exclude_agents_regexp() default {};
+
+        /**
+         * Excluded the configured paths from the referrer check
+         */
+        @AttributeDefinition(name = "Exclude Paths", description = "List of 
paths for which not to check the referrer")
+        String[] exclude_paths() default {};
+
+    }
+
+}
\ 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 53b1f29..0a68898 100644
--- a/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java
+++ b/src/test/java/org/apache/sling/security/impl/ReferrerFilterTest.java
@@ -22,9 +22,11 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.lang.annotation.Annotation;
+import java.util.Collections;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.sling.security.impl.ReferrerFilterAmendmentImpl.Config;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -35,15 +37,16 @@ public class ReferrerFilterTest {
 
     @Before
     public void setup() {
-        ReferrerFilter.Config config = createConfiguration(false, new 
String[]{"relhost"}, 
-                new String[]{"http://([^.]*.)?abshost:80", "^app://.+"}, 
-                new String[]{"[a-zA-Z]*\\/[0-9]*\\.[0-9]*;Some-Agent\\s.*"}, 
-                new String[] {null, "/test_path"});
-        filter = new ReferrerFilter(config);
+        ReferrerFilter.Config config = createConfiguration(false, new String[] 
{ "relhost" },
+                new String[] { "http://([^.]*.)?abshost:80", "^app://.+" },
+                new String[] { "[a-zA-Z]*\\/[0-9]*\\.[0-9]*;Some-Agent\\s.*" },
+                new String[] { null, "/test_path" });
+        filter = new ReferrerFilter(config, Collections.emptyList());
     }
 
-    private static ReferrerFilter.Config createConfiguration(boolean 
allowEmpty, String[] allowHosts, String[] allowHostsRexexp, 
-                                                             String[] 
excludeAgentsRegexp, String[] excludePaths) {
+    private static ReferrerFilter.Config createConfiguration(boolean 
allowEmpty, String[] allowHosts,
+            String[] allowHostsRexexp,
+            String[] excludeAgentsRegexp, String[] excludePaths) {
         return new ReferrerFilter.Config() {
             @Override
             public Class<? extends Annotation> annotationType() {
@@ -79,7 +82,7 @@ public class ReferrerFilterTest {
             public String[] exclude_paths() {
                 return excludePaths;
             }
-        }; 
+        };
     }
 
     @Test
@@ -105,18 +108,18 @@ public class ReferrerFilterTest {
         final HttpServletRequest request = mock(HttpServletRequest.class);
         when(request.getMethod()).thenReturn("POST");
         if (pathInfo != null) {
-            
when(request.getRequestURI()).thenReturn("http://somehost/somewhere"+pathInfo);
+            
when(request.getRequestURI()).thenReturn("http://somehost/somewhere"; + 
pathInfo);
             when(request.getPathInfo()).thenReturn(pathInfo);
         } else {
             
when(request.getRequestURI()).thenReturn("http://somehost/somewhere";);
         }
         when(request.getHeader("referer")).thenReturn(referrer);
-        if ( userAgent != null && userAgent.length() > 0 ) {
+        if (userAgent != null && userAgent.length() > 0) {
             when(request.getHeader("User-Agent")).thenReturn(userAgent);
         }
         return request;
     }
-    
+
     private static HttpServletRequest getRequest(final String referrer, final 
String userAgent) {
         return getRequest(referrer, userAgent, null);
     }
@@ -146,21 +149,22 @@ public class ReferrerFilterTest {
         
assertTrue(filter.isValidRequest(getRequest("app://yet.another.abshost:80")));
         assertFalse(filter.isValidRequest(getRequest("?://")));
     }
-    
+
     @Test
     public void testExcludedPath() {
         assertTrue(filter.isValidRequest(getRequest(null, null, 
"/test_path")));
         assertFalse(filter.isValidRequest(getRequest(null, null, 
"/test_path/subtree")));
         assertFalse(filter.isValidRequest(getRequest(null, null, 
"/test_path_sibling")));
-        
+
         assertTrue(filter.isValidRequest(getRequest("relative", null, 
"/test_path")));
         
assertTrue(filter.isValidRequest(getRequest("http://yet.another.abshost:80";, 
null, "/test_path")));
     }
 
     @Test
     public void testExcludedPathNull() {
-        ReferrerFilter rf = new ReferrerFilter(createConfiguration(false, 
null, null, null, null));
-        
+        ReferrerFilter rf = new ReferrerFilter(createConfiguration(false, 
null, null, null, null),
+                Collections.emptyList());
+
         assertFalse(rf.isValidRequest(getRequest(null, null, "/test_path")));
         assertFalse(rf.isValidRequest(getRequest(null, null, 
"/test_path/subtree")));
         assertFalse(rf.isValidRequest(getRequest(null, null, 
"/test_path_sibling")));
@@ -168,10 +172,59 @@ public class ReferrerFilterTest {
         assertTrue(rf.isValidRequest(getRequest("relative", null, 
"/test_path")));
         
assertFalse(rf.isValidRequest(getRequest("http://yet.another.abshost:80";, null, 
"/test_path")));
     }
-    
+
+    @Test
+    public void testWithAmendments() {
+        ReferrerFilterAmendment amendment = new 
ReferrerFilterAmendmentImpl(new Config() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                throw new UnsupportedOperationException("Unimplemented method 
'annotationType'");
+            }
+
+            @Override
+            public String[] allow_hosts() {
+                return new String[]{"test.com"};
+            }
+
+            @Override
+            public String[] allow_hosts_regexp() {
+                return new String[]{".*test2.com.*"};
+            }
+
+            @Override
+            public String[] exclude_agents_regexp() {
+                return null;
+            }
+
+            @Override
+            public String[] exclude_paths() {
+                return new String[]{"/testpath2"};
+            }
+            
+        });
+        ReferrerFilter rf = new ReferrerFilter(createConfiguration(false, 
null, new String[]{".*test1.com.*"}, null, null), 
Collections.singletonList(amendment));
+        
+        assertTrue(rf.isValidRequest(getRequest(null, null, "/testpath2")));
+        assertFalse(rf.isValidRequest(getRequest(null, null, "/test1path")));
+
+        assertFalse(rf.isValidRequest(getRequest("http://testnotvalid.com:80";, 
null, "/test_path")));
+        assertTrue(rf.isValidRequest(getRequest("http://test1.com:80";, null, 
"/test_path")));
+        assertTrue(rf.isValidRequest(getRequest("http://test2.com:80";, null, 
"/test_path")));
+    }
+
+
+    @Test
+    public void testAllowsWithOrigin(){
+        HttpServletRequest request = getRequest(null);
+        when(request.getHeader("origin")).thenReturn("http://abshost";);
+        Assert.assertEquals(true, filter.isValidRequest(request));
+    }
+
     @Test
     public void testAllowEmpty() {
-        ReferrerFilter rf = new ReferrerFilter(createConfiguration(true, null, 
null, null, null));
+        ReferrerFilter rf = new ReferrerFilter(createConfiguration(true, null, 
null, null, null),
+                Collections.emptyList());
 
         assertTrue(rf.isValidRequest(getRequest(null, null, "/test_path")));
         assertTrue(rf.isValidRequest(getRequest("", null, null)));

Reply via email to