Fix for Penetration test _ Session and cookie management
[vid.git] / vid-app-common / src / main / java / org / onap / vid / client / SyncRestClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2018 - 2019 Nokia. All rights reserved.
6  * Modifications Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.vid.client;
23
24 import static org.onap.vid.client.UnirestPatchKt.patched;
25 import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import io.joshworks.restclient.http.HttpResponse;
29 import io.joshworks.restclient.http.JsonNode;
30 import io.joshworks.restclient.http.RestClient;
31 import io.joshworks.restclient.http.exceptions.RestClientException;
32 import io.joshworks.restclient.http.mapper.ObjectMapper;
33 import io.joshworks.restclient.request.GetRequest;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.security.KeyManagementException;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.UnrecoverableKeyException;
43 import java.security.cert.CertificateException;
44 import java.util.Map;
45 import javax.net.ssl.SSLContext;
46 import javax.net.ssl.SSLException;
47 import org.apache.commons.lang3.ObjectUtils;
48 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
49 import org.apache.http.conn.ssl.SSLContexts;
50 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
51 import org.apache.http.impl.client.CloseableHttpClient;
52 import org.apache.http.impl.client.HttpClientBuilder;
53 import org.apache.http.impl.client.HttpClients;
54 import org.eclipse.jetty.util.security.Password;
55 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
56 import org.onap.portalsdk.core.util.SystemProperties;
57 import org.onap.vid.logging.ApacheClientMetricRequestInterceptor;
58 import org.onap.vid.logging.ApacheClientMetricResponseInterceptor;
59 import org.onap.vid.properties.VidProperties;
60 import org.onap.vid.utils.Logging;
61 import org.springframework.http.HttpMethod;
62
63 public class SyncRestClient implements SyncRestClientInterface {
64     private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
65     private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
66     private static final String HTTPS_SCHEMA = "https://";
67     private static final String HTTP_SCHEMA = "http://";
68     private final Logging loggingService;
69     private final EELFLogger outgoingRequestsLogger;
70
71     private RestClient restClient;
72
73     public SyncRestClient(Logging loggingService) {
74         this(null, null, loggingService, false);
75     }
76
77     public SyncRestClient(Logging loggingService, boolean useLoggingInterceptor) {
78         this(null, null, loggingService, useLoggingInterceptor);
79     }
80
81     public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) {
82         this(null, objectMapper,  loggingService, true);
83     }
84
85     public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) {
86         this(httpClient, null,  loggingService, false);
87     }
88
89     public SyncRestClient(CloseableHttpClient httpClient,
90         ObjectMapper objectMapper,
91         Logging loggingService,
92         boolean useLoggingInterceptor) {
93         restClient = RestClient
94             .newClient()
95             .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper()))
96             .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient(useLoggingInterceptor)))
97             .build();
98         this.loggingService = loggingService;
99         this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient");
100     }
101
102     @Override
103     public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
104         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
105
106         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
107             url2 -> restClient.post(url2).headers(headers).body(body).asJson());
108
109         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
110
111         return response;
112     }
113
114     @Override
115     public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
116         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
117
118         HttpResponse<T> response = callWithRetryOverHttp(url,
119             url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
120
121         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
122
123         return response;
124     }
125
126     @Override
127     public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
128         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
129
130         HttpResponse<JsonNode> response = callWithRetryOverHttp(url, url2 -> {
131             GetRequest getRequest = restClient.get(url2).headers(headers);
132             routeParams.forEach(getRequest::routeParam);
133             return getRequest.asJson();
134         });
135
136         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
137
138         return response;
139     }
140
141     @Override
142     public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
143                                    Class<T> responseClass) {
144         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
145
146         HttpResponse<T> response = callWithRetryOverHttp(url, url2 -> {
147             GetRequest getRequest = restClient.get(url2).headers(headers);
148             routeParams.forEach(getRequest::routeParam);
149             return getRequest.asObject(responseClass);
150         });
151
152         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
153
154         return response;
155     }
156
157     @Override
158     public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
159                                                Map<String, String> routeParams) {
160         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
161
162         HttpResponse<InputStream> response = callWithRetryOverHttp(url, url2 -> {
163             GetRequest getRequest = restClient.get(url2).headers(headers);
164             routeParams.forEach(getRequest::routeParam);
165             return getRequest.asBinary();
166         });
167
168         //no logging of the response since the response is too long
169         return response;
170
171     }
172
173     @Override
174     public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
175         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
176
177         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
178             url2 -> restClient.put(url2).headers(headers).body(body).asJson());
179
180         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
181
182         return response;
183     }
184
185     @Override
186     public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
187         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
188
189         HttpResponse<T> response = callWithRetryOverHttp(url,
190             url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
191
192         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
193
194         return response;
195     }
196
197     @Override
198     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
199         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url, body);
200
201         HttpResponse<T> response = callWithRetryOverHttp(url,
202             url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass));
203
204         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
205
206         return response;
207     }
208
209     @Override
210     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
211         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
212
213         HttpResponse<T> response = callWithRetryOverHttp(url,
214             url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
215
216         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
217
218         return response;
219     }
220
221     @Override
222     public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
223         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
224
225         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
226             url2 -> restClient.delete(url2).headers(headers).asJson());
227
228         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
229
230         return response;
231     }
232
233     @Override
234     public void destroy() {
235         restClient.shutdown();
236     }
237
238     private <T> HttpResponse<T> callWithRetryOverHttp(String url, HttpRequest<T> httpRequest) {
239         try {
240             return callWithRetryOverHttpThrows(url, httpRequest);
241         } catch (IOException e) {
242             throw new SyncRestClientException("IOException while calling rest service", e);
243         }
244     }
245
246     private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
247         try {
248             return patched(httpRequest.apply(url));
249         } catch (RestClientException e) {
250             if (causedBySslHandshakeError(e)) {
251                 logger.warn("SSL Handshake problem occured. Will try to retry over Http.", e);
252                 return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)));
253             }
254             throw e;
255         }
256     }
257
258     private boolean causedBySslHandshakeError(RestClientException exception) {
259         return exception.getCause() instanceof SSLException;
260     }
261
262     private ObjectMapper defaultObjectMapper() {
263         return JOSHWORKS_JACKSON_OBJECT_MAPPER;
264     }
265
266     private CloseableHttpClient defaultHttpClient(boolean useLoggingInterceptor) {
267         try {
268             String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
269             String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
270             String decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
271
272             KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
273             SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
274             SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext);
275
276             HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSf);
277             if (useLoggingInterceptor) {
278                 httpClientBuilder
279                     .addInterceptorFirst(new ApacheClientMetricRequestInterceptor())
280                     .addInterceptorLast(new ApacheClientMetricResponseInterceptor());
281             }
282             return httpClientBuilder.build();
283         } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
284             logger.warn("Cannot initialize custom http client from current configuration. Using default one.", e);
285             return HttpClients.createDefault();
286         }
287     }
288
289     private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
290         return new SSLConnectionSocketFactory(
291                 sslcontext,
292                 SUPPORTED_SSL_VERSIONS,
293                 null,
294                 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
295     }
296
297     private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
298             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
299         return SSLContexts.custom()
300                 .useTLS()
301                 .loadKeyMaterial(trustStore, trustStorePass.toCharArray())
302                 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
303                 .build();
304     }
305
306     private KeyStore loadTruststore(String trustStorePath, String trustStorePass)
307             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
308         KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
309         try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
310             trustStore.load(instream, trustStorePass.toCharArray());
311         }
312         return trustStore;
313     }
314
315     @FunctionalInterface
316     private interface HttpRequest<T> {
317         HttpResponse<T> apply(String url) throws IOException;
318     }
319
320 }