import java.net.URI; import javax.ws.rs.WebApplicationException; import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.atlassian.gadgets.opensocial.spi.Whitelist; import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerRequestFilter; import com.sun.jersey.spi.container.ContainerResponse; import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; @PreMatching public class CorsFilter implements ResourceFilter, ContainerRequestFilter, ContainerResponseFilter { private static Logger logger = LoggerFactory.getLogger(CorsFilter.class); private final Whitelist whiteList; public CorsFilter(Whitelist whiteList) { this.whiteList = whiteList; } @Override public ContainerRequestFilter getRequestFilter() { return this; } @Override public ContainerResponseFilter getResponseFilter() { return this; } @Override public ContainerRequest filter(ContainerRequest request) { // Issue 1 - this filter is not getting called in case of preflight request // In case of preflight request no further processing is required so break the // request. logger.debug("CorsFilter:request - check if preflight request"); if (isPreflightRequest(request)) { logger.debug( "CorsFilter:request - throw exception with response ok to return the request if its OPTIONS request"); throw new WebApplicationException(Response.ok().build()); } return request; } @Override public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { // if there is no Origin header, then it is not a // cross origin request. We don't do anything. String origin = request.getHeaderValue("Origin"); logger.debug("CorsFilter:response check if preflight request for origin {}", origin); if (origin == null) { return response; } // If it is a preflight request, then we add all // the CORS headers here. if (isPreflightRequest(request)) { logger.debug("CorsFilter:response - preflight request validate origin now"); checkOrigin(origin); response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true"); response.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); response.getHttpHeaders().add("Access-Control-Allow-Headers", "Authorization, X-Atlassian-Token, Content-Type"); } else { logger.debug("isPreflightRequest returned false, origin = {}, method = {}", request.getHeaderValue("Origin"), request.getMethod()); } // Cross origin requests can be either simple requests // or preflight request. We need to add this header // to both type of requests. Only preflight requests // need the previously added headers. response.getHttpHeaders().add("Access-Control-Allow-Origin", "*"); return response; } private boolean isPreflightRequest(ContainerRequest request) { // Issue 2 - issue here is, even if its "OPTIONS" request method, we get "GET" // method here return request.getHeaderValue("Origin") != null && request.getMethod().equalsIgnoreCase("OPTIONS"); } private void checkOrigin(String origin) { if (!whiteList.allows(URI.create(origin))) { logger.error("Origin {} is *not* allowed to make requests.", origin); // throw exception with some error message throw new WebApplicationException("Origin " + origin + " is not allowed to make requests.", Response.Status.FORBIDDEN); } logger.info("Origin {} is allowed to make requests.", origin); } }