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}