/* * Copyright (c) 2015 the original author or authors * * Licensed 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 io.werval.filters; import io.werval.api.Config; import io.werval.api.outcomes.Outcome; import io.werval.runtime.routes.RoutesParserProvider; import io.werval.test.WervalHttpRule; import org.junit.ClassRule; import org.junit.Test; import static com.jayway.restassured.RestAssured.expect; import static com.jayway.restassured.RestAssured.given; import static io.werval.api.context.CurrentContext.outcomes; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_ALLOW_CREDENTIALS; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_ALLOW_HEADERS; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_ALLOW_METHODS; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_ALLOW_ORIGIN; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_EXPOSE_HEADERS; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_REQUEST_HEADERS; import static io.werval.api.http.Headers.Names.ACCESS_CONTROL_REQUEST_METHOD; import static io.werval.api.http.Headers.Names.ORIGIN; import static io.werval.api.http.Status.BAD_REQUEST_CODE; import static io.werval.api.http.Status.NO_CONTENT_CODE; import static io.werval.api.http.Status.OK_CODE; import static io.werval.api.http.Status.UNAUTHORIZED_CODE; import static io.werval.util.Strings.join; import static org.hamcrest.Matchers.nullValue; /** * CORS Test. */ public class CORSTest { @ClassRule public static final WervalHttpRule WERVAL = new WervalHttpRule( new RoutesParserProvider( "OPTIONS /custom CORS.preflight( " + " String origin = 'http://example.com'," + " String methods = 'GET'," + " String headers = 'User-Agent'," + " Boolean creds = true " + ")\n" + "OPTIONS /*path CORS.preflight( String path )\n" + "GET /global io.werval.filters.CORSTest$Controller.global\n" + "GET /custom io.werval.filters.CORSTest$Controller.custom\n" ) ); public static class Controller { @CORS public Outcome global() { return outcomes().ok().build(); } @CORS( allowOrigin = "http://example.com", allowCredentials = true, exposeHeaders = "X-Custom-Exposed-Header" ) public Outcome custom() { return outcomes().ok().build(); } } @Test public void global() { Config config = WERVAL.application().config(); String allowOrigin = join( config.stringList( "werval.controllers.cors.allow_origin" ), ", " ); String allowMethods = join( config.stringList( "werval.controllers.cors.allow_methods" ), ", " ); String allowHeaders = join( config.stringList( "werval.controllers.cors.allow_headers" ), ", " ); String exposeHeaders = join( config.stringList( "werval.filters.cors.expose_headers" ), ", " ); boolean allowCredentials = config.bool( "werval.controllers.cors.allow_credentials" ) && !"*".equals( allowOrigin ); given() .header( ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_REQUEST_METHOD, "PUT" ) .expect() .statusCode( NO_CONTENT_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin ) .header( ACCESS_CONTROL_ALLOW_METHODS, allowMethods ) .header( ACCESS_CONTROL_ALLOW_HEADERS, allowHeaders ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, String.valueOf( allowCredentials ) ) .when() .options( "/global" ); given() .header( ORIGIN, "http://example.com" ) .expect() .statusCode( OK_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, String.valueOf( allowCredentials ) ) .header( ACCESS_CONTROL_EXPOSE_HEADERS, exposeHeaders ) .when() .get( "/global" ); } @Test public void custom() { given() .header( ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_REQUEST_METHOD, "GET" ) .expect() .statusCode( NO_CONTENT_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_ALLOW_METHODS, "GET" ) .header( ACCESS_CONTROL_ALLOW_HEADERS, "User-Agent" ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, "true" ) .when() .options( "/custom" ); given() .header( ORIGIN, "http://example.com" ) .expect() .statusCode( OK_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, "true" ) .header( ACCESS_CONTROL_EXPOSE_HEADERS, "X-Custom-Exposed-Header" ) .when() .get( "/custom" ); } @Test public void preflightMissingCorsHeaders() { expect() .statusCode( BAD_REQUEST_CODE ) .when() .options( "/global" ); given() .header( ORIGIN, "http://example.com" ) .expect() .statusCode( BAD_REQUEST_CODE ) .when() .options( "/global" ); given() .header( ACCESS_CONTROL_REQUEST_METHOD, "GET" ) .expect() .statusCode( BAD_REQUEST_CODE ) .when() .options( "/global" ); } @Test public void preflightUnauthorizedOrigin() { given() .header( ORIGIN, "http://example.org" ) .header( ACCESS_CONTROL_REQUEST_METHOD, "GET" ) .expect() .statusCode( UNAUTHORIZED_CODE ) .when() .options( "/custom" ); } @Test public void preflightUnauthorizedMethod() { given() .header( ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_REQUEST_METHOD, "PATCH" ) .expect() .statusCode( UNAUTHORIZED_CODE ) .when() .options( "/custom" ); } @Test public void preflightUnauthorizedHeader() { given() .header( ORIGIN, "http://example.com" ) .header( ACCESS_CONTROL_REQUEST_METHOD, "GET" ) .header( ACCESS_CONTROL_REQUEST_HEADERS, "Any-Header" ) .expect() .statusCode( UNAUTHORIZED_CODE ) .when() .options( "/custom" ); } @Test public void requestNonCors() { expect() .statusCode( OK_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, nullValue() ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, nullValue() ) .header( ACCESS_CONTROL_EXPOSE_HEADERS, nullValue() ) .when() .get( "/global" ); expect() .statusCode( OK_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, nullValue() ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, nullValue() ) .header( ACCESS_CONTROL_EXPOSE_HEADERS, nullValue() ) .when() .get( "/custom" ); } @Test public void requestUnauthorizedOrigin() { given() .header( ORIGIN, "http://example.org" ) .expect() .statusCode( OK_CODE ) .header( ACCESS_CONTROL_ALLOW_ORIGIN, nullValue() ) .header( ACCESS_CONTROL_ALLOW_CREDENTIALS, nullValue() ) .header( ACCESS_CONTROL_EXPOSE_HEADERS, nullValue() ) .when() .get( "/custom" ); } }