fixes cors problems
Project: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/commit/6029baec Tree: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/tree/6029baec Diff: http://git-wip-us.apache.org/repos/asf/incubator-marmotta/diff/6029baec Branch: refs/heads/develop Commit: 6029baec9e10bca9dbca317092505122cc922e8d Parents: c7d85fc Author: tkurz <[email protected]> Authored: Mon Apr 8 10:26:26 2013 +0200 Committer: tkurz <[email protected]> Committed: Mon Apr 8 10:26:26 2013 +0200 ---------------------------------------------------------------------- .../src/main/webapp/WEB-INF/web.xml | 3 +- .../platform/core/filters/MarmottaCorsFilter.java | 100 +++++++++++++ .../marmotta/platform/core/util/CorsHandler.java | 90 ++++++++++++ .../platform/core/test/cors/CorsFilterTest.java | 111 +++++++++++++++ .../sparql/webservices/SparqlWebService.java | 3 +- 5 files changed, 305 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/6029baec/launchers/marmotta-webapp/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/launchers/marmotta-webapp/src/main/webapp/WEB-INF/web.xml b/launchers/marmotta-webapp/src/main/webapp/WEB-INF/web.xml index 2f53049..1e417bf 100644 --- a/launchers/marmotta-webapp/src/main/webapp/WEB-INF/web.xml +++ b/launchers/marmotta-webapp/src/main/webapp/WEB-INF/web.xml @@ -60,6 +60,7 @@ </filter-mapping> <!-- handle OPTIONS requests --> + <!-- <filter> <filter-name>MarmottaOptionsFilter</filter-name> <filter-class>org.apache.marmotta.platform.core.servlet.MarmottaOptionsFilter</filter-class> @@ -68,7 +69,7 @@ <filter-name>MarmottaOptionsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> - + --> <!-- Serve static resources from file system and from .jar files of the respective modules --> <filter> <filter-name>MarmottaResourceFilter</filter-name> http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/6029baec/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/filters/MarmottaCorsFilter.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/filters/MarmottaCorsFilter.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/filters/MarmottaCorsFilter.java new file mode 100644 index 0000000..8828135 --- /dev/null +++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/filters/MarmottaCorsFilter.java @@ -0,0 +1,100 @@ +/* + * 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.marmotta.platform.core.filters; + +import org.apache.marmotta.platform.core.api.config.ConfigurationService; +import org.apache.marmotta.platform.core.api.modules.MarmottaHttpFilter; +import org.apache.marmotta.platform.core.events.ConfigurationChangedEvent; +import org.apache.marmotta.platform.core.util.CorsHandler; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * ... + * <p/> + * Author: Thomas Kurz ([email protected]) + */ +@ApplicationScoped +public class MarmottaCorsFilter implements MarmottaHttpFilter { + + public static Map<String,Object> options; + + @Inject + ConfigurationService configurationService; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + initializeOptions(); + } + + public void configChanged(@Observes ConfigurationChangedEvent event) { + if (event.getKeys().contains("kiwi.allow_origin") + || event.getKeys().contains("kiwi.allow_methods")) { + initializeOptions(); + } + } + + private void initializeOptions() { + options = new HashMap<String,Object>(); + options.put("Access-Control-Allow-Origin",configurationService.getStringConfiguration("kiwi.allow_origin","*")); + options.put("Access-Control-Allow-Methods", configurationService.getStringConfiguration("kiwi.allow_methods","POST, PUT, GET, DELETE, HEAD")); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if(response instanceof HttpServletResponse) { + HttpServletResponse resp = (HttpServletResponse)response; + HttpServletRequest req = (HttpServletRequest)request; + + String origin = req.getHeader("Origin"); + + if(origin != null) { + CorsHandler.run(req,resp,options); + } + + if(req.getMethod().equalsIgnoreCase("OPTIONS")) { + resp.setStatus(200); + } + } + chain.doFilter(request,response); + } + + @Override + public void destroy() { + // + } + + @Override + public String getPattern() { + return "^/.*"; + } + + @Override + public int getPriority() { + return 0; + } +} http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/6029baec/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CorsHandler.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CorsHandler.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CorsHandler.java new file mode 100644 index 0000000..abd6b61 --- /dev/null +++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CorsHandler.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.marmotta.platform.core.util; + +import org.apache.commons.lang.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * ... + * <p/> + * Author: Thomas Kurz ([email protected]) + */ +public class CorsHandler { + + private static final String[] CORS_HEADERS= { + "Access-Control-Allow-Origin", + "Access-Control-Expose-Headers", + "Access-Control-Max-Age", + "Access-Control-Allow-Credentials", + "Access-Control-Allow-Methods", + "Access-Control-Allow-Headers"}; + + /** + * This method sets the response headers for CORS request. The options may contain following fields: + * <ul> + * <li> + * "Access-Control-Allow-Origin" : List<String> | "*" + * </li> + * <li> + * "Access-Control-Expose-Headers" : List<String> + * </li> + * <li> + * "Access-Control-Max-Age" : long + * </li> + * <li> + * "Access-Control-Allow-Credentials" : boolean + * </li> + * <li> + * "Access-Control-Allow-Methods" : List<String> + * </li> + * <li> + * "Access-Control-Allow-Headers" : List<String> + * </li> + * </ul> + * @param request the HTTP servlet request + * @param response the HTTP servlet response + * @param options the options + */ + public static void run(HttpServletRequest request, HttpServletResponse response, Map<String,Object> options) { + + //remove all existing cors headers + for(String header : CORS_HEADERS) { + response.setHeader(header,null); + } + + //add headers from options + for(String key : options.keySet()) { + response.addHeader(key,buildHeader(options.get(key))); + } + + } + + private static String buildHeader(Object value) { + if(value instanceof List) { + return StringUtils.join((List)value,","); + } + return value.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/6029baec/platform/marmotta-core/src/test/java/org/apache/marmotta/platform/core/test/cors/CorsFilterTest.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-core/src/test/java/org/apache/marmotta/platform/core/test/cors/CorsFilterTest.java b/platform/marmotta-core/src/test/java/org/apache/marmotta/platform/core/test/cors/CorsFilterTest.java new file mode 100644 index 0000000..52390fe --- /dev/null +++ b/platform/marmotta-core/src/test/java/org/apache/marmotta/platform/core/test/cors/CorsFilterTest.java @@ -0,0 +1,111 @@ +/* + * 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.marmotta.platform.core.test.cors; + +import com.google.common.collect.Lists; +import com.jayway.restassured.RestAssured; +import org.apache.marmotta.platform.core.api.config.ConfigurationService; +import org.apache.marmotta.platform.core.test.base.JettyMarmotta; +import org.apache.marmotta.platform.core.webservices.config.ConfigurationWebService; +import org.codehaus.jackson.map.ObjectMapper; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import static com.jayway.restassured.RestAssured.given; + +/** + * ... + * <p/> + * Author: Thomas Kurz ([email protected]) + */ +public class CorsFilterTest { + + private static JettyMarmotta marmotta; + private static ConfigurationService configurationService; + + @BeforeClass + public static void setUp() { + marmotta = new JettyMarmotta("/marmotta", ConfigurationWebService.class); + configurationService = marmotta.getService(ConfigurationService.class); + + RestAssured.baseURI = "http://localhost"; + RestAssured.port = marmotta.getPort(); + RestAssured.basePath = marmotta.getContext(); + + } + + @AfterClass + public static void tearDown() { + marmotta.shutdown(); + } + + /** + * Sparql Service should use the basic cors functionality + */ + @Test + public void testCorsRequestOnSparqlService() throws UnsupportedEncodingException { + + //options request + given().header("Origin", "http://otherhost.com"). + expect().statusCode(200). + expect().header("Access-Control-Allow-Origin", "*"). + expect().header("Access-Control-Allow-Methods","POST, PUT, GET, DELETE, HEAD"). + when().options("/config/list"); + + //change configuration and retry + configurationService.setConfiguration("Access-Control-Allow-Origin","http://otherhost.com"); + + given().header("Origin", "http://otherhost.com"). + expect().statusCode(200). + expect().header("Access-Control-Allow-Origin", "http://otherhost.com"). + expect().header("Access-Control-Allow-Methods","POST, PUT, GET, DELETE, HEAD"). + when().options("/config/list"); + + //get request + given().header("Origin", "http://otherhost.com"). + given().header("Content-Type","application/json"). + given().content("[\"test\"]"). + expect().statusCode(200). + expect().header("Access-Control-Allow-Origin", "http://otherhost.com"). + expect().header("Access-Control-Allow-Methods","POST, PUT, GET, DELETE, HEAD"). + when().post("/config/data/key"); + + } + + /** + * resource Service should overwrite the basic cors functionality + */ + @Test + public void testCorsRequestOnResourceService() { + //change configuration + configurationService.setConfiguration("Access-Control-Allow-Origin","http://my.host.com"); + + //options request + given().header("Origin", "http://otherhost.com"). + expect().statusCode(201). + expect().header("Access-Control-Allow-Origin", "*"). + expect().header("Access-Control-Allow-Methods","POST, PUT, GET, DELETE, HEAD"). + when().post("/resource/123"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/6029baec/platform/marmotta-sparql/src/main/java/org/apache/marmotta/platform/sparql/webservices/SparqlWebService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-sparql/src/main/java/org/apache/marmotta/platform/sparql/webservices/SparqlWebService.java b/platform/marmotta-sparql/src/main/java/org/apache/marmotta/platform/sparql/webservices/SparqlWebService.java index f15f6df..b6d520e 100644 --- a/platform/marmotta-sparql/src/main/java/org/apache/marmotta/platform/sparql/webservices/SparqlWebService.java +++ b/platform/marmotta-sparql/src/main/java/org/apache/marmotta/platform/sparql/webservices/SparqlWebService.java @@ -185,7 +185,7 @@ public class SparqlWebService { * * @param reqHeaders * @return responde - */ + @OPTIONS @Path(UPDATE) public Response optionsResourceRemote(@HeaderParam("Access-Control-Request-Headers") String reqHeaders) { @@ -200,6 +200,7 @@ public class SparqlWebService { .build(); } + */ /** * Execute a SPARQL 1.1 tuple query on the LMF triple store using the query passed as form parameter to the
