cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bimargul...@apache.org
Subject svn commit: r1209596 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/ systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/ systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/
Date Fri, 02 Dec 2011 17:30:42 GMT
Author: bimargulies
Date: Fri Dec  2 17:30:42 2011
New Revision: 1209596

URL: http://svn.apache.org/viewvc?rev=1209596&view=rev
Log:
More tests, plus some substantive documentation. (CXF-3493)

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html   (with
props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
  (with props)
Modified:
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/CrossOriginSimpleTest.java
    cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/beans.xml

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html?rev=1209596&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html Fri Dec
 2 17:30:42 2011
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML >
+<html>
+<head>
+<!--
+  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.
+-->
+</head>
+<body bgcolor="white">
+<h1>CORS</h1>
+<p>This package provides a filter to assist applications in implementing Cross Origin
Resource Sharing, 
+as described in the <a href="http://www.w3.org/TR/cors">CORS specification</a>.
+</p>
+<h2>CORS Access Model</h2>
+<p>
+CORS exists to protect web servers from unexpected cross-origin access. The premise of CORS
is that many web resources 
+are deployed by people who don't want to permit cross-origin access, but who couldn't detect
it or didn't bother
+to control it. Thus, CORS defines a set of restrictions <em>implemented on the client</em>
that, by default, 
+prohibit cross-origin access.
+</p>
+<p>
+If you want your service to permit cross-origin access, your service must return additional
headers to the client to reassure
+it that you really want to permit the access. {@link CrossOriginResourceSharingFilter} adds
these headers to your service's 
+responses based on rules that you configure.
+</p>
+<h2>CORS Resource Model (versus JAX-RS)</h2>
+<p>
+CORS and JAX-RS differ, fundamentally, in how they define a resource for access control purposes.
In CORS, a resource
+is defined by the combination of URI and HTTP method. Once a client has obtained access information
for a URI+METHOD,
+it may cache it. JAX-RS, on the other hand, defines a resource as:
+<ul>
+<li>URI</li>
+<li>method</li>
+<li>Accepts</li>
+<li>Accepts-Language</li>
+<li>Content-Type</li>
+</ul>
+The logical place, in other words, to specify CORS policy in a JAX-RS application is at the
level of an annotated method. However, each method is
+applied to the narrow 'resource' defined by the list above, not just the URI+Method pair.
This will motivate the annotation model below. 
+</p>
+<h2>Simple and Preflight requests</h2>
+<p>The CORS specification differentiates two kinds of HTTP requests: <em>simple</em>
and <em>not simple</em>. (See the specification
+for the definition.) For a simple request, the client simply
+sends the request to the service, and then looks for the <tt>Access-Control-</tt>
headers to indicate whether the server has explicitly granted 
+cross-origin access. For a non-simple request, the client sends a so-called <em>preflight</em>
request and waits for a response before 
+issuing the original request.
+<h2>Configuration via Annotation</h2>
+<p>
+One way to control the behavior of the filter is the @{@link CrossOriginResourceSharing}
annotation on a method. 
+This is a complete solution for simple requests. You can specify all of the controls. However,
if you have non-simple methods, the mismatch on 
+resource access models above makes it impossible for CXF to map the OPTIONS request that
will arrive to the correct method.
+</p>
+<p>
+If all the methods of a class can share a common policy, you can attach a single @{@link
CrossOriginResourceSharing} 
+to a resource class, and it will apply to all the resource implied by all of the methods.
+</p>
+<p>
+If you need finer control, you can use @{@link CrossOriginResourceSharingPaths} at the class
level. This annotation contains a list of 
+@{@link CrossOriginResourceSharing} annotations. In the nested annotations, you add <tt>path
= "/localResourcePath"</tt> to associate the 
+annotation with a path. The filter will find the annotation based on path, and then allow
(or forbid) based on the method
+as compared to the <tt>allowedMethods</tt>.
+</p>
+<h2>Bean Configuration</h2>
+<p>
+The simplest configuration applies when you want to apply the same configuration to all of
your resources. In this case, you can
+use the properties of {@link CrossOriginResourceSharingFilter} to specify the policy.
+</p>
+</body>
+</html>
\ No newline at end of file

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/package.html
------------------------------------------------------------------------------
    svn:mime-type = text/html

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java?rev=1209596&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
(added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
Fri Dec  2 17:30:42 2011
@@ -0,0 +1,104 @@
+/**
+ * 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.cxf.systest.jaxrs.cors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.jaxrs.cors.CorsHeaderConstants;
+import org.apache.cxf.jaxrs.cors.CrossOriginResourceSharing;
+
+/**
+ * Service bean with no class-level annotation for cross-script control.
+ */
+@CrossOriginResourceSharing(allowOrigins = {
+        "http://area51.mil:31415"
+        }, allowCredentials = true, maxAge = 1, allowMethods = {
+        "PUT"
+        }, allowHeaders = {
+        "X-custom-1", "X-custom-2"
+        }, exposeHeaders = {
+        "X-custom-3", "X-custom-4"
+        }
+)
+public class AnnotatedCorsServer {
+    @Context
+    private HttpHeaders headers;
+
+    @GET
+    @Produces("text/plain")
+    @Path("/simpleGet/{echo}")
+    public String simpleGet(@PathParam("echo") String echo) {
+        return echo;
+    }
+
+    @DELETE
+    @Path("/delete")
+    public Response deleteSomething() {
+        return Response.ok().build();
+    }
+
+    @OPTIONS
+    @Path("/delete")
+    @CrossOriginResourceSharing(localPreflight = true)
+    public Response deleteOptions() {
+        String origin = headers.getRequestHeader("Origin").get(0);
+        if ("http://area51.mil:3333".equals(origin)) {
+            return Response.ok().header(CorsHeaderConstants.HEADER_AC_ALLOW_METHODS, "DELETE
PUT")
+                .header(CorsHeaderConstants.HEADER_AC_ALLOW_CREDENTIALS, "false")
+                .header(CorsHeaderConstants.HEADER_AC_ALLOW_ORIGIN, "http://area51.mil:3333").build();
+        } else {
+            return Response.ok().build();
+        }
+    }
+
+    @GET
+    @CrossOriginResourceSharing(allowOrigins = { "http://area51.mil:31415" }, 
+            allowCredentials = true, 
+            exposeHeaders = { "X-custom-3", "X-custom-4" })
+    @Produces("text/plain")
+    @Path("/annotatedGet/{echo}")
+    public String annotatedGet(@PathParam("echo") String echo) {
+        return echo;
+    }
+
+    /**
+     * A method annotated to test preflight.
+     * 
+     * @param input
+     * @return
+     */
+    @PUT
+    @Consumes("text/plain")
+    @Produces("text/plain")
+    @Path("/annotatedPut")
+    public String annotatedPut(String input) {
+        return input;
+    }
+}

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/CrossOriginSimpleTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/CrossOriginSimpleTest.java?rev=1209596&r1=1209595&r2=1209596&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/CrossOriginSimpleTest.java
(original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/CrossOriginSimpleTest.java
Fri Dec  2 17:30:42 2011
@@ -47,8 +47,7 @@ import org.junit.Ignore;
 import org.junit.Test;
 
 /**
- * Unit tests for simple CORS requests. Simple requests traffic only in allowed origins,
- * allowed credentials, and exposed headers. 
+ * Unit tests for CORS. This isn't precisely simple as it's turned out. 
  * 
  * Note that it's not the server's job to detect invalid CORS requests. If a client
  * fails to preflight, it's just not our job. However, also note that all 'actual' 
@@ -304,6 +303,79 @@ public class CrossOriginSimpleTest exten
             = headerValues(response.getHeaders(CorsHeaderConstants.HEADER_AC_ALLOW_HEADERS));
         assertEquals(Arrays.asList(new String[] {"X-custom-1", "X-custom-2" }), allowHeadersValues);
     }
+    
+    @Test
+    public void testAnnotatedClassCorrectOrigin() throws Exception {
+        HttpClient httpclient = new DefaultHttpClient();
+        HttpGet httpget = new HttpGet("http://localhost:" + PORT + "/antest/simpleGet/HelloThere");
+        httpget.addHeader("Origin", "http://area51.mil:31415");
+
+        HttpResponse response = httpclient.execute(httpget);
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        HttpEntity entity = response.getEntity();
+        String e = IOUtils.toString(entity.getContent(), "utf-8");
+
+        assertEquals("HelloThere", e); // ensure that we didn't bust the operation itself.
+        assertOriginResponse(false, new String[] {"http://area51.mil:31415" }, true, response);
+    }
+    
+    @Test
+    public void testAnnotatedClassWrongOrigin() throws Exception {
+        HttpClient httpclient = new DefaultHttpClient();
+        HttpGet httpget = new HttpGet("http://localhost:" + PORT + "/antest/simpleGet/HelloThere");
+        httpget.addHeader("Origin", "http://su.us:1001");
+
+        HttpResponse response = httpclient.execute(httpget);
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        HttpEntity entity = response.getEntity();
+        String e = IOUtils.toString(entity.getContent(), "utf-8");
+
+        assertEquals("HelloThere", e);
+        assertOriginResponse(false, null, false, response);
+    }
+    
+    @Test
+    public void testAnnotatedLocalPreflight() throws Exception {
+        configureAllowOrigins(true, null);
+        String r = configClient.replacePath("/setAllowCredentials/false")
+            .accept("text/plain").post(null, String.class);
+        assertEquals("ok", r);
+        
+        HttpClient httpclient = new DefaultHttpClient();
+        HttpOptions http = new HttpOptions("http://localhost:" + PORT + "/antest/delete");
+        // this is the origin we expect to get.
+        http.addHeader("Origin", "http://area51.mil:3333");
+        http.addHeader(CorsHeaderConstants.HEADER_AC_REQUEST_METHOD, "DELETE");
+        HttpResponse response = httpclient.execute(http);
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        assertOriginResponse(false, new String[]{"http://area51.mil:3333"}, true, response);
+        assertAllowCredentials(response, false);
+        List<String> exposeHeadersValues 
+            = headerValues(response.getHeaders(CorsHeaderConstants.HEADER_AC_EXPOSE_HEADERS));
+        // preflight never returns Expose-Headers
+        assertEquals(Collections.emptyList(), exposeHeadersValues);
+        List<String> allowedMethods     
+            = headerValues(response.getHeaders(CorsHeaderConstants.HEADER_AC_ALLOW_METHODS));
+        assertEquals(Arrays.asList("DELETE PUT"), allowedMethods);
+    }
+    
+    @Test
+    public void testAnnotatedLocalPreflightNoGo() throws Exception {
+        configureAllowOrigins(true, null);
+        String r = configClient.replacePath("/setAllowCredentials/false")
+            .accept("text/plain").post(null, String.class);
+        assertEquals("ok", r);
+        
+        HttpClient httpclient = new DefaultHttpClient();
+        HttpOptions http = new HttpOptions("http://localhost:" + PORT + "/antest/delete");
+        // this is the origin we expect to get.
+        http.addHeader("Origin", "http://area51.mil:4444");
+        http.addHeader(CorsHeaderConstants.HEADER_AC_REQUEST_METHOD, "DELETE");
+        HttpResponse response = httpclient.execute(http);
+        assertEquals(200, response.getStatusLine().getStatusCode());
+        assertOriginResponse(false, new String[]{"http://area51.mil:4444"}, false, response);
+        // we could check that the others are also missing.
+    }
 
     @Ignore
     public static class SpringServer extends AbstractSpringServer {

Modified: cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/beans.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/beans.xml?rev=1209596&r1=1209595&r2=1209596&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/beans.xml (original)
+++ cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_cors/WEB-INF/beans.xml Fri Dec  2 17:30:42
2011
@@ -25,18 +25,29 @@ http://cxf.apache.org/core 
 		<property name="allowAllOrigins" value="true" />
 	</bean>
 
-	<jaxrs:server id="cors-service" address="/untest">
+	<jaxrs:server id="unann-cors-service" address="/untest">
 		<jaxrs:serviceBeans>
-			<ref bean="cors-server" />
+			<ref bean="unann-cors-server" />
 		</jaxrs:serviceBeans>
 		<jaxrs:providers>
 			<ref bean="cors-filter" />
-		</jaxrs:providers><!-- 
+		</jaxrs:providers>
+		<!-- 
 		<jaxrs:features>
 			<cxf:logging />
-		</jaxrs:features>-->
+		</jaxrs:features>
+		-->
+	</jaxrs:server>
 
+	<jaxrs:server id="ann-cors-service" address="/antest">
+		<jaxrs:serviceBeans>
+			<ref bean="ann-cors-server" />
+		</jaxrs:serviceBeans>
+		<jaxrs:providers>
+			<ref bean="cors-filter" />
+		</jaxrs:providers>
 	</jaxrs:server>
+	
 	<jaxrs:server id="config-service" address="/config">
 		<jaxrs:serviceBeans>
 			<ref bean="config-server" />
@@ -49,6 +60,8 @@ http://cxf.apache.org/core 
 	<bean id="config-server" class="org.apache.cxf.systest.jaxrs.cors.ConfigServer">
 		<property name='inputFilter' ref='cors-filter'/>
 	</bean>
-	<bean id="cors-server" scope="prototype"
+	<bean id="unann-cors-server" scope="prototype"
 		class="org.apache.cxf.systest.jaxrs.cors.UnannotatedCorsServer" />
+	<bean id="ann-cors-server" scope="prototype"
+		class="org.apache.cxf.systest.jaxrs.cors.AnnotatedCorsServer" />
 </beans>



Mime
View raw message