c5f66be2682f7e8d8788c863968392f485d637dc
[dcaegen2/analytics/tca-gen2.git] / dcae-analytics / dcae-analytics-web / src / main / java / org / onap / dcae / analytics / web / http / HttpClientPreferencesCustomizer.java
1 /*
2  * ================================================================================
3  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ============LICENSE_END=========================================================
17  *
18  */
19
20 package org.onap.dcae.analytics.web.http;
21
22
23 import java.io.IOException;
24 import java.net.URL;
25 import java.security.KeyManagementException;
26 import java.security.KeyStoreException;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.cert.CertificateException;
29 import java.security.cert.X509Certificate;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33
34 import org.apache.http.HttpHost;
35 import org.apache.http.auth.AuthScope;
36 import org.apache.http.auth.Credentials;
37 import org.apache.http.auth.UsernamePasswordCredentials;
38 import org.apache.http.client.CredentialsProvider;
39 import org.apache.http.conn.ssl.NoopHostnameVerifier;
40 import org.apache.http.conn.ssl.TrustStrategy;
41 import org.apache.http.impl.client.BasicCredentialsProvider;
42 import org.apache.http.impl.client.HttpClientBuilder;
43 import org.apache.http.impl.client.HttpClients;
44 import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
45 import org.apache.http.ssl.SSLContextBuilder;
46 import org.onap.dcae.analytics.model.AnalyticsHttpConstants;
47 import org.onap.dcae.analytics.model.util.function.StringToURLFunction;
48 import org.onap.dcae.analytics.web.util.AnalyticsWebUtils;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.boot.web.client.RestTemplateCustomizer;
52 import org.springframework.http.HttpHeaders;
53 import org.springframework.http.HttpRequest;
54 import org.springframework.http.client.BufferingClientHttpRequestFactory;
55 import org.springframework.http.client.ClientHttpRequestExecution;
56 import org.springframework.http.client.ClientHttpRequestInterceptor;
57 import org.springframework.http.client.ClientHttpResponse;
58 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
59 import org.springframework.http.client.support.BasicAuthorizationInterceptor;
60 import org.springframework.util.ReflectionUtils;
61 import org.springframework.util.StringUtils;
62 import org.springframework.web.client.RestTemplate;
63 import org.springframework.web.util.DefaultUriBuilderFactory;
64
65 /**
66  * Creates a {@link RestTemplateCustomizer} which can be used to configure the spring rest templates
67  * based on given {@link HttpClientPreferences}
68  *
69  * @param <T> Http Client Configurations
70  *
71  * @author Rajiv Singla
72  */
73 public class HttpClientPreferencesCustomizer<T extends HttpClientPreferences> implements RestTemplateCustomizer {
74
75     private static final Logger logger = LoggerFactory.getLogger(HttpClientPreferencesCustomizer.class);
76
77     private final T httpClientConfig;
78
79     public HttpClientPreferencesCustomizer(final T httpClientConfig) {
80         this.httpClientConfig = httpClientConfig;
81     }
82
83     @Override
84     public void customize(final RestTemplate restTemplate) {
85
86         final String httpClientId = httpClientConfig.getHttpClientId() != null ? httpClientConfig.getHttpClientId()
87                 : AnalyticsHttpConstants.DEFAULT_HTTP_CLIENT_ID_PREFIX + AnalyticsWebUtils.RANDOM_ID_SUPPLIER.get();
88         logger.debug("Customizing Rest Template for Http Client Id: {}", httpClientId);
89
90         // set request url
91         final URL requestURL = new StringToURLFunction().apply(httpClientConfig.getRequestURL())
92                 .orElseThrow(() -> new IllegalArgumentException("Http Client URL is required"));
93         restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(requestURL.toString()));
94
95         // add basic authentication headers
96         final String username = httpClientConfig.getUsername();
97         if (username != null) {
98             restTemplate.getInterceptors().add(
99                     new BasicAuthorizationInterceptor(username, httpClientConfig.getPassword()));
100         }
101
102         // set default request headers
103         final HttpHeaders defaultRequestHeaders = httpClientConfig.getRequestHeaders();
104         if (defaultRequestHeaders != null) {
105             restTemplate.getInterceptors().add(new DefaultHeadersRequestInterceptor(defaultRequestHeaders));
106         }
107
108         // create new http client builder
109         final HttpClientBuilder httpClientBuilder = HttpClients.custom().useSystemProperties()
110                 .disableContentCompression();
111         final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
112
113         // set basic authentication credentials
114         configureAuthenticationCredentials(httpClientId, requestURL, credentialsProvider);
115         // set up proxy url
116         configureProxySettings(httpClientId, httpClientBuilder, credentialsProvider);
117         // setup credentials provider
118         httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
119         // set up ssl Context
120         configureSSLContext(httpClientId, httpClientBuilder);
121
122         // set rest client builder
123         final HttpComponentsClientHttpRequestFactory httpRequestFactory =
124                 new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
125
126         // set ecomp logging interceptor
127         if (httpClientConfig.getEnableEcompAuditLogging()) {
128             restTemplate.getInterceptors().add(new EelfAuditLogInterceptor(httpClientConfig));
129         }
130
131         restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));
132     }
133
134     /**
135      * Configures authentication credentials
136      *
137      * @param httpClientId http client id
138      * @param requestURL request url
139      * @param credentialsProvider credentials provider
140      */
141     private void configureAuthenticationCredentials(final String httpClientId, final URL requestURL,
142                                                     final CredentialsProvider credentialsProvider) {
143         final String username = httpClientConfig.getUsername();
144         if (username != null) {
145             logger.info("Setting basic Authentication credentials for Http Client Id: {} with username: {}",
146                     httpClientId, username);
147             final String requestURLProtocol = requestURL.getProtocol();
148             final String requestUrlHost = requestURL.getHost();
149             final Integer requestUrlPortNumber = requestURL.getPort();
150             final HttpHost requestURLHost = new HttpHost(requestUrlHost, requestUrlPortNumber, requestURLProtocol);
151             final String password = httpClientConfig.getPassword();
152             final AuthScope httpClientAuthScope = new AuthScope(requestURLHost);
153             final Credentials credentials = new UsernamePasswordCredentials(username, password);
154             credentialsProvider.setCredentials(httpClientAuthScope, credentials);
155         } else {
156             logger.warn("No credentials set for Http Client Id: {}. No username present", httpClientId);
157         }
158     }
159
160     /**
161      * Configures proxy host, port and authentication
162      *
163      * @param httpClientId http client id
164      * @param httpClientBuilder http client builder
165      * @param credentialsProvider http credentials provider
166      */
167     private void configureProxySettings(final String httpClientId, final HttpClientBuilder httpClientBuilder,
168                                         final CredentialsProvider credentialsProvider) {
169
170         final URL proxyURL = httpClientConfig.getProxyURL();
171
172         if (proxyURL == null) {
173             logger.debug("Proxy not Enabled - bypassing setting Proxy settings for Http Client Id: {}", httpClientId);
174             return;
175         }
176
177         final String proxyProtocol = proxyURL.getProtocol();
178         final String proxyHost = proxyURL.getHost();
179         final Integer proxyPort = proxyURL.getPort();
180         final HttpHost proxy = new HttpHost(proxyHost, proxyPort, proxyProtocol);
181
182         logger.info("Setting up proxy for Http Client Id: {} as: {}", httpClientId, proxy);
183
184         final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
185         httpClientBuilder.setRoutePlanner(routePlanner);
186
187         // get proxy credentials information
188         final String userInfo = proxyURL.getUserInfo();
189
190         if (!StringUtils.hasText(userInfo)) {
191             logger.debug("Proxy username not present. " +
192                     "No proxy authentication credentials will be set for Http Client Id: {}", httpClientId);
193             return;
194         }
195
196         final String[] userInfoArray = userInfo.split(":");
197         final String proxyUsername = userInfoArray[0];
198         String proxyPassword = null;
199         if (userInfoArray.length > 1) {
200             proxyPassword = userInfoArray[1];
201         }
202         logger.info("Setting proxy credentials with username: {} for Http Client Id: {}", proxyUsername, httpClientId);
203         final AuthScope proxyAuthScope = new AuthScope(proxyHost, proxyPort);
204         final Credentials proxyCredentials = new UsernamePasswordCredentials(proxyUsername, proxyPassword);
205         credentialsProvider.setCredentials(proxyAuthScope, proxyCredentials);
206     }
207
208     /**
209      * Configures SSL Context
210      *
211      * @param httpClientId http client id
212      * @param httpClientBuilder http client builder
213      */
214     private void configureSSLContext(final String httpClientId, final HttpClientBuilder httpClientBuilder) {
215
216         // Setup SSL Context to ignore SSL certificate issues if ignoreSSLCertificateErrors is true
217         final boolean ignoreSSLValidation =
218                 Optional.ofNullable(httpClientConfig.getIgnoreSSLValidation()).orElse(false);
219         logger.info("Ignore SSL Certificate Errors attributed is set to: {} for Http Client Id: {}",
220                 ignoreSSLValidation, httpClientId);
221
222         if (!ignoreSSLValidation) {
223             logger.info("SSL Validation will be enforced for Http Client Id: {}", httpClientId);
224             return;
225         }
226
227         logger.warn("SSL Certificate Errors will be ignored for Http Client Id: {}", httpClientId);
228         try {
229             SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
230             sslContextBuilder.loadTrustMaterial(null, new AlwaysTrustingTrustStrategy());
231             httpClientBuilder.setSSLContext(sslContextBuilder.build());
232         } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
233             ReflectionUtils.rethrowRuntimeException(e);
234         }
235         httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
236
237     }
238
239
240     /**
241      * Header Request Interceptor adds defaults headers if not set explicitly
242      */
243     private static class DefaultHeadersRequestInterceptor implements ClientHttpRequestInterceptor {
244         private final HttpHeaders httpHeaders;
245
246         DefaultHeadersRequestInterceptor(final HttpHeaders httpHeaders) {
247             this.httpHeaders = httpHeaders;
248         }
249
250         @Override
251         public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
252                                             final ClientHttpRequestExecution execution) throws IOException {
253             final HttpHeaders currentRequestHeaders = request.getHeaders();
254             for (Map.Entry<String, List<String>> defaultHttpHeader : httpHeaders.entrySet()) {
255                 if (!currentRequestHeaders.containsKey(defaultHttpHeader.getKey())) {
256                     currentRequestHeaders.addAll(defaultHttpHeader.getKey(), defaultHttpHeader.getValue());
257                 }
258             }
259             currentRequestHeaders.setAccept(httpHeaders.getAccept());
260             currentRequestHeaders.setAcceptCharset(httpHeaders.getAcceptCharset());
261             currentRequestHeaders.remove(HttpHeaders.ACCEPT_ENCODING);
262             return execution.execute(request, body);
263         }
264     }
265
266     /**
267      * An implementation of SSL Trust Strategy which does no SSL certificate validation effectively
268      * bypassing any SSL certificate related issues
269      */
270     private static class AlwaysTrustingTrustStrategy implements TrustStrategy {
271         @Override
272         public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
273             return true;
274         }
275     }
276
277 }