Sonar fix- APPC-1715
[appc.git] / appc-adapters / appc-ansible-adapter / appc-ansible-adapter-bundle / src / main / java / org / onap / appc / adapter / ansible / impl / ConnectionBuilder.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * ================================================================================
9  * Modifications Copyright © 2018-2019 IBM.
10  * =============================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  *
23  * ============LICENSE_END=========================================================
24  */
25
26 package org.onap.appc.adapter.ansible.impl;
27
28 import java.io.Closeable;
29 import java.io.FileInputStream;
30 import java.io.IOException;
31 import java.security.KeyManagementException;
32 import java.security.KeyStore;
33 import java.security.KeyStoreException;
34 import java.security.NoSuchAlgorithmException;
35 import java.security.cert.CertificateException;
36 import java.security.cert.CertificateFactory;
37 import java.security.cert.X509Certificate;
38 import javax.net.ssl.SSLContext;
39 import org.apache.http.HttpEntity;
40 import org.apache.http.HttpResponse;
41 import org.apache.http.auth.AuthScope;
42 import org.apache.http.auth.UsernamePasswordCredentials;
43 import org.apache.http.client.config.RequestConfig;
44 import org.apache.http.client.methods.HttpGet;
45 import org.apache.http.client.methods.HttpPost;
46 import org.apache.http.client.protocol.HttpClientContext;
47 import org.apache.http.conn.ssl.NoopHostnameVerifier;
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.entity.StringEntity;
52 import org.apache.http.impl.client.BasicCredentialsProvider;
53 import org.apache.http.impl.client.CloseableHttpClient;
54 import org.apache.http.impl.client.HttpClients;
55 import org.apache.http.util.EntityUtils;
56 import org.onap.appc.adapter.ansible.model.AnsibleResult;
57 import org.onap.appc.adapter.ansible.model.AnsibleResultCodes;
58 import com.att.eelf.configuration.EELFLogger;
59 import com.att.eelf.configuration.EELFManager;
60 import org.json.JSONObject;
61 import org.onap.appc.exceptions.APPCException;
62 import org.apache.commons.lang.StringUtils;
63
64 /**
65  * Returns a custom http client - based on options - can create one with ssl
66  * using an X509 certificate that does NOT have a known CA - create one which
67  * trusts ALL SSL certificates - return default httpclient (which only trusts
68  * known CAs from default cacerts file for process) this is the default option
69  **/
70
71 public class ConnectionBuilder implements Closeable {
72     private static final String STATUS_CODE_KEY = "StatusCode";
73     private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConnectionBuilder.class);
74
75     private CloseableHttpClient httpClient = null;
76     private HttpClientContext httpContext = new HttpClientContext();
77
78     /**
79      * Constructor that initializes an http client based on certificate
80      **/
81
82
83     public ConnectionBuilder(String certFile, int timeout) throws KeyStoreException, CertificateException, IOException,
84             KeyManagementException, NoSuchAlgorithmException{
85
86         /* Point to the certificate */
87         try(FileInputStream fs = new FileInputStream(certFile)) {
88
89             /* Generate a certificate from the X509 */
90             CertificateFactory cf = CertificateFactory.getInstance("X.509");
91             X509Certificate cert = (X509Certificate) cf.generateCertificate(fs);
92
93             /* Create a keystore object and load the certificate there */
94             KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
95             keystore.load(null, null);
96             keystore.setCertificateEntry("cacert", cert);
97
98             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build();
99             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
100                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
101
102             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
103             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
104         }
105     }
106
107     /**
108      * Constructor which trusts all certificates in a specific java keystore file
109      * (assumes a JKS file)
110      **/
111     public ConnectionBuilder(String trustStoreFile, char[] trustStorePasswd, int timeout, String serverIP)
112             throws KeyStoreException, IOException, KeyManagementException, NoSuchAlgorithmException,
113             CertificateException {
114
115         /* Load the specified trustStore */
116         KeyStore keystore = KeyStore.getInstance("JKS");
117         FileInputStream readStream = new FileInputStream(trustStoreFile);
118         keystore.load(readStream, trustStorePasswd);
119         if (StringUtils.isNotBlank(serverIP)) {
120             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
121             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new NoopHostnameVerifier());
122
123             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
124             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
125         } else {
126             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build();
127             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
128                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
129             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
130             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
131         }
132
133     }
134
135     /**
136      * Constructor that trusts ALL SSl certificates (NOTE : ONLY FOR DEV TESTING) if
137      * Mode == 1 or Default if Mode == 0
138      */
139
140     public ConnectionBuilder(int mode, int timeout)
141             throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
142         RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
143         if (mode == 1) {
144             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
145             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
146                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
147
148             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
149         } else {
150             httpClient = HttpClients.custom().setDefaultRequestConfig(config).build();
151         }
152     }
153
154     // Use to create an http context with auth headers
155     public void setHttpContext(String user, String myPassword) {
156
157         // Are credential provided ? If so, set the context to be used
158         if (user != null && !user.isEmpty() && myPassword != null && !myPassword.isEmpty()) {
159             UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user, myPassword);
160             AuthScope authscope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT);
161             BasicCredentialsProvider credsprovider = new BasicCredentialsProvider();
162             credsprovider.setCredentials(authscope, credentials);
163             httpContext.setCredentialsProvider(credsprovider);
164         }
165     }
166
167     // Method posts to the ansible server and writes out response to
168     // Ansible result object
169     public AnsibleResult post(String agentUrl, String payload) {
170
171         AnsibleResult result = new AnsibleResult();
172         try {
173
174             HttpPost postObj = new HttpPost(agentUrl);
175             StringEntity bodyParams = new StringEntity(payload, "UTF-8");
176             postObj.setEntity(bodyParams);
177             postObj.addHeader("Content-type", "application/json");
178
179             HttpResponse response = httpClient.execute(postObj, httpContext);
180
181             HttpEntity entity = response.getEntity();
182             String responseOutput = entity != null ? EntityUtils.toString(entity) : null;
183             int responseCode = response.getStatusLine().getStatusCode();
184             result.setStatusCode(responseCode);
185             result.setStatusMessage(responseOutput);
186         } catch (IOException io) {
187             logger.error("Caught IOException", io);
188             result.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue());
189             result.setStatusMessage(io.getMessage());
190         }
191         return result;
192     }
193
194     // Method gets information from an Ansible server and writes out response to
195     // Ansible result object
196
197     public AnsibleResult get(String agentUrl) {
198
199         AnsibleResult result = new AnsibleResult();
200
201         try {
202             HttpGet getObj = new HttpGet(agentUrl);
203             HttpResponse response = httpClient.execute(getObj, httpContext);
204
205             HttpEntity entity = response.getEntity();
206             String responseOutput = entity != null ? EntityUtils.toString(entity) : null;
207             int responseCode = response.getStatusLine().getStatusCode();
208             logger.info("GetResult response for ansible GET URL" + agentUrl + " returned " + responseOutput);
209             JSONObject postResponse = new JSONObject(responseOutput);
210             if (postResponse.has(STATUS_CODE_KEY)) {
211                 int codeStatus = postResponse.getInt(STATUS_CODE_KEY);
212                 if (codeStatus == AnsibleResultCodes.PENDING.getValue())
213                     result.setStatusCode(codeStatus);
214                 else
215                     result.setStatusCode(responseCode);
216             } else
217                 result.setStatusCode(responseCode);
218             result.setStatusMessage(responseOutput);
219         } catch (IOException io) {
220             result.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue());
221             result.setStatusMessage(io.getMessage());
222             logger.error("Caught IOException", io);
223         }
224         return result;
225     }
226
227     @Override
228     public void close() {
229         if (httpClient != null) {
230             try {
231                 httpClient.close();
232             } catch (IOException e) {
233                 logger.error("Caught IOException during httpClient close", e);
234             }
235         }
236     }
237
238 }