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

andy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/master by this push:
     new 65fea57  JENA-1846: Add basic CORS support
     new db0f148  Merge pull request #698 from afs/jena1846-cors
65fea57 is described below

commit 65fea57fa34b7f38307a99835ef26918eff4c0d4
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Feb 21 23:30:37 2020 +0000

    JENA-1846: Add basic CORS support
---
 .../main/java/org/apache/jena/fuseki/Fuseki.java   |  2 +-
 .../jena/fuseki/servlets/CrossOriginFilter.java    |  3 +-
 .../org/apache/jena/fuseki/main/FusekiServer.java  | 88 ++++++++++++++++------
 .../apache/jena/fuseki/main/cmds/FusekiMain.java   |  6 ++
 .../apache/jena/fuseki/main/cmds/ServerConfig.java |  1 +
 5 files changed, 72 insertions(+), 28 deletions(-)

diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
index 9105958..0637220 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
@@ -79,7 +79,7 @@ public class Fuseki {
 
     /** Add CORS header */
     static public final boolean CORS_ENABLED = false;
-    
+
     /**
      * A relative resources path to the location of
      * <code>fuseki-properties.xml</code> file.
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/CrossOriginFilter.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/CrossOriginFilter.java
index 0849401..a83b5a3 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/CrossOriginFilter.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/CrossOriginFilter.java
@@ -18,10 +18,9 @@
 
 package org.apache.jena.fuseki.servlets;
 
-// This is a copy of Jettys CrossOriginFilter - Fuseki need something
+// This is a copy of Jetty's CrossOriginFilter - Fuseki need something
 // that works without Jetty on the classpath when running as a WAR file.
 
-
 // We elect to use and distribute under The Apache License v2.0.
 
 // Changes:
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
index e983e14..2521d3b 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
@@ -33,7 +33,7 @@ import org.apache.jena.atlas.web.AuthScheme;
 import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.FusekiConfigException;
 import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.access.*;
+import org.apache.jena.fuseki.access.DataAccessCtl;
 import org.apache.jena.fuseki.auth.Auth;
 import org.apache.jena.fuseki.auth.AuthPolicy;
 import org.apache.jena.fuseki.build.FusekiConfig;
@@ -96,7 +96,7 @@ import org.slf4j.Logger;
 
 public class FusekiServer {
     static { JenaSystem.init(); }
-    
+
     /** Construct a Fuseki server for one dataset.
      * It only responds to localhost.
      * The returned server has not been started.
@@ -232,7 +232,7 @@ public class FusekiServer {
 
     /** FusekiServer.Builder */
     public static class Builder {
-        private final DataAccessPointRegistry  dataAccessPoints = 
+        private final DataAccessPointRegistry  dataAccessPoints =
             new DataAccessPointRegistry( 
MetricsProviderRegistry.get().getMeterRegistry() );
         private final OperationRegistry  operationRegistry;
         // Default values.
@@ -242,6 +242,7 @@ public class FusekiServer {
         private boolean                  verbose            = false;
         private boolean                  withStats          = false;
         private boolean                  withPing           = false;
+        private Map<String, String>      corsInitParams     = null;
 
         // Server wide authorization policy.
         // Endpoints, datasets and graphs within datasets may have addition 
policies.
@@ -250,7 +251,7 @@ public class FusekiServer {
         // HTTP authentication
         private String                   passwordFile       = null;
         private String                   realm              = null;
-        private AuthScheme               authScheme             = null;
+        private AuthScheme               authScheme         = null;
 
         // HTTPS
         private String                   httpsKeystore          = null;
@@ -264,6 +265,21 @@ public class FusekiServer {
         private String                   staticContentDir   = null;
         private SecurityHandler          securityHandler    = null;
         private Map<String, Object>      servletAttr        = new HashMap<>();
+
+        // The default CORS settings.
+        private static final Map<String, String> corsInitParamsDft = new 
LinkedHashMap<>();
+        static {
+            // This is the CrossOriginFilter default.
+            corsInitParamsDft.put(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, 
"*");
+            // Variatiosn from CrossOriginFilter defaults.
+            corsInitParamsDft.put(CrossOriginFilter.ALLOWED_METHODS_PARAM, 
"GET,POST,DELETE,PUT,HEAD,OPTIONS,PATCH");
+            corsInitParamsDft.put(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
+                "X-Requested-With, Content-Type, Accept, Origin, 
Last-Modified, Authorization");
+            // The 7 CORS default exposed headers.
+            corsInitParamsDft.put(CrossOriginFilter.EXPOSED_HEADERS_PARAM,
+                "Cache-Control, Content-Language, Content-Length, 
Content-Type, Expires, Last-Modified, Pragma");
+        }
+
         // Builder with standard operation-action mapping.
         Builder() {
             this.operationRegistry = OperationRegistry.createStd();
@@ -377,6 +393,14 @@ public class FusekiServer {
             return this;
         }
 
+        /** Add the Cross Origin (CORS) filter.
+         * {@link CrossOriginFilter}.
+         */
+        public Builder enableCors(boolean withCORS) {
+            corsInitParams = withCORS ? corsInitParamsDft : null ;
+            return this;
+        }
+
         /** Add the "/$/stats" servlet that responds with stats about the 
server,
          * including counts of all calls made.
          */
@@ -473,7 +497,7 @@ public class FusekiServer {
             Model model = AssemblerUtils.readAssemblerFile(filename);
             parseConfig(model);
             return this;
-            
+
         }
 
         /**
@@ -575,7 +599,7 @@ public class FusekiServer {
         /**
          * Set the password file. This will be used to build a {@link 
#securityHandler
          * security handler} if one is not supplied. Setting null clears any 
previous entry.
-         * The file should be in the format of 
+         * The file should be in the format of
          * <a 
href="https://www.eclipse.org/jetty/documentation/current/configuring-security.html#hash-login-service";>Eclipse
 jetty password file</a>.
          */
         public Builder passwordFile(String passwordFile) {
@@ -609,7 +633,7 @@ public class FusekiServer {
         public Builder addProcessor(String pathSpec, ActionProcessor 
processor, Logger log) {
             requireNonNull(pathSpec, "pathSpec");
             requireNonNull(processor, "processor");
-            
+
             HttpServlet servlet;
             if ( processor instanceof HttpServlet )
                 servlet = (HttpServlet)processor;
@@ -707,7 +731,7 @@ public class FusekiServer {
 
         /**
          * Create an endpoint on the dataset i.e. {@code /dataset/} for an 
operation that has other query parameters
-         * or a Content-Type that distinguishes it.  
+         * or a Content-Type that distinguishes it.
          * The operation must already be registered with the builder.
          * @see #registerOperation(Operation, ActionService)
          */
@@ -719,8 +743,8 @@ public class FusekiServer {
         /**
          * Create an endpoint on the dataset i.e. {@code /dataset/} for an 
operation that has other query parameters
          * or a Content-Type that distinguishes it.  Use {@link 
#addEndpoint(String, String, Operation)} when
-         * the functionality is invoked by presence of a name in the URL after 
the dataset name.  
-         * 
+         * the functionality is invoked by presence of a name in the URL after 
the dataset name.
+         *
          * The operation must already be registered with the builder.
          * @see #registerOperation(Operation, ActionService)
          */
@@ -783,14 +807,14 @@ public class FusekiServer {
         }
 
         // These booleans are only for validation.
-        // They do not affect the build() step. 
+        // They do not affect the build() step.
 
         // Triggers some checking.
         private boolean hasAuthenticationHandler = false;
-        
-        // Whether there is any per-graph access control. 
+
+        // Whether there is any per-graph access control.
         private boolean hasDataAccessControl     = false;
-        
+
         // Do we need to authenticate the user?
         // Triggers some checking.
         private boolean authenticateUser         = false;
@@ -841,10 +865,10 @@ public class FusekiServer {
             // Test for any AuthPolicy that accepts "no user".
             return policy == null || policy == Auth.ANY_ANON || 
policy.isAllowed(null);
         }
-        
+
         /** Test whether some server authorization is needed. */
         private boolean hasServerWideAuth() {
-            return ! authAny(serverAuth); 
+            return ! authAny(serverAuth);
         }
 
         private void buildFinish() {
@@ -878,13 +902,13 @@ public class FusekiServer {
             JettyLib.setMimeTypes(handler);
             servletsAndFilters(handler);
             buildAccessControl(handler);
-            
+
             dapRegistry.forEach((name, dap) -> {
                 // Custom processors (endpoint specific,fuseki:implementation) 
will have already
-                // been set; all others need setting from the 
OperationRegistry in scope. 
+                // been set; all others need setting from the 
OperationRegistry in scope.
                 dap.getDataService().setEndpointProcessors(operationReg);
                 dap.getDataService().forEachEndpoint(ep->{
-                    // Override for graph-level access control. 
+                    // Override for graph-level access control.
                     if ( 
DataAccessCtl.isAccessControlled(dap.getDataService().getDataset()) )
                         FusekiLib.modifyForAccessCtl(ep, 
DataAccessCtl.requestUserServlet);
                 });
@@ -906,7 +930,7 @@ public class FusekiServer {
                     }
                     else {
                         // Find datasets that need login.
-                        // If any endpoint 
+                        // If any endpoint
                         
DataAccessPointRegistry.get(cxt.getServletContext()).forEach((name, dap)-> {
                             DatasetGraph dsg = 
dap.getDataService().getDataset();
                             if ( ! authAny(dap.getDataService().authPolicy()) 
) {
@@ -918,11 +942,11 @@ public class FusekiServer {
                                 dap.getDataService().forEachEndpoint(ep->{
                                     // repeats.
                                     if ( ! authAny(ep.getAuthPolicy()) ) {
-                                        // Unnamed - applies to the dataset. 
Yuk.  
+                                        // Unnamed - applies to the dataset. 
Yuk.
                                         if ( ep.getName().isEmpty() ) {
                                             JettyLib.addPathConstraint(csh, 
DataAccessPoint.canonical(name));
                                             JettyLib.addPathConstraint(csh, 
DataAccessPoint.canonical(name)+"/*");
-                                            
+
                                         } else {
                                             // Named.
                                             JettyLib.addPathConstraint(csh, 
DataAccessPoint.canonical(name)+"/"+ep.getName());
@@ -960,7 +984,17 @@ public class FusekiServer {
                 AuthFilter authFilter = new AuthFilter(auth);
                 addFilter(context, "/*", authFilter);
             }
-            // Second in chain. Looks for any URL that starts with a dataset 
name.
+
+            // CORS, maybe
+            if ( corsInitParams != null ) {
+                Filter corsFilter = new CrossOriginFilter();
+                FilterHolder holder = new FilterHolder(corsFilter);
+                holder.setInitParameters(corsInitParams);
+                addFilterHolder(context, "/*", holder);
+            }
+
+            // End of chain. May dispatch and not pass on requests.
+            // Looks for any URL that starts with a dataset name.
             FusekiFilter ff = new FusekiFilter();
             addFilter(context, "/*", ff);
 
@@ -988,8 +1022,12 @@ public class FusekiServer {
         }
 
         private void addFilter(ServletContextHandler context, String pathspec, 
Filter filter) {
-            FilterHolder h = new FilterHolder(filter);
-            context.addFilter(h, pathspec, null);
+            FilterHolder holder = new FilterHolder(filter);
+            addFilterHolder(context, pathspec, holder);
+        }
+
+        private void addFilterHolder(ServletContextHandler context, String 
pathspec, FilterHolder holder) {
+            context.addFilter(holder, pathspec, null);
         }
 
         /** Jetty server with one connector/port. */
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
index 76e33f0..755f14c 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
@@ -86,6 +86,7 @@ public class FusekiMain extends CmdARQ {
         private static ArgDecl  argGZip         = new 
ArgDecl(ArgDecl.HasValue, "gzip");
         private static ArgDecl  argBase         = new 
ArgDecl(ArgDecl.HasValue, "base", "files");
 
+        private static ArgDecl  argCORS         = new ArgDecl(ArgDecl.NoValue, 
"withCORS", "cors", "CORS");
         private static ArgDecl  argWithPing     = new ArgDecl(ArgDecl.NoValue, 
"withPing", "ping");
         private static ArgDecl  argWithStats    = new ArgDecl(ArgDecl.NoValue, 
"withStats", "stats");
 
@@ -166,6 +167,7 @@ public class FusekiMain extends CmdARQ {
             add(argHttpsPort, "--httpsPort=NUM", "https port (default port is 
3043)");
 
             add(argPasswdFile, "--passwd=FILE", "Password file");
+            add(argCORS, "--cors", "Enable CORS");
             // put in the configuration file
 //            add(argRealm, "--realm=REALM", "Realm name");
            add(argWithPing,    "--ping",   "Enable /$/ping");
@@ -387,6 +389,7 @@ public class FusekiMain extends CmdARQ {
                 serverConfig.authScheme = AuthScheme.scheme(schemeStr);
             }
 
+            serverConfig.withCORS = contains(argCORS);
             serverConfig.withPing = contains(argWithPing);
             serverConfig.withStats = contains(argWithStats);
 
@@ -490,6 +493,9 @@ public class FusekiMain extends CmdARQ {
             if ( serverConfig.authScheme != null )
                 builder.auth(serverConfig.authScheme);
 
+            if ( serverConfig.withCORS )
+                builder.enableCors(true);
+
             if ( serverConfig.withPing )
                 builder.enablePing(true);
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerConfig.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerConfig.java
index 6a3a48d..5cf11bb 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerConfig.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerConfig.java
@@ -37,6 +37,7 @@ class ServerConfig {
 
     public boolean   verboseLogging   = false;
 
+    public boolean withCORS           = false;
     public boolean withPing           = false;
     public boolean withStats          = false;
 

Reply via email to