You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2011/12/05 12:59:29 UTC

svn commit: r1210420 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/ systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/

Author: sergeyb
Date: Mon Dec  5 11:59:29 2011
New Revision: 1210420

URL: http://svn.apache.org/viewvc?rev=1210420&view=rev
Log:
[CXF-3943] Using JAX-RS Runtime to confirm a resource method exists during a preflight check

Removed:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingPaths.java
Modified:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharing.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingFilter.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/UnannotatedCorsServer.java

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharing.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharing.java?rev=1210420&r1=1210419&r2=1210420&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharing.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharing.java Mon Dec  5 11:59:29 2011
@@ -55,11 +55,6 @@ public @interface CrossOriginResourceSha
      */
     String[] allowOrigins() default { };
     /**
-     * A list of HTTP methods. This is used only for preflight,
-     * and is only valid on a class.
-     */
-    String[] allowMethods() default { };
-    /**
      * A list of headers that the client may include
      * in an actual request.
      */
@@ -90,9 +85,4 @@ public @interface CrossOriginResourceSha
      */
     boolean localPreflight() default false;
     
-    /**
-     * For use inside @{@link CrossOriginResourceSharingPaths}. The path to apply the
-     * policies to.
-     */
-    String path() default "";
 }

Modified: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingFilter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingFilter.java?rev=1210420&r1=1210419&r2=1210420&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingFilter.java (original)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/cors/CrossOriginResourceSharingFilter.java Mon Dec  5 11:59:29 2011
@@ -19,6 +19,7 @@
 
 package org.apache.cxf.jaxrs.cors;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -27,16 +28,23 @@ import java.util.regex.Pattern;
 
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 
 import org.apache.cxf.common.util.ReflectionUtil;
+import org.apache.cxf.jaxrs.JAXRSServiceImpl;
 import org.apache.cxf.jaxrs.ext.RequestHandler;
 import org.apache.cxf.jaxrs.ext.ResponseHandler;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
 import org.apache.cxf.jaxrs.model.OperationResourceInfo;
+import org.apache.cxf.jaxrs.model.URITemplate;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.message.Message;
+import org.apache.cxf.service.Service;
 
 /**
  * An single class that provides both an input and an output filter for CORS, following
@@ -65,7 +73,6 @@ public class CrossOriginResourceSharingF
      * deployment.
      */
     private List<String> allowOrigins = Collections.emptyList();
-    private List<String> allowMethods = Collections.emptyList();
     private List<String> allowHeaders = Collections.emptyList();
     private boolean allowAllOrigins;
     private boolean allowCredentials;
@@ -172,32 +179,18 @@ public class CrossOriginResourceSharingF
             return null;
         }
         String requestMethod = requestMethodValues.get(0);
-
         /*
          * CORS doesn't send enough information with a preflight to accurately identity the single method
-         * that will handle the request. CrossOriginResourceSharingPaths provides annotations by path/method
-         * for this case. If none of those apply, a plain class level CrossOrginResourceSharing is the
-         * best we can do.
+         * that will handle the request. We ask the JAX-RS runtime to find the matching method which is
+         * expected to have a CrossOriginResourceSharing annotation set.
          */
-        String requestUri = HttpUtils.getPathToMatch(m, true);
-        CrossOriginResourceSharing ann = null;
-        CrossOriginResourceSharingPaths classPathsAnn = 
-            resourceClass.getResourceClass().getAnnotation(CrossOriginResourceSharingPaths.class);
-        if (classPathsAnn != null) {
-            /* search the path/method pair. */
-            for (CrossOriginResourceSharing pathAnn : classPathsAnn.value()) {
-                /* A very simple path policy! If someone wants to turn this into
-                 * searching up the tree, they are welcome.
-                 */
-                if (pathAnn.path() != null && pathAnn.path().equals(requestUri) 
-                    && Arrays.asList(pathAnn.allowMethods()).contains(requestMethod)) {
-                    ann = pathAnn;
-                    break;
-                }
-            }
-        }
+        
+        Method method = getPreflightMethod(m, requestMethod);
+        
+        CrossOriginResourceSharing ann = method.getAnnotation(CrossOriginResourceSharing.class);
+        ann = ann == null ? optionAnn : ann; 
         if (ann == null) {
-            ann = resourceClass.getResourceClass().getAnnotation(CrossOriginResourceSharing.class);
+            return null;
         }
 
         // 5.2.2 must be on the list or we must be matching *.
@@ -210,11 +203,7 @@ public class CrossOriginResourceSharingF
         List<String> requestHeaders = getHeaderValues(CorsHeaderConstants.HEADER_AC_REQUEST_HEADERS, false);
 
         // 5.2.5 reject if the method is not on the list.
-        List<String> effectiveAllowMethods = effectiveAllowMethods(ann);
-
-        if (!effectiveAllowMethods.contains(requestMethod)) {
-            return null;
-        }
+        // This was indirectly enforced by getCorsMethod()
 
         // 5.2.6 reject if the header is not listed.
         if (!effectiveAllowHeaders(ann).containsAll(requestHeaders)) {
@@ -248,6 +237,58 @@ public class CrossOriginResourceSharingF
     }
     //CHECKSTYLE:ON
 
+    private Method getPreflightMethod(Message m, String httpMethod) {
+        String requestUri = HttpUtils.getPathToMatch(m, true);
+        
+        Service service = m.getExchange().get(Service.class);
+        List<ClassResourceInfo> resources = ((JAXRSServiceImpl)service).getClassResourceInfos();
+        MultivaluedMap<String, String> values = new MetadataMap<String, String>();
+        ClassResourceInfo resource = JAXRSUtils.selectResourceClass(resources, 
+                                                                    requestUri, 
+                                                                    values,
+                                                                    m);
+        if (resource == null) {
+            return null;
+        }
+        OperationResourceInfo ori = findPreflightMethod(resource, requestUri, httpMethod, values, m);
+        return ori == null ? null : ori.getAnnotatedMethod();
+    }
+    
+    
+    private OperationResourceInfo findPreflightMethod(ClassResourceInfo resource, 
+                                                      String requestUri,
+                                                      String httpMethod,
+                                                      MultivaluedMap<String, String> values, 
+                                                      Message m) {
+        final String contentType = MediaType.WILDCARD;
+        final MediaType acceptType = MediaType.WILDCARD_TYPE;
+        OperationResourceInfo ori = JAXRSUtils.findTargetMethod(resource, 
+                                    m, httpMethod, values, 
+                                    contentType, 
+                                    Collections.singletonList(acceptType), 
+                                    true); 
+        if (ori == null) {
+            return null;
+        }
+        if (ori.isSubResourceLocator()) {
+            Class<?> cls = ori.getMethodToInvoke().getReturnType();
+            ClassResourceInfo subcri = resource.getSubResource(cls, cls);
+            if (subcri == null) {
+                return null;
+            } else {
+                MultivaluedMap<String, String> newValues = new MetadataMap<String, String>();
+                newValues.putAll(values);
+                return findPreflightMethod(subcri, 
+                                           values.getFirst(URITemplate.FINAL_MATCH_GROUP),
+                                           httpMethod, 
+                                           newValues, 
+                                           m);
+            }
+        } else {
+            return ori;
+        }
+    }
+    
     private void commonRequestProcessing(Message m, CrossOriginResourceSharing ann, String origin) {
         
         m.getExchange().put(CorsHeaderConstants.HEADER_ORIGIN, origin);
@@ -331,17 +372,6 @@ public class CrossOriginResourceSharingF
         }
     }
     
-    private List<String> effectiveAllowMethods(CrossOriginResourceSharing ann) {
-        if (ann != null) {
-            if (ann.allowMethods() == null) {
-                return Collections.emptyList();
-            }
-            return Arrays.asList(ann.allowMethods());
-        } else {
-            return allowMethods;
-        }
-    }
-
     private List<String> effectiveAllowHeaders(CrossOriginResourceSharing ann) {
         if (ann != null) {
             if (ann.allowHeaders() == null) {
@@ -461,19 +491,7 @@ public class CrossOriginResourceSharingF
         this.allowAllOrigins = allowAllOrigins;
     }
 
-    public List<String> getAllowMethods() {
-        return allowMethods;
-    }
-
-    /**
-     * The list of allowed non-simple methods for preflight checks. Section 5.2.3.
-     * 
-     * @param allowedMethods a list of case-sensitive HTTP method names.
-     */
-    public void setAllowMethods(List<String> allowedMethods) {
-        this.allowMethods = allowedMethods;
-    }
-
+    
     public List<String> getAllowHeaders() {
         return allowHeaders;
     }

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java?rev=1210420&r1=1210419&r2=1210420&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java Mon Dec  5 11:59:29 2011
@@ -71,7 +71,7 @@ public class JAXRSClientServerBookTest e
     @BeforeClass
     public static void startServers() throws Exception {
         assertTrue("server did not launch correctly",
-                   launchServer(BookServer.class));
+                   launchServer(BookServer.class, true));
     }
     
     @Test

Modified: 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=1210420&r1=1210419&r2=1210420&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/AnnotatedCorsServer.java Mon Dec  5 11:59:29 2011
@@ -39,9 +39,7 @@ import org.apache.cxf.jaxrs.cors.CrossOr
  */
 @CrossOriginResourceSharing(allowOrigins = {
         "http://area51.mil:31415"
-        }, allowCredentials = true, maxAge = 1, allowMethods = {
-        "PUT"
-        }, allowHeaders = {
+        }, allowCredentials = true, maxAge = 1, allowHeaders = {
         "X-custom-1", "X-custom-2"
         }, exposeHeaders = {
         "X-custom-3", "X-custom-4"

Modified: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/UnannotatedCorsServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/UnannotatedCorsServer.java?rev=1210420&r1=1210419&r2=1210420&view=diff
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/UnannotatedCorsServer.java (original)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/cors/UnannotatedCorsServer.java Mon Dec  5 11:59:29 2011
@@ -29,20 +29,10 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.Response;
 
 import org.apache.cxf.jaxrs.cors.CrossOriginResourceSharing;
-import org.apache.cxf.jaxrs.cors.CrossOriginResourceSharingPaths;
 
 /**
  * Service bean with no class-level annotation for cross-script control.
  */
-@CrossOriginResourceSharingPaths(
- @CrossOriginResourceSharing(path = "/annotatedPut", 
-     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 UnannotatedCorsServer {
 
     @GET
@@ -78,6 +68,13 @@ public class UnannotatedCorsServer {
     @Consumes("text/plain")
     @Produces("text/plain")
     @Path("/annotatedPut")
+    @CrossOriginResourceSharing(
+        allowOrigins = { "http://area51.mil:31415" }, 
+        allowCredentials = true, 
+        maxAge = 1, 
+        allowHeaders = { "X-custom-1", "X-custom-2" },
+        exposeHeaders = {"X-custom-3", "X-custom-4" }
+    )
     public String annotatedPut(String input) {
         return input;
     }