Add optional API for PM Mapper
[dmaap/datarouter.git] / datarouter-node / src / main / java / org / onap / dmaap / datarouter / node / NodeConfig.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.node;
26
27 import java.io.File;
28 import java.util.HashSet;
29 import java.util.Hashtable;
30 import java.util.Vector;
31
32 /**
33  * Processed configuration for this node.
34  * <p>
35  * The NodeConfig represents a processed configuration from the Data Router provisioning server.  Each time
36  * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
37  * discarded.
38  */
39 public class NodeConfig {
40
41     /**
42      * Raw configuration entry for a data router node
43      */
44     public static class ProvNode {
45
46         private String cname;
47
48         /**
49          * Construct a node configuration entry.
50          *
51          * @param cname The cname of the node.
52          */
53         public ProvNode(String cname) {
54             this.cname = cname;
55         }
56
57         /**
58          * Get the cname of the node
59          */
60         public String getCName() {
61             return (cname);
62         }
63     }
64
65     /**
66      * Raw configuration entry for a provisioning parameter
67      */
68     public static class ProvParam {
69
70         private String name;
71         private String value;
72
73         /**
74          * Construct a provisioning parameter configuration entry.
75          *
76          * @param name The name of the parameter.
77          * @param value The value of the parameter.
78          */
79         public ProvParam(String name, String value) {
80             this.name = name;
81             this.value = value;
82         }
83
84         /**
85          * Get the name of the parameter.
86          */
87         public String getName() {
88             return (name);
89         }
90
91         /**
92          * Get the value of the parameter.
93          */
94         public String getValue() {
95             return (value);
96         }
97     }
98
99     /**
100      * Raw configuration entry for a data feed.
101      */
102     public static class ProvFeed {
103
104         private String id;
105         private String logdata;
106         private String status;
107
108         /**
109          * Construct a feed configuration entry.
110          *
111          * @param id The feed ID of the entry.
112          * @param logdata String for log entries about the entry.
113          * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
114          * null if it is valid.
115          */
116         public ProvFeed(String id, String logdata, String status) {
117             this.id = id;
118             this.logdata = logdata;
119             this.status = status;
120         }
121
122         /**
123          * Get the feed id of the data feed.
124          */
125         public String getId() {
126             return (id);
127         }
128
129         /**
130          * Get the log data of the data feed.
131          */
132         public String getLogData() {
133             return (logdata);
134         }
135
136         /**
137          * Get the status of the data feed.
138          */
139         public String getStatus() {
140             return (status);
141         }
142     }
143
144     /**
145      * Raw configuration entry for a feed user.
146      */
147     public static class ProvFeedUser {
148
149         private String feedid;
150         private String user;
151         private String credentials;
152
153         /**
154          * Construct a feed user configuration entry
155          *
156          * @param feedid The feed id.
157          * @param user The user that will publish to the feed.
158          * @param credentials The Authorization header the user will use to publish.
159          */
160         public ProvFeedUser(String feedid, String user, String credentials) {
161             this.feedid = feedid;
162             this.user = user;
163             this.credentials = credentials;
164         }
165
166         /**
167          * Get the feed id of the feed user.
168          */
169         public String getFeedId() {
170             return (feedid);
171         }
172
173         /**
174          * Get the user for the feed user.
175          */
176         public String getUser() {
177             return (user);
178         }
179
180         /**
181          * Get the credentials for the feed user.
182          */
183         public String getCredentials() {
184             return (credentials);
185         }
186     }
187
188     /**
189      * Raw configuration entry for a feed subnet
190      */
191     public static class ProvFeedSubnet {
192
193         private String feedid;
194         private String cidr;
195
196         /**
197          * Construct a feed subnet configuration entry
198          *
199          * @param feedid The feed ID
200          * @param cidr The CIDR allowed to publish to the feed.
201          */
202         public ProvFeedSubnet(String feedid, String cidr) {
203             this.feedid = feedid;
204             this.cidr = cidr;
205         }
206
207         /**
208          * Get the feed id of the feed subnet.
209          */
210         public String getFeedId() {
211             return (feedid);
212         }
213
214         /**
215          * Get the CIDR of the feed subnet.
216          */
217         public String getCidr() {
218             return (cidr);
219         }
220     }
221
222     /**
223      * Raw configuration entry for a subscription
224      */
225     public static class ProvSubscription {
226
227         private String subid;
228         private String feedid;
229         private String url;
230         private String authuser;
231         private String credentials;
232         private boolean metaonly;
233         private boolean use100;
234         private boolean privilegedSubscriber;
235
236         /**
237          * Construct a subscription configuration entry
238          *
239          * @param subid The subscription ID
240          * @param feedid The feed ID
241          * @param url The base delivery URL (not including the fileid)
242          * @param authuser The user in the credentials used to deliver
243          * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
244          * Authorization header.
245          * @param metaonly Is this a meta data only subscription?
246          * @param use100 Should we send Expect: 100-continue?
247          * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
248          */
249         public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
250                 boolean metaonly, boolean use100, boolean privilegedSubscriber) {
251             this.subid = subid;
252             this.feedid = feedid;
253             this.url = url;
254             this.authuser = authuser;
255             this.credentials = credentials;
256             this.metaonly = metaonly;
257             this.use100 = use100;
258             this.privilegedSubscriber = privilegedSubscriber;
259         }
260
261         /**
262          * Get the subscription ID
263          */
264         public String getSubId() {
265             return (subid);
266         }
267
268         /**
269          * Get the feed ID
270          */
271         public String getFeedId() {
272             return (feedid);
273         }
274
275         /**
276          * Get the delivery URL
277          */
278         public String getURL() {
279             return (url);
280         }
281
282         /**
283          * Get the user
284          */
285         public String getAuthUser() {
286             return (authuser);
287         }
288
289         /**
290          * Get the delivery credentials
291          */
292         public String getCredentials() {
293             return (credentials);
294         }
295
296         /**
297          * Is this a meta data only subscription?
298          */
299         public boolean isMetaDataOnly() {
300             return (metaonly);
301         }
302
303         /**
304          * Should we send Expect: 100-continue?
305          */
306         public boolean isUsing100() {
307             return (use100);
308         }
309
310         /**
311          * Can we wait to receive a delete file call before deleting file
312          */
313         public boolean isPrivilegedSubscriber() {
314             return (privilegedSubscriber);
315         }
316     }
317
318     /**
319      * Raw configuration entry for controlled ingress to the data router node
320      */
321     public static class ProvForceIngress {
322
323         private String feedid;
324         private String subnet;
325         private String user;
326         private String[] nodes;
327
328         /**
329          * Construct a forced ingress configuration entry
330          *
331          * @param feedid The feed ID that this entry applies to
332          * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
333          * publisher IP addresses
334          * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
335          * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
336          */
337         public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
338             this.feedid = feedid;
339             this.subnet = subnet;
340             this.user = user;
341             this.nodes = nodes;
342         }
343
344         /**
345          * Get the feed ID
346          */
347         public String getFeedId() {
348             return (feedid);
349         }
350
351         /**
352          * Get the subnet
353          */
354         public String getSubnet() {
355             return (subnet);
356         }
357
358         /**
359          * Get the user
360          */
361         public String getUser() {
362             return (user);
363         }
364
365         /**
366          * Get the node
367          */
368         public String[] getNodes() {
369             return (nodes);
370         }
371     }
372
373     /**
374      * Raw configuration entry for controlled egress from the data router
375      */
376     public static class ProvForceEgress {
377
378         private String subid;
379         private String node;
380
381         /**
382          * Construct a forced egress configuration entry
383          *
384          * @param subid The subscription ID the subscription with forced egress
385          * @param node The node handling deliveries for this subscription
386          */
387         public ProvForceEgress(String subid, String node) {
388             this.subid = subid;
389             this.node = node;
390         }
391
392         /**
393          * Get the subscription ID
394          */
395         public String getSubId() {
396             return (subid);
397         }
398
399         /**
400          * Get the node
401          */
402         public String getNode() {
403             return (node);
404         }
405     }
406
407     /**
408      * Raw configuration entry for routing within the data router network
409      */
410     public static class ProvHop {
411
412         private String from;
413         private String to;
414         private String via;
415
416         /**
417          * A human readable description of this entry
418          */
419         public String toString() {
420             return ("Hop " + from + "->" + to + " via " + via);
421         }
422
423         /**
424          * Construct a hop entry
425          *
426          * @param from The FQDN of the node with the data to be delivered
427          * @param to The FQDN of the node that will deliver to the subscriber
428          * @param via The FQDN of the node where the from node should send the data
429          */
430         public ProvHop(String from, String to, String via) {
431             this.from = from;
432             this.to = to;
433             this.via = via;
434         }
435
436         /**
437          * Get the from node
438          */
439         public String getFrom() {
440             return (from);
441         }
442
443         /**
444          * Get the to node
445          */
446         public String getTo() {
447             return (to);
448         }
449
450         /**
451          * Get the next intermediate node
452          */
453         public String getVia() {
454             return (via);
455         }
456     }
457
458     private static class Redirection {
459
460         SubnetMatcher snm;
461         String user;
462         String[] nodes;
463     }
464
465     private static class Feed {
466
467         String loginfo;
468         String status;
469         SubnetMatcher[] subnets;
470         Hashtable<String, String> authusers = new Hashtable<String, String>();
471         Redirection[] redirections;
472         Target[] targets;
473     }
474
475     private Hashtable<String, String> params = new Hashtable<>();
476     private Hashtable<String, Feed> feeds = new Hashtable<>();
477     private Hashtable<String, DestInfo> nodeinfo = new Hashtable<>();
478     private Hashtable<String, DestInfo> subinfo = new Hashtable<>();
479     private Hashtable<String, IsFrom> nodes = new Hashtable<>();
480     private Hashtable<String, ProvSubscription> provSubscriptions = new Hashtable<>();
481     private String myname;
482     private String myauth;
483     private DestInfo[] alldests;
484     private int rrcntr;
485
486     /**
487      * Process the raw provisioning data to configure this node
488      *
489      * @param pd The parsed provisioning data
490      * @param myname My name as seen by external systems
491      * @param spooldir The directory where temporary files live
492      * @param port The port number for URLs
493      * @param nodeauthkey The keying string used to generate node authentication credentials
494      */
495     public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
496         this.myname = myname;
497         for (ProvParam p : pd.getParams()) {
498             params.put(p.getName(), p.getValue());
499         }
500         Vector<DestInfo> destInfos = new Vector<>();
501         myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
502         for (ProvNode pn : pd.getNodes()) {
503             String cn = pn.getCName();
504             if (nodeinfo.get(cn) != null) {
505                 continue;
506             }
507             String auth = NodeUtils.getNodeAuthHdr(cn, nodeauthkey);
508             DestInfo di = new DestInfo("n:" + cn, spooldir + "/n/" + cn, null, "n2n-" + cn,
509                     "https://" + cn + ":" + port + "/internal/publish", cn, myauth, false, true, false);
510             (new File(di.getSpool())).mkdirs();
511             destInfos.add(di);
512             nodeinfo.put(cn, di);
513             nodes.put(auth, new IsFrom(cn));
514         }
515         PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[nodeinfo.size()]), pd.getHops());
516         Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<String, Vector<Redirection>>();
517         for (ProvForceIngress pfi : pd.getForceIngress()) {
518             Vector<Redirection> v = rdtab.get(pfi.getFeedId());
519             if (v == null) {
520                 v = new Vector<Redirection>();
521                 rdtab.put(pfi.getFeedId(), v);
522             }
523             Redirection r = new Redirection();
524             if (pfi.getSubnet() != null) {
525                 r.snm = new SubnetMatcher(pfi.getSubnet());
526             }
527             r.user = pfi.getUser();
528             r.nodes = pfi.getNodes();
529             v.add(r);
530         }
531         Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<String, Hashtable<String, String>>();
532         for (ProvFeedUser pfu : pd.getFeedUsers()) {
533             Hashtable<String, String> t = pfutab.get(pfu.getFeedId());
534             if (t == null) {
535                 t = new Hashtable<String, String>();
536                 pfutab.put(pfu.getFeedId(), t);
537             }
538             t.put(pfu.getCredentials(), pfu.getUser());
539         }
540         Hashtable<String, String> egrtab = new Hashtable<String, String>();
541         for (ProvForceEgress pfe : pd.getForceEgress()) {
542             if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
543                 continue;
544             }
545             egrtab.put(pfe.getSubId(), pfe.getNode());
546         }
547         Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<>();
548         for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
549             Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
550             if (v == null) {
551                 v = new Vector<SubnetMatcher>();
552                 pfstab.put(pfs.getFeedId(), v);
553             }
554             v.add(new SubnetMatcher(pfs.getCidr()));
555         }
556         Hashtable<String, StringBuffer> feedTargets = new Hashtable<>();
557         HashSet<String> allfeeds = new HashSet<>();
558         for (ProvFeed pfx : pd.getFeeds()) {
559             if (pfx.getStatus() == null) {
560                 allfeeds.add(pfx.getId());
561             }
562         }
563         for (ProvSubscription provSubscription : pd.getSubscriptions()) {
564             String subId = provSubscription.getSubId();
565             String feedId = provSubscription.getFeedId();
566             if (!allfeeds.contains(feedId)) {
567                 continue;
568             }
569             if (subinfo.get(subId) != null) {
570                 continue;
571             }
572             int sididx = 999;
573             try {
574                 sididx = Integer.parseInt(subId);
575                 sididx -= sididx % 100;
576             } catch (Exception e) {
577             }
578             String subscriptionDirectory = sididx + "/" + subId;
579             DestInfo destinationInfo = new DestInfo("s:" + subId,
580                     spooldir + "/s/" + subscriptionDirectory, provSubscription);
581             (new File(destinationInfo.getSpool())).mkdirs();
582             destInfos.add(destinationInfo);
583             provSubscriptions.put(subId, provSubscription);
584             subinfo.put(subId, destinationInfo);
585             String egr = egrtab.get(subId);
586             if (egr != null) {
587                 subId = pf.getPath(egr) + subId;
588             }
589             StringBuffer sb = feedTargets.get(feedId);
590             if (sb == null) {
591                 sb = new StringBuffer();
592                 feedTargets.put(feedId, sb);
593             }
594             sb.append(' ').append(subId);
595         }
596         alldests = destInfos.toArray(new DestInfo[destInfos.size()]);
597         for (ProvFeed pfx : pd.getFeeds()) {
598             String fid = pfx.getId();
599             Feed f = feeds.get(fid);
600             if (f != null) {
601                 continue;
602             }
603             f = new Feed();
604             feeds.put(fid, f);
605             f.loginfo = pfx.getLogData();
606             f.status = pfx.getStatus();
607             Vector<SubnetMatcher> v1 = pfstab.get(fid);
608             if (v1 == null) {
609                 f.subnets = new SubnetMatcher[0];
610             } else {
611                 f.subnets = v1.toArray(new SubnetMatcher[v1.size()]);
612             }
613             Hashtable<String, String> h1 = pfutab.get(fid);
614             if (h1 == null) {
615                 h1 = new Hashtable<String, String>();
616             }
617             f.authusers = h1;
618             Vector<Redirection> v2 = rdtab.get(fid);
619             if (v2 == null) {
620                 f.redirections = new Redirection[0];
621             } else {
622                 f.redirections = v2.toArray(new Redirection[v2.size()]);
623             }
624             StringBuffer sb = feedTargets.get(fid);
625             if (sb == null) {
626                 f.targets = new Target[0];
627             } else {
628                 f.targets = parseRouting(sb.toString());
629             }
630         }
631     }
632
633     /**
634      * Parse a target string into an array of targets
635      *
636      * @param routing Target string
637      * @return Array of targets.
638      */
639     public Target[] parseRouting(String routing) {
640         routing = routing.trim();
641         if ("".equals(routing)) {
642             return (new Target[0]);
643         }
644         String[] xx = routing.split("\\s+");
645         Hashtable<String, Target> tmap = new Hashtable<String, Target>();
646         HashSet<String> subset = new HashSet<String>();
647         Vector<Target> tv = new Vector<Target>();
648         Target[] ret = new Target[xx.length];
649         for (int i = 0; i < xx.length; i++) {
650             String t = xx[i];
651             int j = t.indexOf('/');
652             if (j == -1) {
653                 DestInfo di = subinfo.get(t);
654                 if (di == null) {
655                     tv.add(new Target(null, t));
656                 } else {
657                     if (!subset.contains(t)) {
658                         subset.add(t);
659                         tv.add(new Target(di, null));
660                     }
661                 }
662             } else {
663                 String node = t.substring(0, j);
664                 String rtg = t.substring(j + 1);
665                 DestInfo di = nodeinfo.get(node);
666                 if (di == null) {
667                     tv.add(new Target(null, t));
668                 } else {
669                     Target tt = tmap.get(node);
670                     if (tt == null) {
671                         tt = new Target(di, rtg);
672                         tmap.put(node, tt);
673                         tv.add(tt);
674                     } else {
675                         tt.addRouting(rtg);
676                     }
677                 }
678             }
679         }
680         return (tv.toArray(new Target[tv.size()]));
681     }
682
683     /**
684      * Check whether this is a valid node-to-node transfer
685      *
686      * @param credentials Credentials offered by the supposed node
687      * @param ip IP address the request came from
688      */
689     public boolean isAnotherNode(String credentials, String ip) {
690         IsFrom n = nodes.get(credentials);
691         return (n != null && n.isFrom(ip));
692     }
693
694     /**
695      * Check whether publication is allowed.
696      *
697      * @param feedid The ID of the feed being requested.
698      * @param credentials The offered credentials
699      * @param ip The requesting IP address
700      */
701     public String isPublishPermitted(String feedid, String credentials, String ip) {
702         Feed f = feeds.get(feedid);
703         String nf = "Feed does not exist";
704         if (f != null) {
705             nf = f.status;
706         }
707         if (nf != null) {
708             return (nf);
709         }
710         String user = f.authusers.get(credentials);
711         if (user == null) {
712             return ("Publisher not permitted for this feed");
713         }
714         if (f.subnets.length == 0) {
715             return (null);
716         }
717         byte[] addr = NodeUtils.getInetAddress(ip);
718         for (SubnetMatcher snm : f.subnets) {
719             if (snm.matches(addr)) {
720                 return (null);
721             }
722         }
723         return ("Publisher not permitted for this feed");
724     }
725
726     /**
727      * Check whether delete file is allowed.
728      *
729      * @param subId The ID of the subscription being requested.
730      */
731     public boolean isDeletePermitted(String subId) {
732         ProvSubscription provSubscription = provSubscriptions.get(subId);
733         return provSubscription.isPrivilegedSubscriber();
734     }
735
736     /**
737      * Get authenticated user
738      */
739     public String getAuthUser(String feedid, String credentials) {
740         return (feeds.get(feedid).authusers.get(credentials));
741     }
742
743     /**
744      * Check if the request should be redirected to a different ingress node
745      */
746     public String getIngressNode(String feedid, String user, String ip) {
747         Feed f = feeds.get(feedid);
748         if (f.redirections.length == 0) {
749             return (null);
750         }
751         byte[] addr = NodeUtils.getInetAddress(ip);
752         for (Redirection r : f.redirections) {
753             if (r.user != null && !user.equals(r.user)) {
754                 continue;
755             }
756             if (r.snm != null && !r.snm.matches(addr)) {
757                 continue;
758             }
759             for (String n : r.nodes) {
760                 if (myname.equals(n)) {
761                     return (null);
762                 }
763             }
764             if (r.nodes.length == 0) {
765                 return (null);
766             }
767             return (r.nodes[rrcntr++ % r.nodes.length]);
768         }
769         return (null);
770     }
771
772     /**
773      * Get a provisioned configuration parameter
774      */
775     public String getProvParam(String name) {
776         return (params.get(name));
777     }
778
779     /**
780      * Get all the DestInfos
781      */
782     public DestInfo[] getAllDests() {
783         return (alldests);
784     }
785
786     /**
787      * Get the targets for a feed
788      *
789      * @param feedid The feed ID
790      * @return The targets this feed should be delivered to
791      */
792     public Target[] getTargets(String feedid) {
793         if (feedid == null) {
794             return (new Target[0]);
795         }
796         Feed f = feeds.get(feedid);
797         if (f == null) {
798             return (new Target[0]);
799         }
800         return (f.targets);
801     }
802
803     /**
804      * Get the feed ID for a subscription
805      *
806      * @param subid The subscription ID
807      * @return The feed ID
808      */
809     public String getFeedId(String subid) {
810         DestInfo di = subinfo.get(subid);
811         if (di == null) {
812             return (null);
813         }
814         return (di.getLogData());
815     }
816
817     /**
818      * Get the spool directory for a subscription
819      *
820      * @param subid The subscription ID
821      * @return The spool directory
822      */
823     public String getSpoolDir(String subid) {
824         DestInfo di = subinfo.get(subid);
825         if (di == null) {
826             return (null);
827         }
828         return (di.getSpool());
829     }
830
831     /**
832      * Get the Authorization value this node uses
833      *
834      * @return The Authorization header value for this node
835      */
836     public String getMyAuth() {
837         return (myauth);
838     }
839
840 }