001package ca.uhn.fhir.rest.server.interceptor; 002 003/* 004 * #%L 005 * HAPI FHIR - Server Framework 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 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 ca.uhn.fhir.i18n.Msg; 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.Arrays; 027 028import javax.servlet.http.HttpServletRequest; 029import javax.servlet.http.HttpServletResponse; 030 031import ca.uhn.fhir.rest.api.Constants; 032import org.apache.commons.lang3.Validate; 033import org.springframework.web.cors.CorsConfiguration; 034import org.springframework.web.cors.CorsProcessor; 035import org.springframework.web.cors.CorsUtils; 036import org.springframework.web.cors.DefaultCorsProcessor; 037 038import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 039 040public class CorsInterceptor extends InterceptorAdapter { 041 042 private CorsProcessor myCorsProcessor; 043 private CorsConfiguration myConfig; 044 045 /** 046 * Constructor which creates an interceptor with default CORS configuration for use in 047 * a FHIR server. This includes: 048 * <ul> 049 * <li>Allowed Origin: *</li> 050 * <li>Allowed Header: Accept</li> 051 * <li>Allowed Header: Access-Control-Request-Headers</li> 052 * <li>Allowed Header: Access-Control-Request-Method</li> 053 * <li>Allowed Header: Cache-Control</li> 054 * <li>Exposed Header: Content-Location</li> 055 * <li>Allowed Header: Content-Type</li> 056 * <li>Exposed Header: Location</li> 057 * <li>Allowed Header: Origin</li> 058 * <li>Allowed Header: Prefer</li> 059 * <li>Allowed Header: X-Requested-With</li> 060 * </ul> 061 * Note that this configuration is useful for quickly getting CORS working, but 062 * in a real production system you probably want to consider whether it is 063 * appropriate for your situation. In particular, using "Allowed Origin: *" 064 * isn't always the right thing to do. 065 */ 066 public CorsInterceptor() { 067 this(createDefaultCorsConfig()); 068 } 069 070 /** 071 * Constructor which accepts the given configuration 072 * 073 * @param theConfiguration 074 * The CORS configuration 075 */ 076 public CorsInterceptor(CorsConfiguration theConfiguration) { 077 Validate.notNull(theConfiguration, "theConfiguration must not be null"); 078 myCorsProcessor = new DefaultCorsProcessor(); 079 setConfig(theConfiguration); 080 } 081 082 /** 083 * Gets the CORS configuration 084 */ 085 public CorsConfiguration getConfig() { 086 return myConfig; 087 } 088 089 /** 090 * Sets the CORS configuration 091 */ 092 public void setConfig(CorsConfiguration theConfiguration) { 093 myConfig = theConfiguration; 094 } 095 096 @Override 097 public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { 098 if (CorsUtils.isCorsRequest(theRequest)) { 099 boolean isValid; 100 try { 101 isValid = myCorsProcessor.processRequest(myConfig, theRequest, theResponse); 102 } catch (IOException e) { 103 throw new InternalErrorException(Msg.code(326) + e); 104 } 105 if (!isValid || CorsUtils.isPreFlightRequest(theRequest)) { 106 return false; 107 } 108 } 109 110 return super.incomingRequestPreProcessed(theRequest, theResponse); 111 } 112 113 private static CorsConfiguration createDefaultCorsConfig() { 114 CorsConfiguration retVal = new CorsConfiguration(); 115 116 retVal.setAllowedHeaders(new ArrayList<>(Constants.CORS_ALLOWED_HEADERS)); 117 retVal.setAllowedMethods(new ArrayList<>(Constants.CORS_ALLWED_METHODS)); 118 119 retVal.addExposedHeader("Content-Location"); 120 retVal.addExposedHeader("Location"); 121 122 retVal.addAllowedOrigin("*"); 123 124 125 return retVal; 126 } 127 128}