001package ca.uhn.fhir.rest.server.interceptor; 002 003/* 004 * #%L 005 * HAPI FHIR - Server Framework 006 * %% 007 * Copyright (C) 2014 - 2019 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Arrays; 026 027import javax.servlet.http.HttpServletRequest; 028import javax.servlet.http.HttpServletResponse; 029 030import ca.uhn.fhir.rest.api.Constants; 031import org.apache.commons.lang3.Validate; 032import org.springframework.web.cors.CorsConfiguration; 033import org.springframework.web.cors.CorsProcessor; 034import org.springframework.web.cors.CorsUtils; 035import org.springframework.web.cors.DefaultCorsProcessor; 036 037import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 038 039public class CorsInterceptor extends InterceptorAdapter { 040 041 private CorsProcessor myCorsProcessor; 042 private CorsConfiguration myConfig; 043 044 /** 045 * Constructor which creates an interceptor with default CORS configuration for use in 046 * a FHIR server. This includes: 047 * <ul> 048 * <li>Allowed Origin: *</li> 049 * <li>Allowed Header: Accept</li> 050 * <li>Allowed Header: Access-Control-Request-Headers</li> 051 * <li>Allowed Header: Access-Control-Request-Method</li> 052 * <li>Allowed Header: Cache-Control</li> 053 * <li>Exposed Header: Content-Location</li> 054 * <li>Allowed Header: Content-Type</li> 055 * <li>Exposed Header: Location</li> 056 * <li>Allowed Header: Origin</li> 057 * <li>Allowed Header: Prefer</li> 058 * <li>Allowed Header: X-Requested-With</li> 059 * </ul> 060 * Note that this configuration is useful for quickly getting CORS working, but 061 * in a real production system you probably want to consider whether it is 062 * appropriate for your situation. In particular, using "Allowed Origin: *" 063 * isn't always the right thing to do. 064 */ 065 public CorsInterceptor() { 066 this(createDefaultCorsConfig()); 067 } 068 069 /** 070 * Constructor which accepts the given configuration 071 * 072 * @param theConfiguration 073 * The CORS configuration 074 */ 075 public CorsInterceptor(CorsConfiguration theConfiguration) { 076 Validate.notNull(theConfiguration, "theConfiguration must not be null"); 077 myCorsProcessor = new DefaultCorsProcessor(); 078 setConfig(theConfiguration); 079 } 080 081 /** 082 * Gets the CORS configuration 083 */ 084 public CorsConfiguration getConfig() { 085 return myConfig; 086 } 087 088 /** 089 * Sets the CORS configuration 090 */ 091 public void setConfig(CorsConfiguration theConfiguration) { 092 myConfig = theConfiguration; 093 } 094 095 @Override 096 public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { 097 if (CorsUtils.isCorsRequest(theRequest)) { 098 boolean isValid; 099 try { 100 isValid = myCorsProcessor.processRequest(myConfig, theRequest, theResponse); 101 } catch (IOException e) { 102 throw new InternalErrorException(e); 103 } 104 if (!isValid || CorsUtils.isPreFlightRequest(theRequest)) { 105 return false; 106 } 107 } 108 109 return super.incomingRequestPreProcessed(theRequest, theResponse); 110 } 111 112 private static CorsConfiguration createDefaultCorsConfig() { 113 CorsConfiguration retVal = new CorsConfiguration(); 114 115 retVal.setAllowedHeaders(new ArrayList<>(Constants.CORS_ALLOWED_HEADERS)); 116 retVal.setAllowedMethods(new ArrayList<>(Constants.CORS_ALLWED_METHODS)); 117 118 retVal.addExposedHeader("Content-Location"); 119 retVal.addExposedHeader("Location"); 120 121 retVal.addAllowedOrigin("*"); 122 123 124 return retVal; 125 } 126 127}