[DMAAP-DR] Remove AAF/TLS phase 1
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / ProxyServlet.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
4  * * ===========================================================================
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * * ===========================================================================
7  * * Licensed under the Apache License, Version 2.0 (the "License");
8  * * you may not use this file except in compliance with the License.
9  * * You may obtain a copy of the License at
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
13  *  * Unless required by applicable law or agreed to in writing, software
14  * * distributed under the License is distributed on an "AS IS" BASIS,
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * * See the License for the specific language governing permissions and
17  * * limitations under the License.
18  * * ============LICENSE_END====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23
24
25 package org.onap.dmaap.datarouter.provisioning;
26
27 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;
28
29 import jakarta.servlet.ServletConfig;
30 import jakarta.servlet.ServletException;
31 import jakarta.servlet.http.HttpServletRequest;
32 import jakarta.servlet.http.HttpServletResponse;
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.FileNotFoundException;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.net.URI;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.util.Collections;
42 import java.util.List;
43 import org.apache.commons.io.IOUtils;
44 import org.apache.http.Header;
45 import org.apache.http.HttpEntity;
46 import org.apache.http.HttpResponse;
47 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
48 import org.apache.http.client.methods.HttpGet;
49 import org.apache.http.client.methods.HttpRequestBase;
50 import org.apache.http.conn.scheme.PlainSocketFactory;
51 import org.apache.http.conn.scheme.Scheme;
52 import org.apache.http.conn.ssl.SSLSocketFactory;
53 import org.apache.http.entity.BasicHttpEntity;
54 import org.apache.http.impl.client.AbstractHttpClient;
55 import org.apache.http.impl.client.DefaultHttpClient;
56 import org.onap.dmaap.datarouter.provisioning.utils.AafPropsUtils;
57 import org.onap.dmaap.datarouter.provisioning.utils.SynchronizerTask;
58 import org.onap.dmaap.datarouter.provisioning.utils.URLUtilities;
59
60 /**
61  * This class is the base class for those servlets that need to proxy their requests from the standby to active server.
62  * Its methods perform the proxy function to the active server. If the active server is not reachable, a 503
63  * (SC_SERVICE_UNAVAILABLE) is returned.  Only DELETE/GET/PUT/POST are supported.
64  *
65  * @author Robert Eby
66  * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $
67  */
68 @SuppressWarnings("serial")
69
70 public class ProxyServlet extends BaseServlet {
71
72     private boolean inited = false;
73     private Scheme sch;
74
75     /**
76      * Initialize this servlet, by setting up SSL.
77      */
78     @SuppressWarnings("deprecation")
79     @Override
80     public void init(ServletConfig config) throws ServletException {
81         super.init(config);
82         try {
83             if (Boolean.TRUE.equals(ProvRunner.getTlsEnabled())) {
84                 // Set up keystore
85                 String type = AafPropsUtils.KEYSTORE_TYPE_PROPERTY;
86                 String store = ProvRunner.getAafPropsUtils().getKeystorePathProperty();
87                 String pass = ProvRunner.getAafPropsUtils().getKeystorePassProperty();
88                 KeyStore keyStore = readStore(store, pass, type);
89                 // Set up truststore
90                 store = ProvRunner.getAafPropsUtils().getTruststorePathProperty();
91                 pass = ProvRunner.getAafPropsUtils().getTruststorePassProperty();
92                 KeyStore trustStore = readStore(store, pass, AafPropsUtils.TRUESTSTORE_TYPE_PROPERTY);
93
94                 // We are connecting with the node name, but the certificate will have the CNAME
95                 // So we need to accept a non-matching certificate name
96                 SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore,
97                     ProvRunner.getAafPropsUtils().getKeystorePassProperty(), trustStore);
98                 socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
99                 sch = new Scheme("https", 443, socketFactory);
100             } else {
101                 PlainSocketFactory socketFactory = new PlainSocketFactory();
102                 sch = new Scheme("http", 80, socketFactory);
103             }
104             inited = true;
105         } catch (Exception e) {
106             intlogger.error("ProxyServlet.init: " + e.getMessage(), e);
107         }
108         intlogger.info("ProxyServlet: inited = " + inited);
109     }
110
111     private KeyStore readStore(String store, String pass, String type) throws KeyStoreException {
112         KeyStore ks = KeyStore.getInstance(type);
113         try (FileInputStream instream = new FileInputStream(new File(store))) {
114             ks.load(instream, pass.toCharArray());
115         } catch (FileNotFoundException fileNotFoundException) {
116             intlogger.error("ProxyServlet.readStore: " + fileNotFoundException.getMessage(), fileNotFoundException);
117         } catch (Exception x) {
118             intlogger.error("READING TRUSTSTORE: " + x);
119         }
120         return ks;
121     }
122
123     /**
124      * Return <i>true</i> if the requester has NOT set the <i>noproxy</i> CGI variable. If they have, this indicates
125      * they want to forcibly turn the proxy off.
126      *
127      * @param req the HTTP request
128      * @return true or false
129      */
130     boolean isProxyOK(final HttpServletRequest req) {
131         String str = req.getQueryString();
132         if (str != null) {
133             str = str.replaceAll("&amp;", "&");
134             for (String s : str.split("&")) {
135                 if ("noproxy".equals(s) || s.startsWith("noproxy=")) {
136                     return false;
137                 }
138             }
139         }
140         return true;
141     }
142
143     /**
144      * Is this the standby server?  If it is, the proxy functions can be used. If not, the proxy functions should not be
145      * called, and will send a response of 500 (Internal Server Error).
146      *
147      * @return true if this server is the standby (and hence a proxy server).
148      */
149     boolean isProxyServer() {
150         SynchronizerTask st = SynchronizerTask.getSynchronizer();
151         return st.getPodState() == SynchronizerTask.STANDBY_POD;
152     }
153
154     /**
155      * Issue a proxy DELETE to the active provisioning server.
156      */
157     @Override
158     public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
159         doProxy(req, resp, "DELETE");
160     }
161
162     /**
163      * Issue a proxy GET to the active provisioning server.
164      */
165     @Override
166     public void doGet(HttpServletRequest req, HttpServletResponse resp) {
167         doProxy(req, resp, "GET");
168     }
169
170     /**
171      * Issue a proxy PUT to the active provisioning server.
172      */
173     @Override
174     public void doPut(HttpServletRequest req, HttpServletResponse resp) {
175         doProxy(req, resp, "PUT");
176     }
177
178     /**
179      * Issue a proxy POST to the active provisioning server.
180      */
181     @Override
182     public void doPost(HttpServletRequest req, HttpServletResponse resp) {
183         doProxy(req, resp, "POST");
184     }
185
186     /**
187      * Issue a proxy GET to the active provisioning server.  Unlike doGet() above, this method will allow the caller to
188      * fall back to other code if the remote server is unreachable.
189      *
190      * @return true if the proxy succeeded
191      */
192     boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) {
193         boolean rv = false;
194         if (inited) {
195             String url = buildUrl(req);
196             intlogger.info("ProxyServlet: proxying with fallback GET " + url);
197             try (AbstractHttpClient httpclient = new DefaultHttpClient()) {
198                 HttpRequestBase proxy = new HttpGet(url);
199                 try {
200                     httpclient.getConnectionManager().getSchemeRegistry().register(sch);
201
202                     // Copy request headers and request body
203                     copyRequestHeaders(req, proxy);
204
205                     // Execute the request
206                     HttpResponse pxyResponse = httpclient.execute(proxy);
207
208                     // Get response headers and body
209                     int code = pxyResponse.getStatusLine().getStatusCode();
210                     resp.setStatus(code);
211                     copyResponseHeaders(pxyResponse, resp);
212                     copyEntityContent(pxyResponse, resp);
213                     rv = true;
214
215                 } catch (IOException e) {
216                     intlogger.error("ProxyServlet.doGetWithFallback: " + e.getMessage(), e);
217                 } finally {
218                     proxy.releaseConnection();
219                     httpclient.getConnectionManager().shutdown();
220                 }
221             }
222         } else {
223             intlogger.warn("ProxyServlet: proxy disabled");
224         }
225         return rv;
226     }
227
228     private void doProxy(HttpServletRequest req, HttpServletResponse resp, final String method) {
229         if (inited && isProxyServer()) {
230             String url = buildUrl(req);
231             intlogger.info("ProxyServlet: proxying " + method + " " + url);
232             try (AbstractHttpClient httpclient = new DefaultHttpClient()) {
233                 ProxyHttpRequest proxy = new ProxyHttpRequest(method, url);
234                 try {
235                     httpclient.getConnectionManager().getSchemeRegistry().register(sch);
236
237                     // Copy request headers and request body
238                     copyRequestHeaders(req, proxy);
239
240                     handlePutOrPost(req, method, proxy);
241
242                     // Execute the request
243                     HttpResponse pxyResponse = httpclient.execute(proxy);
244
245                     // Get response headers and body
246                     int code = pxyResponse.getStatusLine().getStatusCode();
247                     resp.setStatus(code);
248                     copyResponseHeaders(pxyResponse, resp);
249                     copyEntityContent(pxyResponse, resp);
250                 } catch (IOException e) {
251                     intlogger.warn("ProxyServlet.doProxy: " + e.getMessage(), e);
252                     sendResponseError(resp, HttpServletResponse.SC_SERVICE_UNAVAILABLE, "", intlogger);
253                 } finally {
254                     proxy.releaseConnection();
255                     httpclient.getConnectionManager().shutdown();
256                 }
257             }
258         } else {
259             intlogger.warn("ProxyServlet: proxy disabled");
260             sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);
261         }
262     }
263
264     private void handlePutOrPost(HttpServletRequest req, String method, ProxyHttpRequest proxy) throws IOException {
265         if ("POST".equals(method) || "PUT".equals(method)) {
266             BasicHttpEntity body = new BasicHttpEntity();
267             body.setContent(req.getInputStream());
268             body.setContentLength(-1);    // -1 = unknown
269             proxy.setEntity(body);
270         }
271     }
272
273     private String buildUrl(HttpServletRequest req) {
274         StringBuilder sb = new StringBuilder("https://");
275         sb.append(URLUtilities.getPeerPodName());
276         sb.append(req.getRequestURI());
277         String query = req.getQueryString();
278         if (query != null) {
279             sb.append("?").append(query);
280         }
281         return sb.toString();
282     }
283
284     private void copyRequestHeaders(HttpServletRequest from, HttpRequestBase to) {
285         List<String> list = Collections.list(from.getHeaderNames());
286         for (String name : list) {
287             // Proxy code will add this one
288             if (!"Content-Length".equalsIgnoreCase(name)) {
289                 to.addHeader(name, from.getHeader(name));
290             }
291         }
292     }
293
294     void copyResponseHeaders(HttpResponse from, HttpServletResponse to) {
295         for (Header hdr : from.getAllHeaders()) {
296             // Don't copy Date: our Jetty will add another Date header
297             if (!"Date".equals(hdr.getName())) {
298                 to.addHeader(hdr.getName(), hdr.getValue());
299             }
300         }
301     }
302
303     void copyEntityContent(HttpResponse pxyResponse, HttpServletResponse resp) {
304         HttpEntity entity = pxyResponse.getEntity();
305         if (entity != null) {
306             try (InputStream in = entity.getContent()) {
307                 IOUtils.copy(in, resp.getOutputStream());
308             } catch (Exception e) {
309                 intlogger.error("ProxyServlet.copyEntityContent: " + e.getMessage(), e);
310             }
311         }
312     }
313
314     public static class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {
315
316         private final String method;
317
318         ProxyHttpRequest(final String method, final String uri) {
319             super();
320             this.method = method;
321             setURI(URI.create(uri));
322         }
323
324         @Override
325         public String getMethod() {
326             return method;
327         }
328     }
329 }