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.HttpClients;
53 import org.eclipse.jetty.util.security.Password;
54 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
55 import org.onap.portalsdk.core.util.SystemProperties;
56 import org.onap.vid.properties.VidProperties;
57 import org.onap.vid.utils.Logging;
58 import org.springframework.http.HttpMethod;
59
60 public class SyncRestClient implements SyncRestClientInterface {
61     private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
62     private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
63     private static final String HTTPS_SCHEMA = "https://";
64     private static final String HTTP_SCHEMA = "http://";
65     private final Logging loggingService;
66     private final EELFLogger outgoingRequestsLogger;
67
68     private RestClient restClient;
69
70     public SyncRestClient(Logging loggingService) {
71         this(null, null, loggingService);
72     }
73
74     public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) {
75         this(null, objectMapper,  loggingService);
76     }
77
78     public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) {
79         this(httpClient, null,  loggingService);
80     }
81
82     public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper, Logging loggingService) {
83         restClient = RestClient
84             .newClient()
85             .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper()))
86             .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient()))
87             .build();
88         this.loggingService = loggingService;
89         this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient");
90     }
91
92     @Override
93     public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
94         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
95
96         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
97             url2 -> restClient.post(url2).headers(headers).body(body).asJson());
98
99         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
100
101         return response;
102     }
103
104     @Override
105     public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
106         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
107
108         HttpResponse<T> response = callWithRetryOverHttp(url,
109             url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
110
111         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
112
113         return response;
114     }
115
116     @Override
117     public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
118         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
119
120         HttpResponse<JsonNode> response = callWithRetryOverHttp(url, url2 -> {
121             GetRequest getRequest = restClient.get(url2).headers(headers);
122             routeParams.forEach(getRequest::routeParam);
123             return getRequest.asJson();
124         });
125
126         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
127
128         return response;
129     }
130
131     @Override
132     public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
133                                    Class<T> responseClass) {
134         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
135
136         HttpResponse<T> response = callWithRetryOverHttp(url, url2 -> {
137             GetRequest getRequest = restClient.get(url2).headers(headers);
138             routeParams.forEach(getRequest::routeParam);
139             return getRequest.asObject(responseClass);
140         });
141
142         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
143
144         return response;
145     }
146
147     @Override
148     public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
149                                                Map<String, String> routeParams) {
150         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
151
152         HttpResponse<InputStream> response = callWithRetryOverHttp(url, url2 -> {
153             GetRequest getRequest = restClient.get(url2).headers(headers);
154             routeParams.forEach(getRequest::routeParam);
155             return getRequest.asBinary();
156         });
157
158         //no logging of the response since the response is too long
159         return response;
160
161     }
162
163     @Override
164     public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
165         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
166
167         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
168             url2 -> restClient.put(url2).headers(headers).body(body).asJson());
169
170         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
171
172         return response;
173     }
174
175     @Override
176     public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
177         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
178
179         HttpResponse<T> response = callWithRetryOverHttp(url,
180             url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
181
182         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
183
184         return response;
185     }
186
187     @Override
188     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
189         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url, body);
190
191         HttpResponse<T> response = callWithRetryOverHttp(url,
192             url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass));
193
194         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
195
196         return response;
197     }
198
199     @Override
200     public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
201         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
202
203         HttpResponse<T> response = callWithRetryOverHttp(url,
204             url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
205
206         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
207
208         return response;
209     }
210
211     @Override
212     public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
213         loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
214
215         HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
216             url2 -> restClient.delete(url2).headers(headers).asJson());
217
218         loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
219
220         return response;
221     }
222
223     @Override
224     public void destroy() {
225         restClient.shutdown();
226     }
227
228     private <T> HttpResponse<T> callWithRetryOverHttp(String url, HttpRequest<T> httpRequest) {
229         try {
230             return callWithRetryOverHttpThrows(url, httpRequest);
231         } catch (IOException e) {
232             throw new SyncRestClientException("IOException while calling rest service", e);
233         }
234     }
235
236     private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
237         try {
238             return patched(httpRequest.apply(url));
239         } catch (RestClientException e) {
240             if (causedBySslHandshakeError(e)) {
241                 logger.warn("SSL Handshake problem occured. Will try to retry over Http.", e);
242                 return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)));
243             }
244             throw e;
245         }
246     }
247
248     private boolean causedBySslHandshakeError(RestClientException exception) {
249         return exception.getCause() instanceof SSLException;
250     }
251
252     private ObjectMapper defaultObjectMapper() {
253         return JOSHWORKS_JACKSON_OBJECT_MAPPER;
254     }
255
256     private CloseableHttpClient defaultHttpClient() {
257         try {
258             String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
259             String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
260             String decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
261
262             KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
263             SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
264             SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext);
265
266             return HttpClients.custom().setSSLSocketFactory(sslSf).build();
267         } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
268             logger.warn("Cannot initialize custom http client from current configuration. Using default one.", e);
269             return HttpClients.createDefault();
270         }
271     }
272
273     private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
274         return new SSLConnectionSocketFactory(
275                 sslcontext,
276                 SUPPORTED_SSL_VERSIONS,
277                 null,
278                 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
279     }
280
281     private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
282             throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
283         return SSLContexts.custom()
284                 .useTLS()
285                 .loadKeyMaterial(trustStore, trustStorePass.toCharArray())
286                 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
287                 .build();
288     }
289
290     private KeyStore loadTruststore(String trustStorePath, String trustStorePass)
291             throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
292         KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
293         try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
294             trustStore.load(instream, trustStorePass.toCharArray());
295         }
296         return trustStore;
297     }
298
299     @FunctionalInterface
300     private interface HttpRequest<T> {
301         HttpResponse<T> apply(String url) throws IOException;
302     }
303
304 }