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