ef106ab4465ea624911f78d33da110e7ccf05b63
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / BaseServlet.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 com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN;
28
29 import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS;
30 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
31 import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
32
33
34
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.net.InetAddress;
38 import java.net.UnknownHostException;
39 import java.security.cert.X509Certificate;
40 import java.sql.Connection;
41 import java.sql.SQLException;
42
43 import javax.servlet.ServletConfig;
44 import javax.servlet.ServletException;
45 import javax.servlet.http.HttpServlet;
46 import javax.servlet.http.HttpServletRequest;
47
48 import com.att.eelf.configuration.EELFLogger;
49 import com.att.eelf.configuration.EELFManager;
50 import org.apache.commons.lang3.StringUtils;
51 import org.json.JSONArray;
52 import org.json.JSONException;
53 import org.json.JSONObject;
54 import org.json.JSONTokener;
55 import org.onap.dmaap.datarouter.authz.Authorizer;
56 import org.onap.dmaap.datarouter.authz.impl.ProvAuthorizer;
57 import org.onap.dmaap.datarouter.authz.impl.ProvDataProvider;
58 import org.onap.dmaap.datarouter.provisioning.beans.*;
59 import org.onap.dmaap.datarouter.provisioning.utils.DB;
60 import org.onap.dmaap.datarouter.provisioning.utils.PasswordProcessor;
61 import org.onap.dmaap.datarouter.provisioning.utils.ThrottleFilter;
62 import org.slf4j.MDC;
63
64 import javax.mail.*;
65 import javax.mail.internet.InternetAddress;
66 import javax.mail.internet.MimeBodyPart;
67 import javax.mail.internet.MimeMessage;
68 import javax.mail.internet.MimeMultipart;
69 import java.security.GeneralSecurityException;
70 import java.util.*;
71 import java.util.regex.Pattern;
72
73
74 /**
75  * This is the base class for all Servlets in the provisioning code. It provides standard constants and some common
76  * methods.
77  *
78  * @author Robert Eby
79  * @version $Id: BaseServlet.java,v 1.16 2014/03/12 19:45:40 eby Exp $
80  */
81 @SuppressWarnings("serial")
82 public class BaseServlet extends HttpServlet implements ProvDataProvider {
83
84     public static final String BEHALF_HEADER = "X-DMAAP-DR-ON-BEHALF-OF";
85
86     public static final String EXCLUDE_AAF_HEADER = "X-EXCLUDE-AAF";
87
88     private static final String AAF_CADI_FEED_TYPE = "org.onap.dmaap.datarouter.provserver.aaf.feed.type";
89     private static final String AAF_CADI_SUB_TYPE = "org.onap.dmaap.datarouter.provserver.aaf.sub.type";
90     private static final String AAF_INSTANCE = "org.onap.dmaap.datarouter.provserver.aaf.instance";
91     private static final String AAF_CADI_FEED = "org.onap.dmaap-dr.feed";
92     private static final String AAF_CADI_SUB = "org.onap.dmaap-dr.sub";
93
94     static final String CREATE_PERMISSION = "create";
95     static final String EDIT_PERMISSION = "edit";
96     static final String DELETE_PERMISSION = "delete";
97     static final String PUBLISH_PERMISSION = "publish";
98     static final String SUSPEND_PERMISSION = "suspend";
99     static final String RESTORE_PERMISSION = "restore";
100     static final String SUBSCRIBE_PERMISSION = "subscribe";
101     static final String APPROVE_SUB_PERMISSION = "approveSub";
102
103     static final String FEED_BASECONTENT_TYPE = "application/vnd.dmaap-dr.feed";
104     public static final String FEED_CONTENT_TYPE = "application/vnd.dmaap-dr.feed; version=2.0";
105     public static final String FEEDFULL_CONTENT_TYPE = "application/vnd.dmaap-dr.feed-full; version=2.0";
106     public static final String FEEDLIST_CONTENT_TYPE = "application/vnd.dmaap-dr.feed-list; version=1.0";
107     static final String SUB_BASECONTENT_TYPE = "application/vnd.dmaap-dr.subscription";
108     public static final String SUB_CONTENT_TYPE = "application/vnd.dmaap-dr.subscription; version=2.0";
109     public static final String SUBFULL_CONTENT_TYPE = "application/vnd.dmaap-dr.subscription-full; version=2.0";
110     static final String SUBLIST_CONTENT_TYPE = "application/vnd.dmaap-dr.subscription-list; version=1.0";
111
112
113     //Adding groups functionality, ...1610
114     static final String GROUP_BASECONTENT_TYPE = "application/vnd.dmaap-dr.group";
115     static final String GROUP_CONTENT_TYPE = "application/vnd.dmaap-dr.group; version=2.0";
116     public static final String GROUPFULL_CONTENT_TYPE = "application/vnd.dmaap-dr.group-full; version=2.0";
117     public static final String GROUPLIST_CONTENT_TYPE = "application/vnd.dmaap-dr.fegrouped-list; version=1.0";
118
119
120     public static final String LOGLIST_CONTENT_TYPE = "application/vnd.dmaap-dr.log-list; version=1.0";
121     public static final String PROVFULL_CONTENT_TYPE1 = "application/vnd.dmaap-dr.provfeed-full; version=1.0";
122     public static final String PROVFULL_CONTENT_TYPE2 = "application/vnd.dmaap-dr.provfeed-full; version=2.0";
123     public static final String CERT_ATTRIBUTE = "javax.servlet.request.X509Certificate";
124
125     static final String DB_PROBLEM_MSG = "There has been a problem with the DB.  It is suggested you try the operation again.";
126
127     private static final int DEFAULT_MAX_FEEDS = 10000;
128     private static final int DEFAULT_MAX_SUBS = 100000;
129     private static final int DEFAULT_POKETIMER1 = 5;
130     private static final int DEFAULT_POKETIMER2 = 30;
131     private static final String DEFAULT_DOMAIN = "onap";
132     private static final String DEFAULT_PROVSRVR_NAME = "dmaap-dr-prov";
133     private static final String STATIC_ROUTING_NODES = ""; //Adding new param for static Routing - Rally:US664862-1610
134
135     //Common Errors
136     public static final String MISSING_ON_BEHALF = "Missing X-DMAAP-DR-ON-BEHALF-OF header.";
137     public static final String MISSING_FEED = "Missing or bad feed number.";
138     public static final String POLICY_ENGINE = "Policy Engine disallows access.";
139     public static final String UNAUTHORIZED = "Unauthorized.";
140     public static final String BAD_SUB = "Missing or bad subscription number.";
141     public static final String BAD_JSON = "Badly formed JSON";
142     public static final String BAD_URL = "Bad URL.";
143
144     public static final String API = "/api/";
145     public static final String LOGS = "/logs/";
146     public static final String TEXT_CT = "text/plain";
147     public static final String INGRESS = "/ingress/";
148     public static final String EGRESS = "/egress/";
149     public static final String NETWORK = "/network/";
150     public static final String GROUPID = "groupid";
151     public static final String FEEDID = "feedid";
152     public static final String FEEDIDS = "feedids";
153     public static final String SUBID = "subid";
154     public static final String EVENT_TYPE = "eventType";
155     public static final String OUTPUT_TYPE = "output_type";
156     public static final String START_TIME = "start_time";
157     public static final String END_TIME = "end_time";
158     public static final String REASON_SQL = "reasonSQL";
159
160
161     /**
162      * A boolean to trigger one time "provisioning changed" event on startup
163      */
164     private static boolean startmsgFlag = true;
165     /**
166      * This POD should require SSL connections from clients; pulled from the DB (PROV_REQUIRE_SECURE)
167      */
168     private static boolean requireSecure = true;
169     /**
170      * This POD should require signed, recognized certificates from clients; pulled from the DB (PROV_REQUIRE_CERT)
171      */
172     private static boolean requireCert = true;
173     /**
174      * The set of authorized addresses and networks; pulled from the DB (PROV_AUTH_ADDRESSES)
175      */
176     private static Set<String> authorizedAddressesAndNetworks = new HashSet<>();
177     /**
178      * The set of authorized names; pulled from the DB (PROV_AUTH_SUBJECTS)
179      */
180     private static Set<String> authorizedNames = new HashSet<>();
181     /**
182      * The FQDN of the initially "active" provisioning server in this Data Router ecosystem
183      */
184     private static String initialActivePod;
185     /**
186      * The FQDN of the initially "standby" provisioning server in this Data Router ecosystem
187      */
188     private static String initialStandbyPod;
189     /**
190      * The FQDN of this provisioning server in this Data Router ecosystem
191      */
192     private static String thisPod;
193     /**
194      * "Timer 1" - used to determine when to notify nodes of provisioning changes
195      */
196     private static long pokeTimer1;
197     /**
198      * "Timer 2" - used to determine when to notify nodes of provisioning changes
199      */
200     private static long pokeTimer2;
201     /**
202      * Array of nodes names and/or FQDNs
203      */
204     private static String[] nodes = new String[0];
205     /**
206      * [DATARTR-27] Poke all the DR nodes : Array of nodes names and/or FQDNs
207      */
208     private static String[] drnodes = new String[0];
209     /**
210      * Array of node IP addresses
211      */
212     private static InetAddress[] nodeAddresses = new InetAddress[0];
213     /**
214      * Array of POD IP addresses
215      */
216     private static InetAddress[] podAddresses = new InetAddress[0];
217     /**
218      * The maximum number of feeds allowed; pulled from the DB (PROV_MAXFEED_COUNT)
219      */
220     static int maxFeeds = 0;
221     /**
222      * The maximum number of subscriptions allowed; pulled from the DB (PROV_MAXSUB_COUNT)
223      */
224     static int maxSubs = 0;
225     /**
226      * The current number of feeds in the system
227      */
228     static int activeFeeds = 0;
229     /**
230      * The current number of subscriptions in the system
231      */
232     static int activeSubs = 0;
233
234     /**
235      * The domain used to generate a FQDN from the "bare" node names
236      */
237     private static String provDomain = "web.att.com";
238
239     /**
240      * The standard FQDN of the provisioning server in this Data Router ecosystem
241      */
242     private static String provName = "feeds-drtr.web.att.com";
243
244     /**
245      * The standard FQDN of the ACTIVE_POD provisioning server in this Data Router ecosystem
246      */
247     private static String activeProvName = "feeds-drtr.web.att.com";
248
249     //Adding new param for static Routing - Rally:US664862-1610
250     private static String staticRoutingNodes = STATIC_ROUTING_NODES;
251
252     /**
253      * This logger is used to log provisioning events
254      */
255     protected static EELFLogger eventlogger;
256     /**
257      * This logger is used to log internal events (errors, etc.)
258      */
259     protected static EELFLogger intlogger;
260     /**
261      * Authorizer - interface to the Policy Engine
262      */
263     protected static Authorizer authz;
264     /**
265      * The Synchronizer used to sync active DB to standby one
266      */
267     private static SynchronizerTask synctask = null;
268
269     //Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
270     private InetAddress thishost;
271     private InetAddress loopback;
272     private static Boolean mailSendFlag = false;
273
274     private static final String MAILCONFIG_FILE = "mail.properties";
275     private static Properties mailprops;
276
277     //DMAAP-597 (Tech Dept) REST request source IP auth relaxation to accommodate OOM kubernetes deploy
278     private static String isAddressAuthEnabled = (new DB()).getProperties()
279             .getProperty("org.onap.dmaap.datarouter.provserver.isaddressauthenabled", "false");
280
281     static String isCadiEnabled = (new DB()).getProperties()
282             .getProperty("org.onap.dmaap.datarouter.provserver.cadi.enabled", "false");
283
284     /**
285      * Initialize data common to all the provisioning server servlets.
286      */
287     protected BaseServlet() {
288         if(eventlogger == null) {
289             this.eventlogger = EELFManager.getInstance().getLogger("EventLog");
290         }
291         if(intlogger == null) {
292             this.intlogger = EELFManager.getInstance().getLogger("InternalLog");
293         }
294         if (authz == null) {
295             authz = new ProvAuthorizer(this);
296         }
297         if (startmsgFlag) {
298             startmsgFlag = false;
299             provisioningParametersChanged();
300         }
301         if (synctask == null) {
302             synctask = SynchronizerTask.getSynchronizer();
303         }
304         String name = this.getClass().getName();
305         intlogger.info("PROV0002 Servlet " + name + " started.");
306     }
307
308     @Override
309     public void init(ServletConfig config) throws ServletException {
310         super.init(config);
311         try {
312             thishost = InetAddress.getLocalHost();
313             loopback = InetAddress.getLoopbackAddress();
314         } catch (UnknownHostException e) {
315             intlogger.info("BaseServlet.init: " + e.getMessage(), e);
316         }
317     }
318
319     public static int getIdFromPath(HttpServletRequest req) {
320         String path = req.getPathInfo();
321         if (path == null || path.length() < 2) {
322             return -1;
323         }
324         try {
325             return Integer.parseInt(path.substring(1));
326         } catch (NumberFormatException e) {
327             return -1;
328         }
329     }
330
331     /**
332      * Read the request's input stream and return a JSONObject from it
333      *
334      * @param req the HTTP request
335      * @return the JSONObject, or null if the stream cannot be parsed
336      */
337     JSONObject getJSONfromInput(HttpServletRequest req) {
338         JSONObject jo = null;
339         try {
340             jo = new JSONObject(new JSONTokener(req.getInputStream()));
341             if (intlogger.isDebugEnabled()) {
342                 intlogger.debug("JSON: " + jo.toString());
343             }
344         } catch (Exception e) {
345             intlogger.info("Error reading JSON: " + e);
346         }
347         return jo;
348     }
349
350     /**
351      * This method encrypt/decrypt the key in the JSON passed by user request inside the authorisation header object in request before logging the JSON.
352      *
353      * @param jo-      the JSON passed in http request.
354      * @param maskKey- the key to be masked in the JSON passed.
355      * @param action-  whether to mask the key or unmask it in a JSON passed.
356      * @return the JSONObject, or null if the stream cannot be parsed.
357      */
358     public static JSONObject maskJSON(JSONObject jo, String maskKey, boolean action) {
359         if (!jo.isNull("authorization")) {
360             JSONObject j2 = jo.getJSONObject("authorization");
361             JSONArray ja = j2.getJSONArray("endpoint_ids");
362             for (int i = 0; i < ja.length(); i++) {
363                 if ((!ja.getJSONObject(i).isNull(maskKey))) {
364                     String password = ja.getJSONObject(i).get(maskKey).toString();
365                     try {
366                         if (action) {
367                             ja.getJSONObject(i).put(maskKey, PasswordProcessor.encrypt(password));
368                         } else {
369                             ja.getJSONObject(i).put(maskKey, PasswordProcessor.decrypt(password));
370                         }
371                     } catch (JSONException | GeneralSecurityException e) {
372                         intlogger.info("Error reading JSON while masking: " + e);
373                     }
374                 }
375             }
376         }
377         return jo;
378     }
379
380     /**
381      * Check if the remote host is authorized to perform provisioning. Is the request secure? Is it coming from an
382      * authorized IP address or network (configured via PROV_AUTH_ADDRESSES)? Does it have a valid client certificate
383      * (configured via PROV_AUTH_SUBJECTS)?
384      *
385      * @param request the request
386      * @return an error string, or null if all is OK
387      */
388     String isAuthorizedForProvisioning(HttpServletRequest request) {
389         if (!Boolean.parseBoolean(isAddressAuthEnabled)) {
390             return null;
391         }
392         // Is the request https?
393         if (requireSecure && !request.isSecure()) {
394             return "Request must be made over an HTTPS connection.";
395         }
396         // Is remote IP authorized?
397         String remote = request.getRemoteAddr();
398         try {
399             boolean found = false;
400             InetAddress ip = InetAddress.getByName(remote);
401             for (String addrnet : authorizedAddressesAndNetworks) {
402                 found |= addressMatchesNetwork(ip, addrnet);
403             }
404             if (!found) {
405                 return "Unauthorized address: " + remote;
406             }
407         } catch (UnknownHostException e) {
408             intlogger.error("PROV0051 BaseServlet.isAuthorizedForProvisioning: " + e.getMessage(), e);
409             return "Unauthorized address: " + remote;
410         }
411         // Does remote have a valid certificate?
412         if (requireCert) {
413             X509Certificate[] certs = (X509Certificate[]) request.getAttribute(CERT_ATTRIBUTE);
414             if (certs == null || certs.length == 0) {
415                 return "Client certificate is missing.";
416             }
417             // cert[0] is the client cert
418             // see http://www.proto.research.att.com/java/java7/api/javax/net/ssl/SSLSession.html#getPeerCertificates()
419             String name = certs[0].getSubjectX500Principal().getName();
420             if (!authorizedNames.contains(name)) {
421                 return "No authorized certificate found.";
422             }
423         }
424         // No problems!
425         return null;
426     }
427
428     /**
429      * Check if the remote IP address is authorized to see the /internal URL tree.
430      *
431      * @param request the HTTP request
432      * @return true iff authorized
433      */
434     boolean isAuthorizedForInternal(HttpServletRequest request) {
435         try {
436             if (!Boolean.parseBoolean(isAddressAuthEnabled)) {
437                 return true;
438             }
439             InetAddress ip = InetAddress.getByName(request.getRemoteAddr());
440             for (InetAddress node : getNodeAddresses()) {
441                 if (node != null && ip.equals(node)) {
442                     return true;
443                 }
444             }
445             for (InetAddress pod : getPodAddresses()) {
446                 if (pod != null && ip.equals(pod)) {
447                     return true;
448                 }
449             }
450             if (thishost != null && ip.equals(thishost)) {
451                 return true;
452             }
453             if (loopback != null && ip.equals(loopback)) {
454                 return true;
455             }
456         } catch (UnknownHostException e) {
457             intlogger.error("PROV0052 BaseServlet.isAuthorizedForInternal: " + e.getMessage(), e);
458         }
459         return false;
460     }
461
462     /**
463      * Check if an IP address matches a network address.
464      *
465      * @param ip the IP address
466      * @param s  the network address; a bare IP address may be matched also
467      * @return true if they intersect
468      */
469     private static boolean addressMatchesNetwork(InetAddress ip, String s) {
470         int mlen = -1;
471         int n = s.indexOf("/");
472         if (n >= 0) {
473             mlen = Integer.parseInt(s.substring(n + 1));
474             s = s.substring(0, n);
475         }
476         try {
477             InetAddress i2 = InetAddress.getByName(s);
478             byte[] b1 = ip.getAddress();
479             byte[] b2 = i2.getAddress();
480             if (b1.length != b2.length) {
481                 return false;
482             }
483             if (mlen > 0) {
484                 byte[] masks = {
485                         (byte) 0x00, (byte) 0x80, (byte) 0xC0, (byte) 0xE0,
486                         (byte) 0xF0, (byte) 0xF8, (byte) 0xFC, (byte) 0xFE
487                 };
488                 byte mask = masks[mlen % 8];
489                 for (n = mlen / 8; n < b1.length; n++) {
490                     b1[n] &= mask;
491                     b2[n] &= mask;
492                     mask = 0;
493                 }
494             }
495             for (n = 0; n < b1.length; n++) {
496                 if (b1[n] != b2[n]) {
497                     return false;
498                 }
499             }
500         } catch (UnknownHostException e) {
501             intlogger.error("PROV0053 BaseServlet.addressMatchesNetwork: " + e.getMessage(), e);
502             return false;
503         }
504         return true;
505     }
506
507     /**
508      * Something has changed in the provisioning data. Start the timers that will cause the pre-packaged JSON string to
509      * be regenerated, and cause nodes and the other provisioning server to be notified.
510      */
511     static void provisioningDataChanged() {
512         long now = System.currentTimeMillis();
513         Poker p = Poker.getPoker();
514         p.setTimers(now + (pokeTimer1 * 1000L), now + (pokeTimer2 * 1000L));
515     }
516
517     /**
518      * Something in the parameters has changed, reload all parameters from the DB.
519      */
520     static void provisioningParametersChanged() {
521         Map<String, String> map = Parameters.getParameters();
522         requireSecure = getBoolean(map, Parameters.PROV_REQUIRE_SECURE);
523         requireCert = getBoolean(map, Parameters.PROV_REQUIRE_CERT);
524         authorizedAddressesAndNetworks = getSet(map, Parameters.PROV_AUTH_ADDRESSES);
525         authorizedNames = getSet(map, Parameters.PROV_AUTH_SUBJECTS);
526         nodes = getSet(map, Parameters.NODES).toArray(new String[0]);
527         maxFeeds = getInt(map, Parameters.PROV_MAXFEED_COUNT, DEFAULT_MAX_FEEDS);
528         maxSubs = getInt(map, Parameters.PROV_MAXSUB_COUNT, DEFAULT_MAX_SUBS);
529         pokeTimer1 = getInt(map, Parameters.PROV_POKETIMER1, DEFAULT_POKETIMER1);
530         pokeTimer2 = getInt(map, Parameters.PROV_POKETIMER2, DEFAULT_POKETIMER2);
531         /**
532          * The domain used to generate a FQDN from the "bare" node names
533          */
534         provDomain = getString(map, Parameters.PROV_DOMAIN, DEFAULT_DOMAIN);
535         provName = getString(map, Parameters.PROV_NAME, DEFAULT_PROVSRVR_NAME);
536         activeProvName = getString(map, Parameters.PROV_ACTIVE_NAME, provName);
537         initialActivePod = getString(map, Parameters.ACTIVE_POD, "");
538         initialStandbyPod = getString(map, Parameters.STANDBY_POD, "");
539         staticRoutingNodes = getString(map, Parameters.STATIC_ROUTING_NODES,
540                 ""); //Adding new param for static Routing - Rally:US664862-1610
541         activeFeeds = Feed.countActiveFeeds();
542         activeSubs = Subscription.countActiveSubscriptions();
543         try {
544             thisPod = InetAddress.getLocalHost().getHostName();
545         } catch (UnknownHostException e) {
546             thisPod = "";
547             intlogger.warn("PROV0014 Cannot determine the name of this provisioning server.", e);
548         }
549
550         // Normalize the nodes, and fill in nodeAddresses
551         InetAddress[] na = new InetAddress[nodes.length];
552         for (int i = 0; i < nodes.length; i++) {
553             try {
554                 na[i] = InetAddress.getByName(nodes[i]);
555                 intlogger.debug("PROV0003 DNS lookup: " + nodes[i] + " => " + na[i].toString());
556             } catch (UnknownHostException e) {
557                 na[i] = null;
558                 intlogger.warn("PROV0004 Cannot lookup " + nodes[i] + ": " + e.getMessage(), e);
559             }
560         }
561
562         //[DATARTR-27] Poke all the DR nodes: assigning DR Nodes
563         drnodes = nodes.clone();
564
565         //Reset Nodes arr after - removing static routing Nodes, Rally Userstory - US664862 .
566         List<String> filterNodes = new ArrayList<>();
567         for (String node : nodes) {
568             if (!staticRoutingNodes.contains(node)) {
569                 filterNodes.add(node);
570             }
571         }
572         nodes = filterNodes.toArray(new String[0]);
573
574         nodeAddresses = na;
575         NodeClass.setNodes(nodes);        // update NODES table
576
577         // Normalize the PODs, and fill in podAddresses
578         String[] pods = getPods();
579         na = new InetAddress[pods.length];
580         for (int i = 0; i < pods.length; i++) {
581             try {
582                 na[i] = InetAddress.getByName(pods[i]);
583                 intlogger.debug("PROV0003 DNS lookup: " + pods[i] + " => " + na[i].toString());
584             } catch (UnknownHostException e) {
585                 na[i] = null;
586                 intlogger.warn("PROV0004 Cannot lookup " + pods[i] + ": " + e.getMessage(), e);
587             }
588         }
589         podAddresses = na;
590
591         // Update ThrottleFilter
592         ThrottleFilter.configure();
593
594         // Check if we are active or standby POD
595         if (!isInitialActivePOD() && !isInitialStandbyPOD()) {
596             intlogger.warn("PROV0015 This machine is neither the active nor the standby POD.");
597         }
598     }
599
600
601     /**
602      * Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047. Load mail properties.
603      *
604      * @author vs215k
605      **/
606     private void loadMailProperties() {
607         if (mailprops == null) {
608             mailprops = new Properties();
609             try (InputStream inStream = getClass().getClassLoader().getResourceAsStream(MAILCONFIG_FILE)) {
610                 mailprops.load(inStream);
611             } catch (IOException e) {
612                 intlogger.error("PROV9003 Opening properties: " + e.getMessage(), e);
613                 System.exit(1);
614             }
615         }
616     }
617
618
619     /**
620      * Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
621      *
622      * @param email - list of email ids to notify if HTTP relexcation is enabled.
623      * @author vs215k
624      **/
625     private void notifyPSTeam(String email) {
626         loadMailProperties(); //Load HTTPS Relex mail properties.
627         String[] emails = email.split(Pattern.quote("|"));
628
629         Properties mailproperties = new Properties();
630         mailproperties.put("mail.smtp.host", mailprops.get("com.att.dmaap.datarouter.mail.server"));
631         mailproperties.put("mail.transport.protocol", mailprops.get("com.att.dmaap.datarouter.mail.protocol"));
632
633         Session session = Session.getDefaultInstance(mailproperties, null);
634         Multipart mp = new MimeMultipart();
635         MimeBodyPart htmlPart = new MimeBodyPart();
636
637         try {
638
639             Message msg = new MimeMessage(session);
640             msg.setFrom(new InternetAddress(mailprops.get("com.att.dmaap.datarouter.mail.from").toString()));
641
642             InternetAddress[] addressTo = new InternetAddress[emails.length];
643             for (int x = 0; x < emails.length; x++) {
644                 addressTo[x] = new InternetAddress(emails[x]);
645             }
646
647             msg.addRecipients(Message.RecipientType.TO, addressTo);
648             msg.setSubject(mailprops.get("com.att.dmaap.datarouter.mail.subject").toString());
649             htmlPart.setContent(mailprops.get("com.att.dmaap.datarouter.mail.body").toString()
650                     .replace("[SERVER]", InetAddress.getLocalHost().getHostName()), "text/html");
651             mp.addBodyPart(htmlPart);
652             msg.setContent(mp);
653
654             intlogger.info(mailprops.get("com.att.dmaap.datarouter.mail.body").toString()
655                     .replace("[SERVER]", InetAddress.getLocalHost().getHostName()));
656
657             Transport.send(msg);
658             intlogger.info("HTTPS relaxation mail is sent to - : " + email);
659
660         } catch (MessagingException e) {
661             intlogger.error("Invalid email address, unable to send https relaxation mail to - : " + email, e);
662         } catch (UnknownHostException uhe) {
663             intlogger.error("UnknownHostException", uhe);
664         }
665     }
666
667     public static String getProvName() {
668         return provName;
669     }
670
671     public static String getActiveProvName() {
672         return activeProvName;
673     }
674
675     /**
676      * Get an array of all node names in the DR network.
677      *
678      * @return an array of Strings
679      */
680     public static String[] getNodes() {
681         return nodes;
682     }
683
684     /**
685      * [DATARTR-27] Poke all the DR nodes
686      * Get an array of all node names in the DR network.
687      *
688      * @return an array of Strings
689      */
690     public static String[] getDRNodes() {
691         return drnodes;
692     }
693
694     /**
695      * Get an array of all node InetAddresses in the DR network.
696      *
697      * @return an array of InetAddresses
698      */
699     public static InetAddress[] getNodeAddresses() {
700         return nodeAddresses;
701     }
702
703     /**
704      * Get an array of all POD names in the DR network.
705      *
706      * @return an array of Strings
707      */
708     public static String[] getPods() {
709         return new String[]{initialActivePod, initialStandbyPod};
710     }
711
712     /**
713      * Get an array of all POD InetAddresses in the DR network.
714      *
715      * @return an array of InetAddresses
716      */
717     private static InetAddress[] getPodAddresses() {
718         return podAddresses;
719     }
720
721     /**
722      * Gets the FQDN of the initially ACTIVE_POD provisioning server (POD). Note: this used to be called isActivePOD(),
723      * however, that is a misnomer, as the active status could shift to the standby POD without these parameters
724      * changing.  Hence, the function names have been changed to more accurately reflect their purpose.
725      *
726      * @return the FQDN
727      */
728     public static boolean isInitialActivePOD() {
729         return thisPod.equals(initialActivePod);
730     }
731
732     /**
733      * Gets the FQDN of the initially STANDBY_POD provisioning server (POD). Note: this used to be called isStandbyPOD(),
734      * however, that is a misnomer, as the standby status could shift to the active POD without these parameters
735      * changing.  Hence, the function names have been changed to more accurately reflect their purpose.
736      *
737      * @return the FQDN
738      */
739     public static boolean isInitialStandbyPOD() {
740         return thisPod.equals(initialStandbyPod);
741     }
742
743     /**
744      * INSERT an {@link Insertable} bean into the database.
745      *
746      * @param bean the bean representing a row to insert
747      * @return true if the INSERT was successful
748      */
749     protected boolean doInsert(Insertable bean) {
750         boolean rv;
751         DB db = new DB();
752         Connection conn = null;
753         try {
754             conn = db.getConnection();
755             rv = bean.doInsert(conn);
756         } catch (SQLException e) {
757             rv = false;
758             intlogger.warn("PROV0005 doInsert: " + e.getMessage(), e);
759         } finally {
760             if (conn != null) {
761                 db.release(conn);
762             }
763         }
764         return rv;
765     }
766
767     /**
768      * UPDATE an {@link Updateable} bean in the database.
769      *
770      * @param bean the bean representing a row to update
771      * @return true if the UPDATE was successful
772      */
773     protected boolean doUpdate(Updateable bean) {
774         boolean rv;
775         DB db = new DB();
776         Connection conn = null;
777         try {
778             conn = db.getConnection();
779             rv = bean.doUpdate(conn);
780         } catch (SQLException e) {
781             rv = false;
782             intlogger.warn("PROV0006 doUpdate: " + e.getMessage(), e);
783         } finally {
784             if (conn != null) {
785                 db.release(conn);
786             }
787         }
788         return rv;
789     }
790
791     /**
792      * DELETE an {@link Deleteable} bean from the database.
793      *
794      * @param bean the bean representing a row to delete
795      * @return true if the DELETE was successful
796      */
797     protected boolean doDelete(Deleteable bean) {
798         boolean rv;
799         DB db = new DB();
800         Connection conn = null;
801         try {
802             conn = db.getConnection();
803             rv = bean.doDelete(conn);
804         } catch (SQLException e) {
805             rv = false;
806             intlogger.warn("PROV0007 doDelete: " + e.getMessage(), e);
807         } finally {
808             if (conn != null) {
809                 db.release(conn);
810             }
811         }
812         return rv;
813     }
814
815     private static boolean getBoolean(Map<String, String> map, String name) {
816         String s = map.get(name);
817         return (s != null) && "true".equalsIgnoreCase(s);
818     }
819
820     private static String getString(Map<String, String> map, String name, String dflt) {
821         String s = map.get(name);
822         return (s != null) ? s : dflt;
823     }
824
825     private static int getInt(Map<String, String> map, String name, int dflt) {
826         try {
827             String s = map.get(name);
828             return Integer.parseInt(s);
829         } catch (NumberFormatException e) {
830             return dflt;
831         }
832     }
833
834     private static Set<String> getSet(Map<String, String> map, String name) {
835         Set<String> set = new HashSet<>();
836         String s = map.get(name);
837         if (s != null) {
838             String[] pp = s.split("\\|");
839             if (pp != null) {
840                 for (String t : pp) {
841                     String t2 = t.trim();
842                     if (t2.length() > 0) {
843                         set.add(t2);
844                     }
845                 }
846             }
847         }
848         return set;
849     }
850
851     /**
852      * A class used to encapsulate a Content-type header, separating out the "version" attribute (which defaults to
853      * "1.0" if missing).
854      */
855     public class ContentHeader {
856
857         private String type = "";
858         private Map<String, String> map = new HashMap<>();
859
860         ContentHeader() {
861             this("", "1.0");
862         }
863
864         ContentHeader(String t, String v) {
865             type = t.trim();
866             map.put("version", v);
867         }
868
869         public String getType() {
870             return type;
871         }
872
873         public String getAttribute(String key) {
874             String s = map.get(key);
875             if (s == null) {
876                 s = "";
877             }
878             return s;
879         }
880     }
881
882     /**
883      * Get the ContentHeader from an HTTP request.
884      *
885      * @param req the request
886      * @return the header, encapsulated in a ContentHeader object
887      */
888     ContentHeader getContentHeader(HttpServletRequest req) {
889         ContentHeader ch = new ContentHeader();
890         String s = req.getHeader("Content-Type");
891         if (s != null) {
892             String[] pp = s.split(";");
893             ch.type = pp[0].trim();
894             for (int i = 1; i < pp.length; i++) {
895                 int ix = pp[i].indexOf('=');
896                 if (ix > 0) {
897                     String k = pp[i].substring(0, ix).trim();
898                     String v = pp[i].substring(ix + 1).trim();
899                     ch.map.put(k, v);
900                 } else {
901                     ch.map.put(pp[i].trim(), "");
902                 }
903             }
904         }
905         return ch;
906     }
907
908     // Methods for the Policy Engine classes - ProvDataProvider interface
909     @Override
910     public String getFeedOwner(String feedId) {
911         try {
912             int n = Integer.parseInt(feedId);
913             Feed f = Feed.getFeedById(n);
914             if (f != null) {
915                 return f.getPublisher();
916             }
917         } catch (NumberFormatException e) {
918             // ignore
919         }
920         return null;
921     }
922
923     @Override
924     public String getFeedClassification(String feedId) {
925         try {
926             int n = Integer.parseInt(feedId);
927             Feed f = Feed.getFeedById(n);
928             if (f != null) {
929                 return f.getAuthorization().getClassification();
930             }
931         } catch (NumberFormatException e) {
932             // ignore
933         }
934         return null;
935     }
936
937     @Override
938     public String getSubscriptionOwner(String subId) {
939         try {
940             int n = Integer.parseInt(subId);
941             Subscription s = Subscription.getSubscriptionById(n);
942             if (s != null) {
943                 return s.getSubscriber();
944             }
945         } catch (NumberFormatException e) {
946             // ignore
947         }
948         return null;
949     }
950
951     /*
952      * @Method - isUserMemberOfGroup - Rally:US708115
953      * @Params - group object and user to check if exists in given group
954      * @return - boolean value /true/false
955      */
956     private boolean isUserMemberOfGroup(Group group, String user) {
957
958         String groupDetails = group.getMembers().replace("]", "").replace("[", "");
959         String[] s = groupDetails.split("},");
960
961         for (String value : s) {
962             JSONObject jsonObj;
963             try {
964                 jsonObj = new JSONObject(value + "}");
965                 if (jsonObj.get("id").equals(user)) {
966                     return true;
967                 }
968             } catch (JSONException e) {
969                 intlogger.error("JSONException: " + e.getMessage(), e);
970             }
971         }
972         return false;
973
974     }
975
976     /*
977      * @Method - getGroupByFeedGroupId- Rally:US708115
978      * @Params - User to check in group and feedid which is assigned the group.
979      * @return - string value grupid/null
980      */
981     @Override
982     public String getGroupByFeedGroupId(String owner, String feedId) {
983         try {
984             int n = Integer.parseInt(feedId);
985             Feed f = Feed.getFeedById(n);
986             if (f != null) {
987                 int groupid = f.getGroupid();
988                 if (groupid > 0) {
989                     Group group = Group.getGroupById(groupid);
990                     assert group != null;
991                     if (isUserMemberOfGroup(group, owner)) {
992                         return group.getAuthid();
993                     }
994                 }
995             }
996         } catch (NumberFormatException e) {
997             // ignore
998         }
999         return null;
1000     }
1001
1002     /*
1003      * @Method - getGroupBySubGroupId - Rally:US708115
1004      * @Params - User to check in group and subid which is assigned the group.
1005      * @return - string value grupid/null
1006      */
1007     @Override
1008     public String getGroupBySubGroupId(String owner, String subId) {
1009         try {
1010             int n = Integer.parseInt(subId);
1011             Subscription s = Subscription.getSubscriptionById(n);
1012             if (s != null) {
1013                 int groupid = s.getGroupid();
1014                 if (groupid > 0) {
1015                     Group group = Group.getGroupById(groupid);
1016                     assert group != null;
1017                     if (isUserMemberOfGroup(group, owner)) {
1018                         return group.getAuthid();
1019                     }
1020                 }
1021             }
1022         } catch (NumberFormatException e) {
1023             // ignore
1024         }
1025         return null;
1026     }
1027
1028     /*
1029      * @Method - setIpFqdnRequestIDandInvocationIDForEelf
1030      * @Params - method, prints method name in EELF log.
1031      * @Params- Req, Request used to get RequestId and InvocationId
1032      */
1033     void setIpFqdnRequestIDandInvocationIDForEelf(String method, HttpServletRequest req) {
1034         setIpFqdnForEelf(method);
1035         setMDC(req, "X-ONAP-RequestID", MDC_KEY_REQUEST_ID);
1036         setMDC(req, "X-InvocationID", "InvocationId");
1037     }
1038
1039     private void setMDC(HttpServletRequest req, String headerName, String keyName) {
1040         String mdcId = req.getHeader(headerName);
1041         if (StringUtils.isBlank(mdcId)) {
1042             mdcId = UUID.randomUUID().toString();
1043         }
1044         MDC.put(keyName, mdcId);
1045     }
1046
1047     /*
1048      * @Method - setIpFqdnRequestIdForEelf - Rally:US664892
1049      * @Params - method, prints method name in EELF log.
1050      */
1051     void setIpFqdnForEelf(String method) {
1052         MDC.clear();
1053         MDC.put(MDC_SERVICE_NAME, method);
1054         try {
1055             MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName());
1056             MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
1057         } catch (Exception e) {
1058             intlogger.error("Exception: " + e.getMessage(), e);
1059         }
1060
1061     }
1062
1063     /*
1064      * AAF changes: TDP EPIC US# 307413
1065      * @Method - getFeedPermission - Forming permission string for feed part to check AAF access in CADI Framework
1066      * @Params - aafInstance Passing aafInstance as it's used in permission string
1067      * @Params - userAction Passing CONST values to set different actions in permission string
1068      */
1069     String getFeedPermission(String aafInstance, String userAction) {
1070         try {
1071             Properties props = (new DB()).getProperties();
1072             String type = props.getProperty(AAF_CADI_FEED_TYPE, AAF_CADI_FEED);
1073             String action;
1074             switch (userAction) {
1075                 case CREATE_PERMISSION:
1076                     action = CREATE_PERMISSION;
1077                     break;
1078                 case EDIT_PERMISSION:
1079                     action = EDIT_PERMISSION;
1080                     break;
1081                 case DELETE_PERMISSION:
1082                     action = DELETE_PERMISSION;
1083                     break;
1084                 case PUBLISH_PERMISSION:
1085                     action = PUBLISH_PERMISSION;
1086                     break;
1087                 case SUSPEND_PERMISSION:
1088                     action = SUSPEND_PERMISSION;
1089                     break;
1090                 case RESTORE_PERMISSION:
1091                     action = RESTORE_PERMISSION;
1092                     break;
1093                 default:
1094                     action = "*";
1095             }
1096             if (aafInstance == null || "".equals(aafInstance)) {
1097                 aafInstance = props.getProperty(AAF_INSTANCE, "org.onap.dmaap-dr.NoInstanceDefined");
1098             }
1099             return type + "|" + aafInstance + "|" + action;
1100         } catch (Exception e) {
1101             intlogger.error("PROV7005 BaseServlet.getFeedPermission: " + e.getMessage(), e);
1102         }
1103         return null;
1104     }
1105
1106     /*
1107      * AAF changes: TDP EPIC US# 307413
1108      * @Method - getSubscriberPermission - Forming permission string for subscription part to check AAF access in CADI Framework
1109      * @Params - aafInstance Passing aafInstance as it's used in permission string
1110      * @Params - userAction Passing CONST values to set different actions in permission string
1111      */
1112     String getSubscriberPermission(String aafInstance, String userAction) {
1113         try {
1114             Properties props = (new DB()).getProperties();
1115             String type = props.getProperty(AAF_CADI_SUB_TYPE, AAF_CADI_SUB);
1116             String action;
1117             switch (userAction) {
1118                 case SUBSCRIBE_PERMISSION:
1119                     action = SUBSCRIBE_PERMISSION;
1120                     type = props.getProperty(AAF_CADI_FEED_TYPE, AAF_CADI_FEED);
1121                     break;
1122                 case EDIT_PERMISSION:
1123                     action = EDIT_PERMISSION;
1124                     break;
1125                 case DELETE_PERMISSION:
1126                     action = DELETE_PERMISSION;
1127                     break;
1128                 case RESTORE_PERMISSION:
1129                     action = RESTORE_PERMISSION;
1130                     break;
1131                 case SUSPEND_PERMISSION:
1132                     action = SUSPEND_PERMISSION;
1133                     break;
1134                 case PUBLISH_PERMISSION:
1135                     action = PUBLISH_PERMISSION;
1136                     break;
1137                 case APPROVE_SUB_PERMISSION:
1138                     action = APPROVE_SUB_PERMISSION;
1139                     type = props.getProperty(AAF_CADI_FEED_TYPE, AAF_CADI_FEED);
1140                     break;
1141                 default:
1142                     action = "*";
1143             }
1144             if (aafInstance == null || "".equals(aafInstance)) {
1145                 aafInstance = props.getProperty(AAF_INSTANCE, "org.onap.dmaap-dr.NoInstanceDefined");
1146             }
1147             return type + "|" + aafInstance + "|" + action;
1148         } catch (Exception e) {
1149             intlogger.error("PROV7005 BaseServlet.getSubscriberPermission: " + e.getMessage(), e);
1150         }
1151         return null;
1152     }
1153 }