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
9 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.dcae.analytics.web.http;
24 import java.io.IOException;
26 import java.security.KeyManagementException;
27 import java.security.KeyStoreException;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.cert.CertificateException;
30 import java.security.cert.X509Certificate;
31 import java.util.List;
33 import java.util.Optional;
35 import org.apache.http.HttpHost;
36 import org.apache.http.auth.AuthScope;
37 import org.apache.http.auth.Credentials;
38 import org.apache.http.auth.UsernamePasswordCredentials;
39 import org.apache.http.client.CredentialsProvider;
40 import org.apache.http.conn.ssl.NoopHostnameVerifier;
41 import org.apache.http.conn.ssl.TrustStrategy;
42 import org.apache.http.impl.client.BasicCredentialsProvider;
43 import org.apache.http.impl.client.HttpClientBuilder;
44 import org.apache.http.impl.client.HttpClients;
45 import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
46 import org.apache.http.ssl.SSLContextBuilder;
47 import org.onap.dcae.analytics.model.AnalyticsHttpConstants;
48 import org.onap.dcae.analytics.model.util.function.StringToURLFunction;
49 import org.onap.dcae.analytics.web.util.AnalyticsWebUtils;
50 import org.onap.dcaegen2.services.sdk.security.ssl.Password;
51 import org.onap.dcaegen2.services.sdk.security.ssl.Passwords;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.boot.web.client.RestTemplateCustomizer;
55 import org.springframework.http.HttpHeaders;
56 import org.springframework.http.HttpRequest;
57 import org.springframework.http.client.BufferingClientHttpRequestFactory;
58 import org.springframework.http.client.ClientHttpRequestExecution;
59 import org.springframework.http.client.ClientHttpRequestInterceptor;
60 import org.springframework.http.client.ClientHttpResponse;
61 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
62 import org.springframework.http.client.support.BasicAuthorizationInterceptor;
63 import org.springframework.util.ReflectionUtils;
64 import org.springframework.util.StringUtils;
65 import org.springframework.web.client.RestTemplate;
66 import org.springframework.web.util.DefaultUriBuilderFactory;
69 * Creates a {@link RestTemplateCustomizer} which can be used to configure the spring rest templates
70 * based on given {@link HttpClientPreferences}
72 * @param <T> Http Client Configurations
74 * @author Rajiv Singla
76 public class HttpClientPreferencesCustomizer<T extends HttpClientPreferences> implements RestTemplateCustomizer {
78 private static final Logger logger = LoggerFactory.getLogger(HttpClientPreferencesCustomizer.class);
80 private final T httpClientConfig;
82 public HttpClientPreferencesCustomizer(final T httpClientConfig) {
83 this.httpClientConfig = httpClientConfig;
87 public void customize(final RestTemplate restTemplate) {
89 final String httpClientId = httpClientConfig.getHttpClientId() != null ? httpClientConfig.getHttpClientId()
90 : AnalyticsHttpConstants.DEFAULT_HTTP_CLIENT_ID_PREFIX + AnalyticsWebUtils.RANDOM_ID_SUPPLIER.get();
91 logger.debug("Customizing Rest Template for Http Client Id: {}", httpClientId);
94 final URL requestURL = new StringToURLFunction().apply(httpClientConfig.getRequestURL())
95 .orElseThrow(() -> new IllegalArgumentException("Http Client URL is required"));
96 restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(requestURL.toString()));
98 // add basic authentication headers
99 final String username = httpClientConfig.getUsername();
100 if (username != null) {
101 restTemplate.getInterceptors().add(
102 new BasicAuthorizationInterceptor(username, httpClientConfig.getPassword()));
105 // set default request headers
106 final HttpHeaders defaultRequestHeaders = httpClientConfig.getRequestHeaders();
107 if (defaultRequestHeaders != null) {
108 restTemplate.getInterceptors().add(new DefaultHeadersRequestInterceptor(defaultRequestHeaders));
111 // create new http client builder
112 final HttpClientBuilder httpClientBuilder = HttpClients.custom().useSystemProperties()
113 .disableContentCompression();
114 final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
116 // set basic authentication credentials
117 configureAuthenticationCredentials(httpClientId, requestURL, credentialsProvider);
119 configureProxySettings(httpClientId, httpClientBuilder, credentialsProvider);
120 // setup credentials provider
121 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
122 // set up ssl Context
123 configureSSLContext(httpClientId, httpClientBuilder);
125 // set rest client builder
126 final HttpComponentsClientHttpRequestFactory httpRequestFactory =
127 new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
129 // set ecomp logging interceptor
130 if (httpClientConfig.getEnableEcompAuditLogging()) {
131 restTemplate.getInterceptors().add(new EelfAuditLogInterceptor(httpClientConfig));
134 restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));
138 * Configures authentication credentials
140 * @param httpClientId http client id
141 * @param requestURL request url
142 * @param credentialsProvider credentials provider
144 private void configureAuthenticationCredentials(final String httpClientId, final URL requestURL,
145 final CredentialsProvider credentialsProvider) {
146 final String username = httpClientConfig.getUsername();
147 if (username != null) {
148 logger.info("Setting basic Authentication credentials for Http Client Id: {} with username: {}",
149 httpClientId, username);
150 final String requestURLProtocol = requestURL.getProtocol();
151 final String requestUrlHost = requestURL.getHost();
152 final Integer requestUrlPortNumber = requestURL.getPort();
153 final HttpHost requestURLHost = new HttpHost(requestUrlHost, requestUrlPortNumber, requestURLProtocol);
154 final String password = httpClientConfig.getPassword();
155 final AuthScope httpClientAuthScope = new AuthScope(requestURLHost);
156 final Credentials credentials = new UsernamePasswordCredentials(username, password);
157 credentialsProvider.setCredentials(httpClientAuthScope, credentials);
159 logger.warn("No credentials set for Http Client Id: {}. No username present", httpClientId);
164 * Configures proxy host, port and authentication
166 * @param httpClientId http client id
167 * @param httpClientBuilder http client builder
168 * @param credentialsProvider http credentials provider
170 private void configureProxySettings(final String httpClientId, final HttpClientBuilder httpClientBuilder,
171 final CredentialsProvider credentialsProvider) {
173 final URL proxyURL = httpClientConfig.getProxyURL();
175 if (proxyURL == null) {
176 logger.debug("Proxy not Enabled - bypassing setting Proxy settings for Http Client Id: {}", httpClientId);
180 final String proxyProtocol = proxyURL.getProtocol();
181 final String proxyHost = proxyURL.getHost();
182 final Integer proxyPort = proxyURL.getPort();
183 final HttpHost proxy = new HttpHost(proxyHost, proxyPort, proxyProtocol);
185 logger.info("Setting up proxy for Http Client Id: {} as: {}", httpClientId, proxy);
187 final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
188 httpClientBuilder.setRoutePlanner(routePlanner);
190 // get proxy credentials information
191 final String userInfo = proxyURL.getUserInfo();
193 if (!StringUtils.hasText(userInfo)) {
194 logger.debug("Proxy username not present. " +
195 "No proxy authentication credentials will be set for Http Client Id: {}", httpClientId);
199 final String[] userInfoArray = userInfo.split(":");
200 final String proxyUsername = userInfoArray[0];
201 String proxyPassword = null;
202 if (userInfoArray.length > 1) {
203 proxyPassword = userInfoArray[1];
205 logger.info("Setting proxy credentials with username: {} for Http Client Id: {}", proxyUsername, httpClientId);
206 final AuthScope proxyAuthScope = new AuthScope(proxyHost, proxyPort);
207 final Credentials proxyCredentials = new UsernamePasswordCredentials(proxyUsername, proxyPassword);
208 credentialsProvider.setCredentials(proxyAuthScope, proxyCredentials);
212 * Configures SSL Context
214 * @param httpClientId http client id
215 * @param httpClientBuilder http client builder
217 private void configureSSLContext(final String httpClientId, final HttpClientBuilder httpClientBuilder) {
219 // Setup SSL Context to ignore SSL certificate issues if ignoreSSLCertificateErrors is true
220 final boolean ignoreSSLValidation =
221 Optional.ofNullable(httpClientConfig.getIgnoreSSLValidation()).orElse(false);
222 logger.info("Ignore SSL Certificate Errors attributed is set to: {} for Http Client Id: {}",
223 ignoreSSLValidation, httpClientId);
225 if (!ignoreSSLValidation) {
226 logger.info("SSL Validation will be enforced for Http Client Id: {}", httpClientId);
227 setSslContextFromEnvironment(httpClientBuilder);
231 logger.warn("SSL Certificate Errors will be ignored for Http Client Id: {}", httpClientId);
233 SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
234 sslContextBuilder.loadTrustMaterial(null, new AlwaysTrustingTrustStrategy());
235 httpClientBuilder.setSSLContext(sslContextBuilder.build());
236 } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
237 ReflectionUtils.rethrowRuntimeException(e);
239 httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
243 private void setSslContextFromEnvironment(HttpClientBuilder httpClientBuilder) {
244 final String caCertPath = System.getenv("DCAE_CA_CERTPATH");
245 if (!StringUtils.hasText(caCertPath)) {
248 final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
249 final String truststoreFilename = "trust.jks";
250 final String truststorePassFilename = "trust.pass";
251 final String certDirPath = caCertPath.substring(0, caCertPath.lastIndexOf("/"));
252 final File truststoreFile = new File(certDirPath, truststoreFilename);
253 final File truststorePassFile = new File(certDirPath, truststorePassFilename);
254 final Password password = Passwords.fromFile(truststorePassFile);
255 password.use(chars -> {
257 sslContextBuilder.loadTrustMaterial(truststoreFile, chars);
258 httpClientBuilder.setSSLContext(sslContextBuilder.build());
259 } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException |
260 KeyManagementException e) {
261 logger.warn("Could not load trusted certificates from environment");
269 * Header Request Interceptor adds defaults headers if not set explicitly
271 private static class DefaultHeadersRequestInterceptor implements ClientHttpRequestInterceptor {
272 private final HttpHeaders httpHeaders;
274 DefaultHeadersRequestInterceptor(final HttpHeaders httpHeaders) {
275 this.httpHeaders = httpHeaders;
279 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
280 final ClientHttpRequestExecution execution) throws IOException {
281 final HttpHeaders currentRequestHeaders = request.getHeaders();
282 for (Map.Entry<String, List<String>> defaultHttpHeader : httpHeaders.entrySet()) {
283 if (!currentRequestHeaders.containsKey(defaultHttpHeader.getKey())) {
284 currentRequestHeaders.addAll(defaultHttpHeader.getKey(), defaultHttpHeader.getValue());
287 currentRequestHeaders.setAccept(httpHeaders.getAccept());
288 currentRequestHeaders.setAcceptCharset(httpHeaders.getAcceptCharset());
289 currentRequestHeaders.remove(HttpHeaders.ACCEPT_ENCODING);
290 return execution.execute(request, body);
295 * An implementation of SSL Trust Strategy which does no SSL certificate validation effectively
296 * bypassing any SSL certificate related issues
298 private static class AlwaysTrustingTrustStrategy implements TrustStrategy {
300 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {