improved code structure
[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.apache.commons.lang.StringUtils;
62
63 /**
64  * Returns a custom http client - based on options - can create one with ssl
65  * using an X509 certificate that does NOT have a known CA - create one which
66  * trusts ALL SSL certificates - return default httpclient (which only trusts
67  * known CAs from default cacerts file for process) this is the default option
68  **/
69
70 public class ConnectionBuilder implements Closeable {
71     private static final String STATUS_CODE_KEY = "StatusCode";
72     private static final EELFLogger logger = EELFManager.getInstance().getLogger(ConnectionBuilder.class);
73
74     private CloseableHttpClient httpClient = null;
75     private HttpClientContext httpContext = new HttpClientContext();
76
77     /**
78      * Constructor that initializes an http client based on certificate
79      **/
80
81
82     public ConnectionBuilder(String certFile, int timeout) throws KeyStoreException, CertificateException, IOException,
83             KeyManagementException, NoSuchAlgorithmException{
84
85         /* Point to the certificate */
86         try(FileInputStream fs = new FileInputStream(certFile)) {
87
88             /* Generate a certificate from the X509 */
89             CertificateFactory cf = CertificateFactory.getInstance("X.509");
90             X509Certificate cert = (X509Certificate) cf.generateCertificate(fs);
91
92             /* Create a keystore object and load the certificate there */
93             KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
94             keystore.load(null, null);
95             keystore.setCertificateEntry("cacert", cert);
96
97             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build();
98             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
99                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
100
101             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
102             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
103         }
104     }
105
106     /**
107      * Constructor which trusts all certificates in a specific java keystore file
108      * (assumes a JKS file)
109      **/
110     public ConnectionBuilder(String trustStoreFile, char[] trustStorePasswd, int timeout, String serverIP)
111             throws KeyStoreException, IOException, KeyManagementException, NoSuchAlgorithmException,
112             CertificateException {
113
114         /* Load the specified trustStore */
115         KeyStore keystore = KeyStore.getInstance("JKS");
116         FileInputStream readStream = new FileInputStream(trustStoreFile);
117         keystore.load(readStream, trustStorePasswd);
118         if (StringUtils.isNotBlank(serverIP)) {
119             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
120             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new NoopHostnameVerifier());
121
122             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
123             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
124         } else {
125             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keystore).build();
126             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
127                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
128             RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
129             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
130         }
131
132     }
133
134     /**
135      * Constructor that trusts ALL SSl certificates (NOTE : ONLY FOR DEV TESTING) if
136      * Mode == 1 or Default if Mode == 0
137      */
138
139     public ConnectionBuilder(int mode, int timeout)
140             throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
141         RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout).build();
142         if (mode == 1) {
143             SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
144             SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext,
145                     SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
146
147             httpClient = HttpClients.custom().setDefaultRequestConfig(config).setSSLSocketFactory(factory).build();
148         } else {
149             httpClient = HttpClients.custom().setDefaultRequestConfig(config).build();
150         }
151     }
152
153     // Use to create an http context with auth headers
154     public void setHttpContext(String user, String myPassword) {
155
156         // Are credential provided ? If so, set the context to be used
157         if (user != null && !user.isEmpty() && myPassword != null && !myPassword.isEmpty()) {
158             UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user, myPassword);
159             AuthScope authscope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT);
160             BasicCredentialsProvider credsprovider = new BasicCredentialsProvider();
161             credsprovider.setCredentials(authscope, credentials);
162             httpContext.setCredentialsProvider(credsprovider);
163         }
164     }
165
166     // Method posts to the ansible server and writes out response to
167     // Ansible result object
168     public AnsibleResult post(String agentUrl, String payload) {
169
170         AnsibleResult result = new AnsibleResult();
171         try {
172
173             HttpPost postObj = new HttpPost(agentUrl);
174             StringEntity bodyParams = new StringEntity(payload, "UTF-8");
175             postObj.setEntity(bodyParams);
176             postObj.addHeader("Content-type", "application/json");
177
178             HttpResponse response = httpClient.execute(postObj, httpContext);
179
180             HttpEntity entity = response.getEntity();
181             String responseOutput = entity != null ? EntityUtils.toString(entity) : null;
182             int responseCode = response.getStatusLine().getStatusCode();
183             result.setStatusCode(responseCode);
184             result.setStatusMessage(responseOutput);
185         } catch (IOException io) {
186             logger.error("Caught IOException", io);
187             result.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue());
188             result.setStatusMessage(io.getMessage());
189         }
190         return result;
191     }
192
193     // Method gets information from an Ansible server and writes out response to
194     // Ansible result object
195
196     public AnsibleResult get(String agentUrl) {
197
198         AnsibleResult result = new AnsibleResult();
199
200         try {
201             HttpGet getObj = new HttpGet(agentUrl);
202             HttpResponse response = httpClient.execute(getObj, httpContext);
203
204             HttpEntity entity = response.getEntity();
205             String responseOutput = entity != null ? EntityUtils.toString(entity) : null;
206             int responseCode = response.getStatusLine().getStatusCode();
207             logger.info("GetResult response for ansible GET URL" + agentUrl + " returned " + responseOutput);
208             JSONObject postResponse = new JSONObject(responseOutput);
209             if (postResponse.has(STATUS_CODE_KEY)) {
210                 int codeStatus = postResponse.getInt(STATUS_CODE_KEY);
211                 if (codeStatus == AnsibleResultCodes.PENDING.getValue())
212                     result.setStatusCode(codeStatus);
213                 else
214                     result.setStatusCode(responseCode);
215             } else
216                 result.setStatusCode(responseCode);
217             result.setStatusMessage(responseOutput);
218         } catch (IOException io) {
219             result.setStatusCode(AnsibleResultCodes.IO_EXCEPTION.getValue());
220             result.setStatusMessage(io.getMessage());
221             logger.error("Caught IOException", io);
222         }
223         return result;
224     }
225
226     @Override
227     public void close() {
228         if (httpClient != null) {
229             try {
230                 httpClient.close();
231             } catch (IOException e) {
232                 logger.error("Caught IOException during httpClient close", e);
233             }
234         }
235     }
236
237 }