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