Added CorsFilter to enable secure cross site scripting 45/945/3
authorMadhu Venugopal <vmadhu@cisco.com>
Wed, 28 Aug 2013 05:26:13 +0000 (22:26 -0700)
committerMadhu Venugopal <vmadhu@cisco.com>
Wed, 28 Aug 2013 05:26:13 +0000 (22:26 -0700)
This is in addition to Ed's original Cors Filter changes. Default Cors Config
doesnt seem to work in certain scenarios. Added some custom configurations
and also added it per-bundle (started with Flow & i will add it to other bundles
once this is verified).

Also, by default AngularJS like frameworks uses HTTP OPTIONS method to check for
server options and that doesnt carry authentication headers. Hence in order for
the cors to work properly, we have to ignore authentication for OPTIONS method
alone. This is taken care in the web.xml configuration for all the northbound
bundle.

See: http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter
And: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
This is done to allow a web page using javascript to be able to make calls
to our REST APIs even though it does not originate in our domain.

Added CorsFilter bundle in Third Party to bring in the class as a Fragment
on the org.apache.catalina bundle.

Added CorsFilter to the web/root web.xml file so it will be used for
all WebApps.

Change-Id: I5fc6a53f2046816984fab722b841730c0eee396a
Signed-off-by: Madhu Venugopal <vmadhu@cisco.com>
24 files changed:
opendaylight/distribution/opendaylight/pom.xml
opendaylight/northbound/flowprogrammer/pom.xml
opendaylight/northbound/flowprogrammer/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/hosttracker/pom.xml
opendaylight/northbound/hosttracker/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/networkconfiguration/bridgedomain/pom.xml
opendaylight/northbound/networkconfiguration/bridgedomain/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/staticrouting/pom.xml
opendaylight/northbound/staticrouting/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/statistics/pom.xml
opendaylight/northbound/statistics/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/subnets/pom.xml
opendaylight/northbound/subnets/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/switchmanager/pom.xml
opendaylight/northbound/switchmanager/src/main/resources/WEB-INF/web.xml
opendaylight/northbound/topology/pom.xml
opendaylight/northbound/topology/src/main/resources/WEB-INF/web.xml
opendaylight/samples/northbound/loadbalancer/pom.xml
opendaylight/samples/northbound/loadbalancer/src/main/resources/WEB-INF/web.xml
opendaylight/web/root/pom.xml
opendaylight/web/root/src/main/resources/WEB-INF/web.xml
third-party/org.apache.catalina.filters.CorsFilter/README [new file with mode: 0644]
third-party/org.apache.catalina.filters.CorsFilter/pom.xml [new file with mode: 0644]
third-party/org.apache.catalina.filters.CorsFilter/src/main/java/org/apache/catalina/filters/CorsFilter.java [new file with mode: 0644]

index abf508e..4dc4e6f 100644 (file)
@@ -68,6 +68,7 @@
     <module>../../../third-party/net.sf.jung2</module>
     <module>../../../third-party/jersey-servlet</module>
     <module>../../../third-party/commons/thirdparty</module>
+    <module>../../../third-party/org.apache.catalina.filters.CorsFilter</module>
 
     <!-- SAL bundles -->
     <module>../../sal/api</module>
index 94991c5..201bf47 100644 (file)
@@ -43,7 +43,8 @@
               org.opendaylight.controller.northbound.commons.utils,
               org.opendaylight.controller.sal.authorization,
               org.opendaylight.controller.usermanager,
-                          com.sun.jersey.spi.container.servlet,
+              com.sun.jersey.spi.container.servlet,
+              org.apache.catalina.filters,
               javax.ws.rs,
               javax.ws.rs.core,
               javax.xml.bind.annotation,
       <artifactId>com.sun.jersey.jersey-servlet</artifactId>
       <version>1.17-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 4cedf2d..5b3cec2 100644 (file)
           <url-pattern>/*</url-pattern>
         </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
     <security-role>
@@ -48,4 +87,4 @@
                 <auth-method>BASIC</auth-method>
                 <realm-name>opendaylight</realm-name>
         </login-config>
-</web-app>
\ No newline at end of file
+</web-app>
index 40aaf46..b588c07 100644 (file)
@@ -51,6 +51,7 @@
               javax.xml.bind.annotation,
               javax.xml.bind,
               org.slf4j,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Web-ContextPath>/controller/nb/v2/host</Web-ContextPath>
       <artifactId>enunciate-core-annotations</artifactId>
       <version>${enunciate.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 0fa8b95..01b8fed 100644 (file)
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index cba14fd..8cb1320 100644 (file)
@@ -52,6 +52,7 @@
               javax.ws.rs.core,
               javax.xml.bind.annotation,
               javax.xml.bind,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Export-Package>
       <artifactId>com.sun.jersey.jersey-servlet</artifactId>
       <version>1.17-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index b7f35c3..f4de222 100644 (file)
                 <url-pattern>/*</url-pattern>
         </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>BridgeDomain Configuration NorthBound API</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index bb88465..fa9341f 100644 (file)
@@ -50,6 +50,7 @@
               javax.ws.rs.core,
               javax.xml.bind.annotation,
               javax.xml.bind,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Export-Package>
       <artifactId>com.sun.jersey.jersey-servlet</artifactId>
       <version>1.17-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 4a040c1..0bf186b 100644 (file)
   </servlet>
 
         <servlet-mapping>
-                <servlet-name>JAXRSStaticRouting</servlet-name>
-                <url-pattern>/*</url-pattern>
+          <servlet-name>JAXRSStaticRouting</servlet-name>
+          <url-pattern>/*</url-pattern>
         </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index cad50e2..db4c4a9 100644 (file)
@@ -58,6 +58,7 @@
                 javax.xml.bind.annotation,
                 javax.xml.bind,
                 org.slf4j,
+                org.apache.catalina.filters,
                 !org.codehaus.enunciate.jaxrs
               </Import-Package>
             <Export-Package>
       <artifactId>enunciate-core-annotations</artifactId>
       <version>${enunciate.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index f152aa7..db0460b 100644 (file)
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index a3931be..43b8f9e 100644 (file)
@@ -65,6 +65,7 @@
               javax.xml.bind,
               javax.xml.bind.annotation,
               org.slf4j,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Export-Package>
       <artifactId>enunciate-core-annotations</artifactId>
       <version>${enunciate.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index f7eccef..a5c70ee 100644 (file)
     <servlet-name>JAXRSSubnets</servlet-name>
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index 556c964..dd7ff9a 100644 (file)
@@ -51,6 +51,7 @@
               javax.xml.bind.annotation,
               javax.xml.bind,
               org.slf4j,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Web-ContextPath>/controller/nb/v2/switch</Web-ContextPath>
       <artifactId>enunciate-core-annotations</artifactId>
       <version>${enunciate.version}</version>
     </dependency>
-
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 188b21b..ea6fcc9 100644 (file)
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index 726f797..007bdbe 100644 (file)
@@ -54,6 +54,7 @@
               javax.xml.bind,
               javax.xml.bind.annotation,
               org.slf4j,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Web-ContextPath>/controller/nb/v2/topology</Web-ContextPath>
       <artifactId>com.sun.jersey.jersey-servlet</artifactId>
       <version>1.17-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index a46e433..bc818c8 100644 (file)
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index dc23940..ed078a0 100644 (file)
@@ -51,6 +51,7 @@
               javax.xml.bind.annotation,
               javax.xml.bind,
               org.slf4j,
+              org.apache.catalina.filters,
               !org.codehaus.enunciate.jaxrs
             </Import-Package>
             <Web-ContextPath>/one/nb/v2/lb</Web-ContextPath>
       <artifactId>enunciate-core-annotations</artifactId>
       <version>${enunciate.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index aac4647..0e8f8c1 100644 (file)
     <url-pattern>/*</url-pattern>
   </servlet-mapping>
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>NB api</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+          <web-resource-collection>
+            <web-resource-name>NB api</web-resource-name>
+            <url-pattern>/*</url-pattern>
+            <http-method>POST</http-method>
+            <http-method>GET</http-method>
+            <http-method>PUT</http-method>
+            <http-method>PATCH</http-method>
+            <http-method>DELETE</http-method>
+            <http-method>HEAD</http-method>
+          </web-resource-collection>
+          <auth-constraint>
+            <role-name>System-Admin</role-name>
+            <role-name>Network-Admin</role-name>
+            <role-name>Network-Operator</role-name>
+            <role-name>Container-User</role-name>
+          </auth-constraint>
         </security-constraint>
 
         <security-role>
index ed899c5..809751d 100644 (file)
@@ -73,7 +73,8 @@
               org.springframework.web.servlet.view.json,
               org.springframework.web.filter,
               org.springframework.web.context,
-              org.springframework.util
+              org.springframework.util,
+              org.apache.catalina.filters
             </Import-Package>
             <Export-Package>
               org.opendaylight.controller.web
       <version>3.2.1.RELEASE</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller.thirdparty</groupId>
+      <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+      <version>7.0.42-SNAPSHOT</version>
+    </dependency>
   </dependencies>
 </project>
index 557b9c7..0c5cb3a 100644 (file)
@@ -6,28 +6,66 @@
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
 
+        <filter>
+          <filter-name>CorsFilter</filter-name>
+          <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+          <init-param>
+            <param-name>cors.allowed.origins</param-name>
+            <param-value>*</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.methods</param-name>
+            <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.allowed.headers</param-name>
+            <param-value>Content-Type,X-Requested-With,accept,authorization, origin,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.exposed.headers</param-name>
+            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.support.credentials</param-name>
+            <param-value>true</param-value>
+          </init-param>
+          <init-param>
+            <param-name>cors.preflight.maxage</param-name>
+            <param-value>10</param-value>
+          </init-param>
+        </filter>
+        <filter-mapping>
+          <filter-name>CorsFilter</filter-name>
+          <url-pattern>/*</url-pattern>
+        </filter-mapping>
+
         <security-constraint>
-                <web-resource-collection>
-                        <web-resource-name>free access</web-resource-name>
-                        <url-pattern>/js/*</url-pattern>
-                        <url-pattern>/images/*</url-pattern>
-                        <url-pattern>/css/*</url-pattern>
-                        <url-pattern>/favicon.ico</url-pattern>
-                </web-resource-collection>
+          <web-resource-collection>
+             <web-resource-name>free access</web-resource-name>
+             <url-pattern>/js/*</url-pattern>
+             <url-pattern>/images/*</url-pattern>
+             <url-pattern>/css/*</url-pattern>
+             <url-pattern>/favicon.ico</url-pattern>
+          </web-resource-collection>
         </security-constraint>
 
         <security-constraint>
-                <display-name>RootApp</display-name>
-                <web-resource-collection>
-                        <web-resource-name>RootGUI</web-resource-name>
-                        <url-pattern>/*</url-pattern>
-                </web-resource-collection>
-                <auth-constraint>
-                        <role-name>System-Admin</role-name>
-                        <role-name>Network-Admin</role-name>
-                        <role-name>Network-Operator</role-name>
-                        <role-name>Container-User</role-name>
-                </auth-constraint>
+           <display-name>RootApp</display-name>
+           <web-resource-collection>
+              <web-resource-name>RootGUI</web-resource-name>
+              <url-pattern>/*</url-pattern>
+              <http-method>POST</http-method>
+              <http-method>GET</http-method>
+              <http-method>PUT</http-method>
+              <http-method>DELETE</http-method>
+              <http-method>HEAD</http-method>
+           </web-resource-collection>
+           <auth-constraint>
+               <role-name>System-Admin</role-name>
+               <role-name>Network-Admin</role-name>
+               <role-name>Network-Operator</role-name>
+               <role-name>Container-User</role-name>
+           </auth-constraint>
         </security-constraint>
 
         <security-role>
diff --git a/third-party/org.apache.catalina.filters.CorsFilter/README b/third-party/org.apache.catalina.filters.CorsFilter/README
new file mode 100644 (file)
index 0000000..e2d22ab
--- /dev/null
@@ -0,0 +1,12 @@
+See: http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter
+And: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+This is done to allow a web page using javascript to be able to make calls
+to our REST APIs even though it does not originate in our domain.
+
+This bundle just rolls up org.apache.catalina.filters.CorsFilter and adds it as a 
+fragment to the org.apache.catalina bundle.
+
+The reason this is necessary is because the CorsFilter class was originally added
+at Tomcat 7.0.42, and we are using 7.0.32.  As the CorsFilter class is a simple one,
+with very few dependencies, this seemed the best way to bring it in.
+
diff --git a/third-party/org.apache.catalina.filters.CorsFilter/pom.xml b/third-party/org.apache.catalina.filters.CorsFilter/pom.xml
new file mode 100644 (file)
index 0000000..0789d50
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <!-- Get some common settings for the project we are using it in -->
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.thirdparty</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+    <relativePath>../commons/thirdparty</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.opendaylight.controller.thirdparty</groupId>
+  <artifactId>org.apache.catalina.filters.CorsFilter</artifactId>
+  <version>7.0.42-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.6</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Fragment-Host>
+              org.apache.catalina
+            </Fragment-Host>
+            <Import-Package>
+              javax.servlet,
+              javax.servlet.http,
+              org.apache.catalina.filters,
+              org.apache.juli.logging,
+              org.apache.tomcat.util.res,
+              org.apache.catalina.comet,
+              org.apache.tomcat.util
+            </Import-Package>
+          </instructions>
+          <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>equinoxSDK381</groupId>
+      <artifactId>javax.servlet</artifactId>
+      <version>3.0.0.v201112011016</version>
+    </dependency>
+    <dependency>
+      <groupId>orbit</groupId>
+      <artifactId>org.apache.juli.extras</artifactId>
+      <version>7.0.32.v201211081135</version>
+    </dependency>
+    <dependency>
+      <groupId>orbit</groupId>
+      <artifactId>org.apache.tomcat.util</artifactId>
+      <version>7.0.32.v201211201952</version>
+    </dependency>
+    <dependency>
+      <groupId>orbit</groupId>
+      <artifactId>org.apache.catalina</artifactId>
+      <version>7.0.32.v201211201336</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/third-party/org.apache.catalina.filters.CorsFilter/src/main/java/org/apache/catalina/filters/CorsFilter.java b/third-party/org.apache.catalina.filters.CorsFilter/src/main/java/org/apache/catalina/filters/CorsFilter.java
new file mode 100644 (file)
index 0000000..8069c99
--- /dev/null
@@ -0,0 +1,1166 @@
+/*
+ * 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.catalina.filters;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.filters.Constants;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * <p>
+ * A {@link Filter} that enable client-side cross-origin requests by
+ * implementing W3C's CORS (<b>C</b>ross-<b>O</b>rigin <b>R</b>esource
+ * <b>S</b>haring) specification for resources. Each {@link HttpServletRequest}
+ * request is inspected as per specification, and appropriate response headers
+ * are added to {@link HttpServletResponse}.
+ * </p>
+ *
+ * <p>
+ * By default, it also sets following request attributes, that help to
+ * determine the nature of the request downstream.
+ * <ul>
+ * <li><b>cors.isCorsRequest:</b> Flag to determine if the request is a CORS
+ * request. Set to <code>true</code> if a CORS request; <code>false</code>
+ * otherwise.</li>
+ * <li><b>cors.request.origin:</b> The Origin URL, i.e. the URL of the page from
+ * where the request is originated.</li>
+ * <li>
+ * <b>cors.request.type:</b> Type of request. Possible values:
+ * <ul>
+ * <li>SIMPLE: A request which is not preceded by a pre-flight request.</li>
+ * <li>ACTUAL: A request which is preceded by a pre-flight request.</li>
+ * <li>PRE_FLIGHT: A pre-flight request.</li>
+ * <li>NOT_CORS: A normal same-origin request.</li>
+ * <li>INVALID_CORS: A cross-origin request which is invalid.</li>
+ * </ul>
+ * </li>
+ * <li><b>cors.request.headers:</b> Request headers sent as
+ * 'Access-Control-Request-Headers' header, for pre-flight request.</li>
+ * </ul>
+ * </p>
+ *
+ * @see <a href="http://www.w3.org/TR/cors/">CORS specification</a>
+ *
+ */
+public final class CorsFilter implements Filter {
+
+    private static final Log log = LogFactory.getLog(CorsFilter.class);
+
+    private static final StringManager sm =
+            StringManager.getManager(Constants.Package);
+
+
+    /**
+     * A {@link Collection} of origins consisting of zero or more origins that
+     * are allowed access to the resource.
+     */
+    private final Collection<String> allowedOrigins;
+
+    /**
+     * Determines if any origin is allowed to make request.
+     */
+    private boolean anyOriginAllowed;
+
+    /**
+     * A {@link Collection} of methods consisting of zero or more methods that
+     * are supported by the resource.
+     */
+    private final Collection<String> allowedHttpMethods;
+
+    /**
+     * A {@link Collection} of headers consisting of zero or more header field
+     * names that are supported by the resource.
+     */
+    private final Collection<String> allowedHttpHeaders;
+
+    /**
+     * A {@link Collection} of exposed headers consisting of zero or more header
+     * field names of headers other than the simple response headers that the
+     * resource might use and can be exposed.
+     */
+    private final Collection<String> exposedHeaders;
+
+    /**
+     * A supports credentials flag that indicates whether the resource supports
+     * user credentials in the request. It is true when the resource does and
+     * false otherwise.
+     */
+    private boolean supportsCredentials;
+
+    /**
+     * Indicates (in seconds) how long the results of a pre-flight request can
+     * be cached in a pre-flight result cache.
+     */
+    private long preflightMaxAge;
+
+    /**
+     * Determines if the request should be decorated or not.
+     */
+    private boolean decorateRequest;
+
+
+    public CorsFilter() {
+        this.allowedOrigins = new HashSet<String>();
+        this.allowedHttpMethods = new HashSet<String>();
+        this.allowedHttpHeaders = new HashSet<String>();
+        this.exposedHeaders = new HashSet<String>();
+    }
+
+
+    @Override
+    public void doFilter(final ServletRequest servletRequest,
+            final ServletResponse servletResponse, final FilterChain filterChain)
+            throws IOException, ServletException {
+        if (!(servletRequest instanceof HttpServletRequest) ||
+                !(servletResponse instanceof HttpServletResponse)) {
+            throw new ServletException(sm.getString("corsFilter.onlyHttp"));
+        }
+
+        // Safe to downcast at this point.
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+        // Determines the CORS request type.
+        CorsFilter.CORSRequestType requestType = checkRequestType(request);
+
+        // Adds CORS specific attributes to request.
+        if (decorateRequest) {
+            CorsFilter.decorateCORSProperties(request, requestType);
+        }
+        switch (requestType) {
+        case SIMPLE:
+            // Handles a Simple CORS request.
+            this.handleSimpleCORS(request, response, filterChain);
+            break;
+        case ACTUAL:
+            // Handles an Actual CORS request.
+            this.handleSimpleCORS(request, response, filterChain);
+            break;
+        case PRE_FLIGHT:
+            // Handles a Pre-flight CORS request.
+            this.handlePreflightCORS(request, response, filterChain);
+            break;
+        case NOT_CORS:
+            // Handles a Normal request that is not a cross-origin request.
+            this.handleNonCORS(request, response, filterChain);
+            break;
+        default:
+            // Handles a CORS request that violates specification.
+            this.handleInvalidCORS(request, response, filterChain);
+            break;
+        }
+    }
+
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+        // Initialize defaults
+        parseAndStore(DEFAULT_ALLOWED_ORIGINS, DEFAULT_ALLOWED_HTTP_METHODS,
+                DEFAULT_ALLOWED_HTTP_HEADERS, DEFAULT_EXPOSED_HEADERS,
+                DEFAULT_SUPPORTS_CREDENTIALS, DEFAULT_PREFLIGHT_MAXAGE,
+                DEFAULT_DECORATE_REQUEST);
+
+        if (filterConfig != null) {
+            String configAllowedOrigins = filterConfig
+                    .getInitParameter(PARAM_CORS_ALLOWED_ORIGINS);
+            String configAllowedHttpMethods = filterConfig
+                    .getInitParameter(PARAM_CORS_ALLOWED_METHODS);
+            String configAllowedHttpHeaders = filterConfig
+                    .getInitParameter(PARAM_CORS_ALLOWED_HEADERS);
+            String configExposedHeaders = filterConfig
+                    .getInitParameter(PARAM_CORS_EXPOSED_HEADERS);
+            String configSupportsCredentials = filterConfig
+                    .getInitParameter(PARAM_CORS_SUPPORT_CREDENTIALS);
+            String configPreflightMaxAge = filterConfig
+                    .getInitParameter(PARAM_CORS_PREFLIGHT_MAXAGE);
+            String configDecorateRequest = filterConfig
+                    .getInitParameter(PARAM_CORS_REQUEST_DECORATE);
+
+            parseAndStore(configAllowedOrigins, configAllowedHttpMethods,
+                    configAllowedHttpHeaders, configExposedHeaders,
+                    configSupportsCredentials, configPreflightMaxAge,
+                    configDecorateRequest);
+        }
+    }
+
+
+    /**
+     * Handles a CORS request of type {@link CORSRequestType}.SIMPLE.
+     *
+     * @param request
+     *            The {@link HttpServletRequest} object.
+     * @param response
+     *            The {@link HttpServletResponse} object.
+     * @param filterChain
+     *            The {@link FilterChain} object.
+     * @throws IOException
+     * @throws ServletException
+     * @see <a href="http://www.w3.org/TR/cors/#resource-requests">Simple
+     *      Cross-Origin Request, Actual Request, and Redirects</a>
+     */
+    protected void handleSimpleCORS(final HttpServletRequest request,
+            final HttpServletResponse response, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        CorsFilter.CORSRequestType requestType = checkRequestType(request);
+        if (!(requestType == CorsFilter.CORSRequestType.SIMPLE ||
+                requestType == CorsFilter.CORSRequestType.ACTUAL)) {
+            throw new IllegalArgumentException(
+                    sm.getString("corsFilter.wrongType2",
+                            CorsFilter.CORSRequestType.SIMPLE,
+                            CorsFilter.CORSRequestType.ACTUAL));
+        }
+
+        final String origin = request
+                .getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
+        final String method = request.getMethod();
+
+        // Section 6.1.2
+        if (!isOriginAllowed(origin)) {
+            handleInvalidCORS(request, response, filterChain);
+            return;
+        }
+
+        if (!allowedHttpMethods.contains(method)) {
+            handleInvalidCORS(request, response, filterChain);
+            return;
+        }
+
+        // Section 6.1.3
+        // Add a single Access-Control-Allow-Origin header.
+        if (anyOriginAllowed && !supportsCredentials) {
+            // If resource doesn't support credentials and if any origin is
+            // allowed
+            // to make CORS request, return header with '*'.
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                    "*");
+        } else {
+            // If the resource supports credentials add a single
+            // Access-Control-Allow-Origin header, with the value of the Origin
+            // header as value.
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                    origin);
+        }
+
+        // Section 6.1.3
+        // If the resource supports credentials, add a single
+        // Access-Control-Allow-Credentials header with the case-sensitive
+        // string "true" as value.
+        if (supportsCredentials) {
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+                    "true");
+        }
+
+        // Section 6.1.4
+        // If the list of exposed headers is not empty add one or more
+        // Access-Control-Expose-Headers headers, with as values the header
+        // field names given in the list of exposed headers.
+        if ((exposedHeaders != null) && (exposedHeaders.size() > 0)) {
+            String exposedHeadersString = join(exposedHeaders, ",");
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
+                    exposedHeadersString);
+        }
+
+        // Forward the request down the filter chain.
+        filterChain.doFilter(request, response);
+    }
+
+
+    /**
+     * Handles CORS pre-flight request.
+     *
+     * @param request
+     *            The {@link HttpServletRequest} object.
+     * @param response
+     *            The {@link HttpServletResponse} object.
+     * @param filterChain
+     *            The {@link FilterChain} object.
+     * @throws IOException
+     * @throws ServletException
+     */
+    protected void handlePreflightCORS(final HttpServletRequest request,
+            final HttpServletResponse response, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        CORSRequestType requestType = checkRequestType(request);
+        if (requestType != CORSRequestType.PRE_FLIGHT) {
+            throw new IllegalArgumentException(
+                    sm.getString("corsFilter.wrongType1",
+                            CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
+        }
+
+        final String origin = request
+                .getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
+
+        // Section 6.2.2
+        if (!isOriginAllowed(origin)) {
+            handleInvalidCORS(request, response, filterChain);
+            return;
+        }
+
+        // Section 6.2.3
+        String accessControlRequestMethod = request.getHeader(
+                CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
+        if (accessControlRequestMethod == null ||
+                !HTTP_METHODS.contains(accessControlRequestMethod.trim())) {
+            handleInvalidCORS(request, response, filterChain);
+            return;
+        } else {
+            accessControlRequestMethod = accessControlRequestMethod.trim();
+        }
+
+        // Section 6.2.4
+        String accessControlRequestHeadersHeader = request.getHeader(
+                CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
+        List<String> accessControlRequestHeaders = new LinkedList<String>();
+        if (accessControlRequestHeadersHeader != null &&
+                !accessControlRequestHeadersHeader.trim().isEmpty()) {
+            String[] headers = accessControlRequestHeadersHeader.trim().split(
+                    ",");
+            for (String header : headers) {
+                accessControlRequestHeaders.add(header.trim().toLowerCase());
+            }
+        }
+
+        // Section 6.2.5
+        if (!allowedHttpMethods.contains(accessControlRequestMethod)) {
+            handleInvalidCORS(request, response, filterChain);
+            return;
+        }
+
+        // Section 6.2.6
+        if (!accessControlRequestHeaders.isEmpty()) {
+            for (String header : accessControlRequestHeaders) {
+                if (!allowedHttpHeaders.contains(header)) {
+                    handleInvalidCORS(request, response, filterChain);
+                    return;
+                }
+            }
+        }
+
+        // Section 6.2.7
+        if (supportsCredentials) {
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                    origin);
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
+                    "true");
+        } else {
+            if (anyOriginAllowed) {
+                response.addHeader(
+                        CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                        "*");
+            } else {
+                response.addHeader(
+                        CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                        origin);
+            }
+        }
+
+        // Section 6.2.8
+        if (preflightMaxAge > 0) {
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE,
+                    String.valueOf(preflightMaxAge));
+        }
+
+        // Section 6.2.9
+        response.addHeader(
+                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
+                accessControlRequestMethod);
+
+        // Section 6.2.10
+        if ((allowedHttpHeaders != null) && (!allowedHttpHeaders.isEmpty())) {
+            response.addHeader(
+                    CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
+                    join(allowedHttpHeaders, ","));
+        }
+
+        // Do not forward the request down the filter chain.
+    }
+
+
+    /**
+     * Handles a request, that's not a CORS request, but is a valid request i.e.
+     * it is not a cross-origin request. This implementation, just forwards the
+     * request down the filter chain.
+     *
+     * @param request
+     *            The {@link HttpServletRequest} object.
+     * @param response
+     *            The {@link HttpServletResponse} object.
+     * @param filterChain
+     *            The {@link FilterChain} object.
+     * @throws IOException
+     * @throws ServletException
+     */
+    private void handleNonCORS(final HttpServletRequest request,
+            final HttpServletResponse response, final FilterChain filterChain)
+            throws IOException, ServletException {
+        // Let request pass.
+        filterChain.doFilter(request, response);
+    }
+
+
+    /**
+     * Handles a CORS request that violates specification.
+     *
+     * @param request
+     *            The {@link HttpServletRequest} object.
+     * @param response
+     *            The {@link HttpServletResponse} object.
+     * @param filterChain
+     *            The {@link FilterChain} object.
+     */
+    private void handleInvalidCORS(final HttpServletRequest request,
+            final HttpServletResponse response, final FilterChain filterChain) {
+        String origin = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
+        String method = request.getMethod();
+        String accessControlRequestHeaders = request.getHeader(
+                REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
+
+        response.setContentType("text/plain");
+        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        response.resetBuffer();
+
+        if (log.isDebugEnabled()) {
+            // Debug so no need for i18n
+            StringBuilder message =
+                    new StringBuilder("Invalid CORS request; Origin=");
+            message.append(origin);
+            message.append(";Method=");
+            message.append(method);
+            if (accessControlRequestHeaders != null) {
+                message.append(";Access-Control-Request-Headers=");
+                message.append(accessControlRequestHeaders);
+            }
+            log.debug(message.toString());
+        }
+    }
+
+
+    @Override
+    public void destroy() {
+        // NOOP
+    }
+
+
+    /**
+     * Decorates the {@link HttpServletRequest}, with CORS attributes.
+     * <ul>
+     * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS
+     * request. Set to <code>true</code> if CORS request; <code>false</code>
+     * otherwise.</li>
+     * <li><b>cors.request.origin:</b> The Origin URL.</li>
+     * <li><b>cors.request.type:</b> Type of request. Values:
+     * <code>simple</code> or <code>preflight</code> or <code>not_cors</code> or
+     * <code>invalid_cors</code></li>
+     * <li><b>cors.request.headers:</b> Request headers sent as
+     * 'Access-Control-Request-Headers' header, for pre-flight request.</li>
+     * </ul>
+     *
+     * @param request
+     *            The {@link HttpServletRequest} object.
+     * @param corsRequestType
+     *            The {@link CORSRequestType} object.
+     */
+    protected static void decorateCORSProperties(
+            final HttpServletRequest request,
+            final CORSRequestType corsRequestType) {
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    sm.getString("corsFilter.nullRequest"));
+        }
+
+        if (corsRequestType == null) {
+            throw new IllegalArgumentException(
+                    sm.getString("corsFilter.nullRequestType"));
+        }
+
+        switch (corsRequestType) {
+        case SIMPLE:
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
+                    Boolean.TRUE);
+            request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
+                    request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
+                    corsRequestType.name().toLowerCase());
+            break;
+        case ACTUAL:
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
+                    Boolean.TRUE);
+            request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
+                    request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
+                    corsRequestType.name().toLowerCase());
+            break;
+        case PRE_FLIGHT:
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
+                    Boolean.TRUE);
+            request.setAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
+                    request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN));
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
+                    corsRequestType.name().toLowerCase());
+            String headers = request.getHeader(
+                    REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
+            if (headers == null) {
+                headers = "";
+            }
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS, headers);
+            break;
+        case NOT_CORS:
+            request.setAttribute(
+                    CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
+                    Boolean.FALSE);
+            break;
+        default:
+            // Don't set any attributes
+            break;
+        }
+    }
+
+
+    /**
+     * Joins elements of {@link Set} into a string, where each element is
+     * separated by the provided separator.
+     *
+     * @param elements
+     *            The {@link Set} containing elements to join together.
+     * @param joinSeparator
+     *            The character to be used for separating elements.
+     * @return The joined {@link String}; <code>null</code> if elements
+     *         {@link Set} is null.
+     */
+    protected static String join(final Collection<String> elements,
+            final String joinSeparator) {
+        String separator = ",";
+        if (elements == null) {
+            return null;
+        }
+        if (joinSeparator != null) {
+            separator = joinSeparator;
+        }
+        StringBuilder buffer = new StringBuilder();
+        boolean isFirst = true;
+        for (String element : elements) {
+            if (!isFirst) {
+                buffer.append(separator);
+            } else {
+                isFirst = false;
+            }
+
+            if (element != null) {
+                buffer.append(element);
+            }
+        }
+
+        return buffer.toString();
+    }
+
+
+    /**
+     * Determines the request type.
+     *
+     * @param request
+     */
+    protected CORSRequestType checkRequestType(final HttpServletRequest request) {
+        CORSRequestType requestType = CORSRequestType.INVALID_CORS;
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    sm.getString("corsFilter.nullRequest"));
+        }
+        String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN);
+        // Section 6.1.1 and Section 6.2.1
+        if (originHeader != null) {
+            if (originHeader.isEmpty()) {
+                requestType = CORSRequestType.INVALID_CORS;
+            } else if (!isValidOrigin(originHeader)) {
+                requestType = CORSRequestType.INVALID_CORS;
+            } else {
+                String method = request.getMethod();
+                if (method != null && HTTP_METHODS.contains(method)) {
+                    if ("OPTIONS".equals(method)) {
+                        String accessControlRequestMethodHeader =
+                                request.getHeader(
+                                        REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
+                        if (accessControlRequestMethodHeader != null &&
+                                !accessControlRequestMethodHeader.isEmpty()) {
+                            requestType = CORSRequestType.PRE_FLIGHT;
+                        } else if (accessControlRequestMethodHeader != null &&
+                                accessControlRequestMethodHeader.isEmpty()) {
+                            requestType = CORSRequestType.INVALID_CORS;
+                        } else {
+                            requestType = CORSRequestType.ACTUAL;
+                        }
+                    } else if ("GET".equals(method) || "HEAD".equals(method)) {
+                        requestType = CORSRequestType.SIMPLE;
+                    } else if ("POST".equals(method)) {
+                        String contentType = request.getContentType();
+                        if (contentType != null) {
+                            contentType = contentType.toLowerCase().trim();
+                            if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
+                                    .contains(contentType)) {
+                                requestType = CORSRequestType.SIMPLE;
+                            } else {
+                                requestType = CORSRequestType.ACTUAL;
+                            }
+                        }
+                    } else if (COMPLEX_HTTP_METHODS.contains(method)) {
+                        requestType = CORSRequestType.ACTUAL;
+                    }
+                }
+            }
+        } else {
+            requestType = CORSRequestType.NOT_CORS;
+        }
+
+        return requestType;
+    }
+
+
+    /**
+     * Checks if the Origin is allowed to make a CORS request.
+     *
+     * @param origin
+     *            The Origin.
+     * @return <code>true</code> if origin is allowed; <code>false</code>
+     *         otherwise.
+     */
+    private boolean isOriginAllowed(final String origin) {
+        if (anyOriginAllowed) {
+            return true;
+        }
+
+        // If 'Origin' header is a case-sensitive match of any of allowed
+        // origins, then return true, else return false.
+        return allowedOrigins.contains(origin);
+    }
+
+
+    /**
+     * Parses each param-value and populates configuration variables. If a param
+     * is provided, it overrides the default.
+     *
+     * @param allowedOrigins
+     *            A {@link String} of comma separated origins.
+     * @param allowedHttpMethods
+     *            A {@link String} of comma separated HTTP methods.
+     * @param allowedHttpHeaders
+     *            A {@link String} of comma separated HTTP headers.
+     * @param exposedHeaders
+     *            A {@link String} of comma separated headers that needs to be
+     *            exposed.
+     * @param supportsCredentials
+     *            "true" if support credentials needs to be enabled.
+     * @param preflightMaxAge
+     *            The amount of seconds the user agent is allowed to cache the
+     *            result of the pre-flight request.
+     * @throws ServletException
+     */
+    private void parseAndStore(final String allowedOrigins,
+            final String allowedHttpMethods, final String allowedHttpHeaders,
+            final String exposedHeaders, final String supportsCredentials,
+            final String preflightMaxAge, final String decorateRequest)
+                    throws ServletException {
+        if (allowedOrigins != null) {
+            if (allowedOrigins.trim().equals("*")) {
+                this.anyOriginAllowed = true;
+            } else {
+                this.anyOriginAllowed = false;
+                Set<String> setAllowedOrigins =
+                        parseStringToSet(allowedOrigins);
+                this.allowedOrigins.clear();
+                this.allowedOrigins.addAll(setAllowedOrigins);
+            }
+        }
+
+        if (allowedHttpMethods != null) {
+            Set<String> setAllowedHttpMethods =
+                    parseStringToSet(allowedHttpMethods);
+            this.allowedHttpMethods.clear();
+            this.allowedHttpMethods.addAll(setAllowedHttpMethods);
+        }
+
+        if (allowedHttpHeaders != null) {
+            Set<String> setAllowedHttpHeaders =
+                    parseStringToSet(allowedHttpHeaders);
+            Set<String> lowerCaseHeaders = new HashSet<String>();
+            for (String header : setAllowedHttpHeaders) {
+                String lowerCase = header.toLowerCase();
+                lowerCaseHeaders.add(lowerCase);
+            }
+            this.allowedHttpHeaders.clear();
+            this.allowedHttpHeaders.addAll(lowerCaseHeaders);
+        }
+
+        if (exposedHeaders != null) {
+            Set<String> setExposedHeaders = parseStringToSet(exposedHeaders);
+            this.exposedHeaders.clear();
+            this.exposedHeaders.addAll(setExposedHeaders);
+        }
+
+        if (supportsCredentials != null) {
+            // For any value other then 'true' this will be false.
+            this.supportsCredentials = Boolean
+                    .parseBoolean(supportsCredentials);
+        }
+
+        if (preflightMaxAge != null) {
+            try {
+                if (!preflightMaxAge.isEmpty()) {
+                    this.preflightMaxAge = Long.parseLong(preflightMaxAge);
+                } else {
+                    this.preflightMaxAge = 0L;
+                }
+            } catch (NumberFormatException e) {
+                throw new ServletException(
+                        sm.getString("corsFilter.invalidPreflightMaxAge"), e);
+            }
+        }
+
+        if (decorateRequest != null) {
+            // For any value other then 'true' this will be false.
+            this.decorateRequest = Boolean.parseBoolean(decorateRequest);
+        }
+    }
+
+    /**
+     * Takes a comma separated list and returns a Set<String>.
+     *
+     * @param data
+     *            A comma separated list of strings.
+     * @return Set<String>
+     */
+    private Set<String> parseStringToSet(final String data) {
+        String[] splits;
+
+        if (data != null && data.length() > 0) {
+            splits = data.split(",");
+        } else {
+            splits = new String[] {};
+        }
+
+        Set<String> set = new HashSet<String>();
+        if (splits.length > 0) {
+            for (String split : splits) {
+                set.add(split.trim());
+            }
+        }
+
+        return set;
+    }
+
+
+    /**
+     * Checks if a given origin is valid or not. Criteria:
+     * <ul>
+     * <li>If an encoded character is present in origin, it's not valid.</li>
+     * <li>Origin should be a valid {@link URI}</li>
+     * </ul>
+     *
+     * @param origin
+     * @see <a href="http://tools.ietf.org/html/rfc952">RFC952</a>
+     */
+    protected static boolean isValidOrigin(String origin) {
+        // Checks for encoded characters. Helps prevent CRLF injection.
+        if (origin.contains("%")) {
+            return false;
+        }
+
+        URI originURI;
+
+        try {
+            originURI = new URI(origin);
+        } catch (URISyntaxException e) {
+            return false;
+        }
+        // If scheme for URI is null, return false. Return true otherwise.
+        return originURI.getScheme() != null;
+
+    }
+
+
+    /**
+     * Determines if any origin is allowed to make CORS request.
+     *
+     * @return <code>true</code> if it's enabled; false otherwise.
+     */
+    public boolean isAnyOriginAllowed() {
+        return anyOriginAllowed;
+    }
+
+
+    /**
+     * Returns a {@link Set} of headers that should be exposed by browser.
+     */
+    public Collection<String> getExposedHeaders() {
+        return exposedHeaders;
+    }
+
+
+    /**
+     * Determines is supports credentials is enabled.
+     */
+    public boolean isSupportsCredentials() {
+        return supportsCredentials;
+    }
+
+
+    /**
+     * Returns the preflight response cache time in seconds.
+     *
+     * @return Time to cache in seconds.
+     */
+    public long getPreflightMaxAge() {
+        return preflightMaxAge;
+    }
+
+
+    /**
+     * Returns the {@link Set} of allowed origins that are allowed to make
+     * requests.
+     *
+     * @return {@link Set}
+     */
+    public Collection<String> getAllowedOrigins() {
+        return allowedOrigins;
+    }
+
+
+    /**
+     * Returns a {@link Set} of HTTP methods that are allowed to make requests.
+     *
+     * @return {@link Set}
+     */
+    public Collection<String> getAllowedHttpMethods() {
+        return allowedHttpMethods;
+    }
+
+
+    /**
+     * Returns a {@link Set} of headers support by resource.
+     *
+     * @return {@link Set}
+     */
+    public Collection<String> getAllowedHttpHeaders() {
+        return allowedHttpHeaders;
+    }
+
+
+    // -------------------------------------------------- CORS Response Headers
+    /**
+     * The Access-Control-Allow-Origin header indicates whether a resource can
+     * be shared based by returning the value of the Origin request header in
+     * the response.
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN =
+            "Access-Control-Allow-Origin";
+
+    /**
+     * The Access-Control-Allow-Credentials header indicates whether the
+     * response to request can be exposed when the omit credentials flag is
+     * unset. When part of the response to a preflight request it indicates that
+     * the actual request can include user credentials.
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS =
+            "Access-Control-Allow-Credentials";
+
+    /**
+     * The Access-Control-Expose-Headers header indicates which headers are safe
+     * to expose to the API of a CORS API specification
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS =
+            "Access-Control-Expose-Headers";
+
+    /**
+     * The Access-Control-Max-Age header indicates how long the results of a
+     * preflight request can be cached in a preflight result cache.
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE =
+            "Access-Control-Max-Age";
+
+    /**
+     * The Access-Control-Allow-Methods header indicates, as part of the
+     * response to a preflight request, which methods can be used during the
+     * actual request.
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS =
+            "Access-Control-Allow-Methods";
+
+    /**
+     * The Access-Control-Allow-Headers header indicates, as part of the
+     * response to a preflight request, which header field names can be used
+     * during the actual request.
+     */
+    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS =
+            "Access-Control-Allow-Headers";
+
+    // -------------------------------------------------- CORS Request Headers
+    /**
+     * The Origin header indicates where the cross-origin request or preflight
+     * request originates from.
+     */
+    public static final String REQUEST_HEADER_ORIGIN = "Origin";
+
+    /**
+     * The Access-Control-Request-Method header indicates which method will be
+     * used in the actual request as part of the preflight request.
+     */
+    public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD =
+            "Access-Control-Request-Method";
+
+    /**
+     * The Access-Control-Request-Headers header indicates which headers will be
+     * used in the actual request as part of the preflight request.
+     */
+    public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS =
+            "Access-Control-Request-Headers";
+
+    // ----------------------------------------------------- Request attributes
+    /**
+     * The prefix to a CORS request attribute.
+     */
+    public static final String HTTP_REQUEST_ATTRIBUTE_PREFIX = "cors.";
+
+    /**
+     * Attribute that contains the origin of the request.
+     */
+    public static final String HTTP_REQUEST_ATTRIBUTE_ORIGIN =
+            HTTP_REQUEST_ATTRIBUTE_PREFIX + "request.origin";
+
+    /**
+     * Boolean value, suggesting if the request is a CORS request or not.
+     */
+    public static final String HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST =
+            HTTP_REQUEST_ATTRIBUTE_PREFIX + "isCorsRequest";
+
+    /**
+     * Type of CORS request, of type {@link CORSRequestType}.
+     */
+    public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE =
+            HTTP_REQUEST_ATTRIBUTE_PREFIX + "request.type";
+
+    /**
+     * Request headers sent as 'Access-Control-Request-Headers' header, for
+     * pre-flight request.
+     */
+    public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS =
+            HTTP_REQUEST_ATTRIBUTE_PREFIX + "request.headers";
+
+    // -------------------------------------------------------------- Constants
+    /**
+     * Enumerates varies types of CORS requests. Also, provides utility methods
+     * to determine the request type.
+     */
+    protected static enum CORSRequestType {
+        /**
+         * A simple HTTP request, i.e. it shouldn't be pre-flighted.
+         */
+        SIMPLE,
+        /**
+         * A HTTP request that needs to be pre-flighted.
+         */
+        ACTUAL,
+        /**
+         * A pre-flight CORS request, to get meta information, before a
+         * non-simple HTTP request is sent.
+         */
+        PRE_FLIGHT,
+        /**
+         * Not a CORS request, but a normal request.
+         */
+        NOT_CORS,
+        /**
+         * An invalid CORS request, i.e. it qualifies to be a CORS request, but
+         * fails to be a valid one.
+         */
+        INVALID_CORS
+    }
+
+    /**
+     * {@link Collection} of HTTP methods. Case sensitive.
+     *
+     * @see  <a href="http://tools.ietf.org/html/rfc2616#section-5.1.1"
+     *       >http://tools.ietf.org/html/rfc2616#section-5.1.1</a>
+     *
+     */
+    public static final Collection<String> HTTP_METHODS =
+            new HashSet<String>(Arrays.asList("OPTIONS", "GET", "HEAD", "POST",
+                    "PUT", "DELETE", "TRACE", "CONNECT"));
+    /**
+     * {@link Collection} of non-simple HTTP methods. Case sensitive.
+     */
+    public static final Collection<String> COMPLEX_HTTP_METHODS =
+            new HashSet<String>(Arrays.asList("PUT", "DELETE", "TRACE",
+                    "CONNECT"));
+    /**
+     * {@link Collection} of Simple HTTP methods. Case sensitive.
+     *
+     * @see  <a href="http://www.w3.org/TR/cors/#terminology"
+     *       >http://www.w3.org/TR/cors/#terminology</a>
+     */
+    public static final Collection<String> SIMPLE_HTTP_METHODS =
+            new HashSet<String>(Arrays.asList("GET", "POST", "HEAD"));
+
+    /**
+     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
+     *
+     * @see  <a href="http://www.w3.org/TR/cors/#terminology"
+     *       >http://www.w3.org/TR/cors/#terminology</a>
+     */
+    public static final Collection<String> SIMPLE_HTTP_REQUEST_HEADERS =
+            new HashSet<String>(Arrays.asList("Accept", "Accept-Language",
+                    "Content-Language"));
+
+    /**
+     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
+     *
+     * @see  <a href="http://www.w3.org/TR/cors/#terminology"
+     *       >http://www.w3.org/TR/cors/#terminology</a>
+     */
+    public static final Collection<String> SIMPLE_HTTP_RESPONSE_HEADERS =
+            new HashSet<String>(Arrays.asList("Cache-Control",
+                    "Content-Language", "Content-Type", "Expires",
+                    "Last-Modified", "Pragma"));
+
+    /**
+     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
+     *
+     * @see  <a href="http://www.w3.org/TR/cors/#terminology"
+     *       >http://www.w3.org/TR/cors/#terminology</a>
+     */
+    public static final Collection<String> SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES =
+            new HashSet<String>(Arrays.asList(
+                    "application/x-www-form-urlencoded",
+                    "multipart/form-data", "text/plain"));
+
+    // ------------------------------------------------ Configuration Defaults
+    /**
+     * By default, all origins are allowed to make requests.
+     */
+    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
+
+    /**
+     * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
+     */
+    public static final String DEFAULT_ALLOWED_HTTP_METHODS =
+            "GET,POST,HEAD,OPTIONS";
+
+    /**
+     * By default, time duration to cache pre-flight response is 30 mins.
+     */
+    public static final String DEFAULT_PREFLIGHT_MAXAGE = "1800";
+
+    /**
+     * By default, support credentials is turned on.
+     */
+    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
+
+    /**
+     * By default, following headers are supported:
+     * Origin,Accept,X-Requested-With, Content-Type,
+     * Access-Control-Request-Method, and Access-Control-Request-Headers.
+     */
+    public static final String DEFAULT_ALLOWED_HTTP_HEADERS =
+            "Origin,Accept,X-Requested-With,Content-Type," +
+            "Access-Control-Request-Method,Access-Control-Request-Headers";
+
+    /**
+     * By default, none of the headers are exposed in response.
+     */
+    public static final String DEFAULT_EXPOSED_HEADERS = "";
+
+    /**
+     * By default, request is decorated with CORS attributes.
+     */
+    public static final String DEFAULT_DECORATE_REQUEST = "true";
+
+    // ----------------------------------------Filter Config Init param-name(s)
+    /**
+     * Key to retrieve allowed origins from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_ALLOWED_ORIGINS =
+            "cors.allowed.origins";
+
+    /**
+     * Key to retrieve support credentials from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_SUPPORT_CREDENTIALS =
+            "cors.support.credentials";
+
+    /**
+     * Key to retrieve exposed headers from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_EXPOSED_HEADERS =
+            "cors.exposed.headers";
+
+    /**
+     * Key to retrieve allowed headers from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_ALLOWED_HEADERS =
+            "cors.allowed.headers";
+
+    /**
+     * Key to retrieve allowed methods from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_ALLOWED_METHODS =
+            "cors.allowed.methods";
+
+    /**
+     * Key to retrieve preflight max age from {@link FilterConfig}.
+     */
+    public static final String PARAM_CORS_PREFLIGHT_MAXAGE =
+            "cors.preflight.maxage";
+
+    /**
+     * Key to determine if request should be decorated.
+     */
+    public static final String PARAM_CORS_REQUEST_DECORATE =
+            "cors.request.decorate";
+}