001package ca.uhn.fhir.test.utilities;
002
003/*-
004 * #%L
005 * HAPI FHIR Test Utilities
006 * %%
007 * Copyright (C) 2014 - 2023 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.context.FhirContext;
024import ca.uhn.fhir.i18n.Msg;
025import ca.uhn.fhir.rest.client.api.IGenericClient;
026import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
027import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
028import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
029import ca.uhn.fhir.rest.server.IResourceProvider;
030import ca.uhn.fhir.rest.server.IServerAddressStrategy;
031import ca.uhn.fhir.tls.KeyStoreType;
032import org.eclipse.jetty.http.HttpVersion;
033import org.eclipse.jetty.server.HttpConfiguration;
034import org.eclipse.jetty.server.HttpConnectionFactory;
035import org.eclipse.jetty.server.Server;
036import org.eclipse.jetty.server.ServerConnector;
037import org.eclipse.jetty.server.SslConnectionFactory;
038import org.eclipse.jetty.servlet.ServletContextHandler;
039import org.eclipse.jetty.servlet.ServletHolder;
040import org.eclipse.jetty.util.ssl.SslContextFactory;
041import org.hl7.fhir.instance.model.api.IBaseResource;
042import org.hl7.fhir.instance.model.api.IIdType;
043import org.junit.jupiter.api.extension.RegisterExtension;
044
045import javax.servlet.Servlet;
046import java.security.KeyStore;
047import java.util.List;
048
049public abstract class BaseRestServerHelper {
050
051        private final String SERVER_KEYSTORE_PATH = "/tls/server-keystore.p12";
052        private final String SERVER_TRUSTSTORE_PATH = "/tls/server-truststore.p12";
053        private final String PASSWORD = "changeit";
054
055        protected final FhirContext myFhirContext;
056        protected int myListenerPort;
057        protected int myHttpsListenerPort;
058        protected Server myListenerServer;
059        protected String myBase;
060        protected String mySecureBase;
061        protected IGenericClient myClient;
062
063        @RegisterExtension
064        public TlsAuthenticationTestHelper myTlsAuthenticationTestHelper = new TlsAuthenticationTestHelper();
065
066        public BaseRestServerHelper(FhirContext theFhirContext) {
067                myFhirContext = theFhirContext;
068        }
069
070        protected void afterEach() throws Exception {
071                stop();
072        }
073
074        public IGenericClient getClient() {
075                return myClient;
076        }
077
078        protected void startServer(Servlet theServlet) throws Exception {
079                myListenerServer = new Server(0);
080                
081                myFhirContext.getRestfulClientFactory().setSocketTimeout(120000);
082
083                ServletContextHandler proxyHandler = new ServletContextHandler();
084                proxyHandler.setContextPath("/");
085
086                ServletHolder targetServletHolder = new ServletHolder();
087                targetServletHolder.setServlet(theServlet);
088                proxyHandler.addServlet(targetServletHolder, "/target/*");
089
090                myListenerServer.setHandler(proxyHandler);
091
092                SslContextFactory.Server sslContextFactory = getSslContextFactory();
093
094                HttpConfiguration httpsConfig = new HttpConfiguration();
095                httpsConfig.setSecureScheme("https");
096                httpsConfig.setSecurePort(0);
097
098                ServerConnector sslConnector = new ServerConnector(myListenerServer,
099                        new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
100                        new HttpConnectionFactory(httpsConfig));
101                sslConnector.setPort(0);
102
103                myListenerServer.addConnector(sslConnector);
104                myListenerServer.start();
105
106                assignHttpAndHttpsPorts();
107
108                myBase = "http://localhost:" + myListenerPort + "/target";
109                mySecureBase = "https://localhost:" + myHttpsListenerPort + "/target";
110
111                myFhirContext.getRestfulClientFactory().setConnectTimeout(60000);
112                myFhirContext.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
113
114                myClient = myFhirContext.newRestfulGenericClient(myBase);
115                myClient.registerInterceptor(new LoggingInterceptor(false));
116        }
117
118        private void assignHttpAndHttpsPorts() {
119                myListenerPort = ((ServerConnector)myListenerServer.getConnectors()[0]).getLocalPort();
120                myHttpsListenerPort = ((ServerConnector)myListenerServer.getConnectors()[1]).getLocalPort();
121        }
122
123        private SslContextFactory.Server getSslContextFactory() throws Exception{
124                try {
125                        SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
126
127                        KeyStore keyStore = KeyStore.getInstance(KeyStoreType.PKCS12.toString());
128                        keyStore.load(BaseRestServerHelper.class.getResourceAsStream(SERVER_KEYSTORE_PATH), PASSWORD.toCharArray());
129                        sslContextFactory.setKeyStore(keyStore);
130                        sslContextFactory.setKeyStorePassword(PASSWORD);
131
132                        KeyStore trustStore = KeyStore.getInstance(KeyStoreType.PKCS12.toString());
133                        trustStore.load(BaseRestServerHelper.class.getResourceAsStream(SERVER_TRUSTSTORE_PATH), PASSWORD.toCharArray());
134                        sslContextFactory.setTrustStore(trustStore);
135
136                        return sslContextFactory;
137                }
138                catch(Exception e){
139                        throw new RuntimeException(Msg.code(2123)+"Failed to obtain SslContextFactory", e);
140                }
141        }
142
143        public String getBase() {
144                return myBase;
145        }
146
147        public String getSecureBase() {
148                return mySecureBase;
149        }
150
151        public void stop() throws Exception {
152                JettyUtil.closeServer(myListenerServer);
153        }
154
155        public abstract void clearDataAndCounts();
156
157        public abstract void setFailNextPut(boolean theFailNextPut);
158
159        public abstract List<Object> getInterceptors();
160
161        public abstract void unregisterInterceptor(Object theNext);
162
163        public abstract void clearCounts();
164
165        public abstract long getPatientCountSearch();
166
167        public abstract long getPatientCountDelete();
168
169        public abstract long getPatientCountUpdate();
170
171        public abstract long getPatientCountRead();
172
173        public abstract long getObservationCountSearch();
174
175        public abstract long getObservationCountDelete();
176
177        public abstract long getObservationCountUpdate();
178
179        public abstract long getObservationCountRead();
180
181        public abstract boolean registerInterceptor(Object theInterceptorAdapter);
182
183        public abstract IResourceProvider getObservationResourceProvider();
184
185        public abstract IResourceProvider getPatientResourceProvider();
186
187        public abstract IResourceProvider getConceptMapResourceProvider();
188
189        public abstract IIdType createPatientWithId(String theId);
190
191        public abstract IIdType createPatient(IBaseResource theBaseResource);
192
193        public abstract IIdType createObservationForPatient(IIdType theFirstTargetPatientId);
194
195        public abstract IIdType createObservation(IBaseResource theBaseResource);
196
197        public void setServerAddressStrategy(boolean theUseHttps){
198                String path = theUseHttps ? mySecureBase : myBase;
199                HardcodedServerAddressStrategy strategy = new HardcodedServerAddressStrategy(path);
200                setServerAddressStrategy(strategy);
201        }
202
203        protected abstract void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy);
204}