5577e52e9cbbcaf312e708f746bab20de93248d1
[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         private boolean decompress;
236
237         /**
238          * Construct a subscription configuration entry
239          *
240          * @param subid The subscription ID
241          * @param feedid The feed ID
242          * @param url The base delivery URL (not including the fileid)
243          * @param authuser The user in the credentials used to deliver
244          * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
245          * Authorization header.
246          * @param metaonly Is this a meta data only subscription?
247          * @param use100 Should we send Expect: 100-continue?
248          * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
249          * @param decompress To see if they want their information compressed or decompressed
250          */
251         public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials,
252                 boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean decompress) {
253             this.subid = subid;
254             this.feedid = feedid;
255             this.url = url;
256             this.authuser = authuser;
257             this.credentials = credentials;
258             this.metaonly = metaonly;
259             this.use100 = use100;
260             this.privilegedSubscriber = privilegedSubscriber;
261             this.decompress = decompress;
262         }
263
264         /**
265          * Get the subscription ID
266          */
267         public String getSubId() {
268             return (subid);
269         }
270
271         /**
272          * Get the feed ID
273          */
274         public String getFeedId() {
275             return (feedid);
276         }
277
278         /**
279          * Get the delivery URL
280          */
281         public String getURL() {
282             return (url);
283         }
284
285         /**
286          * Get the user
287          */
288         public String getAuthUser() {
289             return (authuser);
290         }
291
292         /**
293          * Get the delivery credentials
294          */
295         public String getCredentials() {
296             return (credentials);
297         }
298
299         /**
300          * Is this a meta data only subscription?
301          */
302         public boolean isMetaDataOnly() {
303             return (metaonly);
304         }
305
306         /**
307          * Should we send Expect: 100-continue?
308          */
309         public boolean isUsing100() {
310             return (use100);
311         }
312
313         /**
314          * Can we wait to receive a delete file call before deleting file
315          */
316         public boolean isPrivilegedSubscriber() {
317             return (privilegedSubscriber);
318         }
319
320         /**
321          * Should i decompress the file before sending it on
322          */
323         public boolean isDecompress() {
324             return (decompress);
325         }
326     }
327
328     /**
329      * Raw configuration entry for controlled ingress to the data router node
330      */
331     public static class ProvForceIngress {
332
333         private String feedid;
334         private String subnet;
335         private String user;
336         private String[] nodes;
337
338         /**
339          * Construct a forced ingress configuration entry
340          *
341          * @param feedid The feed ID that this entry applies to
342          * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
343          * publisher IP addresses
344          * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
345          * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
346          */
347         public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
348             this.feedid = feedid;
349             this.subnet = subnet;
350             this.user = user;
351             this.nodes = nodes;
352         }
353
354         /**
355          * Get the feed ID
356          */
357         public String getFeedId() {
358             return (feedid);
359         }
360
361         /**
362          * Get the subnet
363          */
364         public String getSubnet() {
365             return (subnet);
366         }
367
368         /**
369          * Get the user
370          */
371         public String getUser() {
372             return (user);
373         }
374
375         /**
376          * Get the node
377          */
378         public String[] getNodes() {
379             return (nodes);
380         }
381     }
382
383     /**
384      * Raw configuration entry for controlled egress from the data router
385      */
386     public static class ProvForceEgress {
387
388         private String subid;
389         private String node;
390
391         /**
392          * Construct a forced egress configuration entry
393          *
394          * @param subid The subscription ID the subscription with forced egress
395          * @param node The node handling deliveries for this subscription
396          */
397         public ProvForceEgress(String subid, String node) {
398             this.subid = subid;
399             this.node = node;
400         }
401
402         /**
403          * Get the subscription ID
404          */
405         public String getSubId() {
406             return (subid);
407         }
408
409         /**
410          * Get the node
411          */
412         public String getNode() {
413             return (node);
414         }
415     }
416
417     /**
418      * Raw configuration entry for routing within the data router network
419      */
420     public static class ProvHop {
421
422         private String from;
423         private String to;
424         private String via;
425
426         /**
427          * A human readable description of this entry
428          */
429         public String toString() {
430             return ("Hop " + from + "->" + to + " via " + via);
431         }
432
433         /**
434          * Construct a hop entry
435          *
436          * @param from The FQDN of the node with the data to be delivered
437          * @param to The FQDN of the node that will deliver to the subscriber
438          * @param via The FQDN of the node where the from node should send the data
439          */
440         public ProvHop(String from, String to, String via) {
441             this.from = from;
442             this.to = to;
443             this.via = via;
444         }
445
446         /**
447          * Get the from node
448          */
449         public String getFrom() {
450             return (from);
451         }
452
453         /**
454          * Get the to node
455          */
456         public String getTo() {
457             return (to);
458         }
459
460         /**
461          * Get the next intermediate node
462          */
463         public String getVia() {
464             return (via);
465         }
466     }
467
468     private static class Redirection {
469
470         SubnetMatcher snm;
471         String user;
472         String[] nodes;
473     }
474
475     private static class Feed {
476
477         String loginfo;
478         String status;
479         SubnetMatcher[] subnets;
480         Hashtable<String, String> authusers = new Hashtable<String, String>();
481         Redirection[] redirections;
482         Target[] targets;
483     }
484
485     private Hashtable<String, String> params = new Hashtable<>();
486     private Hashtable<String, Feed> feeds = new Hashtable<>();
487     private Hashtable<String, DestInfo> nodeinfo = new Hashtable<>();
488     private Hashtable<String, DestInfo> subinfo = new Hashtable<>();
489     private Hashtable<String, IsFrom> nodes = new Hashtable<>();
490     private Hashtable<String, ProvSubscription> provSubscriptions = new Hashtable<>();
491     private String myname;
492     private String myauth;
493     private DestInfo[] alldests;
494     private int rrcntr;
495
496     /**
497      * Process the raw provisioning data to configure this node
498      *
499      * @param pd The parsed provisioning data
500      * @param myname My name as seen by external systems
501      * @param spooldir The directory where temporary files live
502      * @param port The port number for URLs
503      * @param nodeauthkey The keying string used to generate node authentication credentials
504      */
505     public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
506         this.myname = myname;
507         for (ProvParam p : pd.getParams()) {
508             params.put(p.getName(), p.getValue());
509         }
510         Vector<DestInfo> destInfos = new Vector<>();
511         myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
512         for (ProvNode pn : pd.getNodes()) {
513             String cn = pn.getCName();
514             if (nodeinfo.get(cn) != null) {
515                 continue;
516             }
517             String auth = NodeUtils.getNodeAuthHdr(cn, nodeauthkey);
518             DestInfo di = new DestInfo("n:" + cn, spooldir + "/n/" + cn, null, "n2n-" + cn,
519                     "https://" + cn + ":" + port + "/internal/publish", cn, myauth, false, true, false, false);
520             (new File(di.getSpool())).mkdirs();
521             destInfos.add(di);
522             nodeinfo.put(cn, di);
523             nodes.put(auth, new IsFrom(cn));
524         }
525         PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[nodeinfo.size()]), pd.getHops());
526         Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<String, Vector<Redirection>>();
527         for (ProvForceIngress pfi : pd.getForceIngress()) {
528             Vector<Redirection> v = rdtab.get(pfi.getFeedId());
529             if (v == null) {
530                 v = new Vector<Redirection>();
531                 rdtab.put(pfi.getFeedId(), v);
532             }
533             Redirection r = new Redirection();
534             if (pfi.getSubnet() != null) {
535                 r.snm = new SubnetMatcher(pfi.getSubnet());
536             }
537             r.user = pfi.getUser();
538             r.nodes = pfi.getNodes();
539             v.add(r);
540         }
541         Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<String, Hashtable<String, String>>();
542         for (ProvFeedUser pfu : pd.getFeedUsers()) {
543             Hashtable<String, String> t = pfutab.get(pfu.getFeedId());
544             if (t == null) {
545                 t = new Hashtable<String, String>();
546                 pfutab.put(pfu.getFeedId(), t);
547             }
548             t.put(pfu.getCredentials(), pfu.getUser());
549         }
550         Hashtable<String, String> egrtab = new Hashtable<String, String>();
551         for (ProvForceEgress pfe : pd.getForceEgress()) {
552             if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
553                 continue;
554             }
555             egrtab.put(pfe.getSubId(), pfe.getNode());
556         }
557         Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<>();
558         for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
559             Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
560             if (v == null) {
561                 v = new Vector<SubnetMatcher>();
562                 pfstab.put(pfs.getFeedId(), v);
563             }
564             v.add(new SubnetMatcher(pfs.getCidr()));
565         }
566         Hashtable<String, StringBuffer> feedTargets = new Hashtable<>();
567         HashSet<String> allfeeds = new HashSet<>();
568         for (ProvFeed pfx : pd.getFeeds()) {
569             if (pfx.getStatus() == null) {
570                 allfeeds.add(pfx.getId());
571             }
572         }
573         for (ProvSubscription provSubscription : pd.getSubscriptions()) {
574             String subId = provSubscription.getSubId();
575             String feedId = provSubscription.getFeedId();
576             if (!allfeeds.contains(feedId)) {
577                 continue;
578             }
579             if (subinfo.get(subId) != null) {
580                 continue;
581             }
582             int sididx = 999;
583             try {
584                 sididx = Integer.parseInt(subId);
585                 sididx -= sididx % 100;
586             } catch (Exception e) {
587             }
588             String subscriptionDirectory = sididx + "/" + subId;
589             DestInfo destinationInfo = new DestInfo("s:" + subId,
590                     spooldir + "/s/" + subscriptionDirectory, provSubscription);
591             (new File(destinationInfo.getSpool())).mkdirs();
592             destInfos.add(destinationInfo);
593             provSubscriptions.put(subId, provSubscription);
594             subinfo.put(subId, destinationInfo);
595             String egr = egrtab.get(subId);
596             if (egr != null) {
597                 subId = pf.getPath(egr) + subId;
598             }
599             StringBuffer sb = feedTargets.get(feedId);
600             if (sb == null) {
601                 sb = new StringBuffer();
602                 feedTargets.put(feedId, sb);
603             }
604             sb.append(' ').append(subId);
605         }
606         alldests = destInfos.toArray(new DestInfo[destInfos.size()]);
607         for (ProvFeed pfx : pd.getFeeds()) {
608             String fid = pfx.getId();
609             Feed f = feeds.get(fid);
610             if (f != null) {
611                 continue;
612             }
613             f = new Feed();
614             feeds.put(fid, f);
615             f.loginfo = pfx.getLogData();
616             f.status = pfx.getStatus();
617             Vector<SubnetMatcher> v1 = pfstab.get(fid);
618             if (v1 == null) {
619                 f.subnets = new SubnetMatcher[0];
620             } else {
621                 f.subnets = v1.toArray(new SubnetMatcher[v1.size()]);
622             }
623             Hashtable<String, String> h1 = pfutab.get(fid);
624             if (h1 == null) {
625                 h1 = new Hashtable<String, String>();
626             }
627             f.authusers = h1;
628             Vector<Redirection> v2 = rdtab.get(fid);
629             if (v2 == null) {
630                 f.redirections = new Redirection[0];
631             } else {
632                 f.redirections = v2.toArray(new Redirection[v2.size()]);
633             }
634             StringBuffer sb = feedTargets.get(fid);
635             if (sb == null) {
636                 f.targets = new Target[0];
637             } else {
638                 f.targets = parseRouting(sb.toString());
639             }
640         }
641     }
642
643     /**
644      * Parse a target string into an array of targets
645      *
646      * @param routing Target string
647      * @return Array of targets.
648      */
649     public Target[] parseRouting(String routing) {
650         routing = routing.trim();
651         if ("".equals(routing)) {
652             return (new Target[0]);
653         }
654         String[] xx = routing.split("\\s+");
655         Hashtable<String, Target> tmap = new Hashtable<String, Target>();
656         HashSet<String> subset = new HashSet<String>();
657         Vector<Target> tv = new Vector<Target>();
658         Target[] ret = new Target[xx.length];
659         for (int i = 0; i < xx.length; i++) {
660             String t = xx[i];
661             int j = t.indexOf('/');
662             if (j == -1) {
663                 DestInfo di = subinfo.get(t);
664                 if (di == null) {
665                     tv.add(new Target(null, t));
666                 } else {
667                     if (!subset.contains(t)) {
668                         subset.add(t);
669                         tv.add(new Target(di, null));
670                     }
671                 }
672             } else {
673                 String node = t.substring(0, j);
674                 String rtg = t.substring(j + 1);
675                 DestInfo di = nodeinfo.get(node);
676                 if (di == null) {
677                     tv.add(new Target(null, t));
678                 } else {
679                     Target tt = tmap.get(node);
680                     if (tt == null) {
681                         tt = new Target(di, rtg);
682                         tmap.put(node, tt);
683                         tv.add(tt);
684                     } else {
685                         tt.addRouting(rtg);
686                     }
687                 }
688             }
689         }
690         return (tv.toArray(new Target[tv.size()]));
691     }
692
693     /**
694      * Check whether this is a valid node-to-node transfer
695      *
696      * @param credentials Credentials offered by the supposed node
697      * @param ip IP address the request came from
698      */
699     public boolean isAnotherNode(String credentials, String ip) {
700         IsFrom n = nodes.get(credentials);
701         return (n != null && n.isFrom(ip));
702     }
703
704     /**
705      * Check whether publication is allowed.
706      *
707      * @param feedid The ID of the feed being requested.
708      * @param credentials The offered credentials
709      * @param ip The requesting IP address
710      */
711     public String isPublishPermitted(String feedid, String credentials, String ip) {
712         Feed f = feeds.get(feedid);
713         String nf = "Feed does not exist";
714         if (f != null) {
715             nf = f.status;
716         }
717         if (nf != null) {
718             return (nf);
719         }
720         String user = f.authusers.get(credentials);
721         if (user == null) {
722             return ("Publisher not permitted for this feed");
723         }
724         if (f.subnets.length == 0) {
725             return (null);
726         }
727         byte[] addr = NodeUtils.getInetAddress(ip);
728         for (SubnetMatcher snm : f.subnets) {
729             if (snm.matches(addr)) {
730                 return (null);
731             }
732         }
733         return ("Publisher not permitted for this feed");
734     }
735
736     /**
737      * Check whether delete file is allowed.
738      *
739      * @param subId The ID of the subscription being requested.
740      */
741     public boolean isDeletePermitted(String subId) {
742         ProvSubscription provSubscription = provSubscriptions.get(subId);
743         return provSubscription.isPrivilegedSubscriber();
744     }
745
746     /**
747      * Get authenticated user
748      */
749     public String getAuthUser(String feedid, String credentials) {
750         return (feeds.get(feedid).authusers.get(credentials));
751     }
752
753     /**
754      * Check if the request should be redirected to a different ingress node
755      */
756     public String getIngressNode(String feedid, String user, String ip) {
757         Feed f = feeds.get(feedid);
758         if (f.redirections.length == 0) {
759             return (null);
760         }
761         byte[] addr = NodeUtils.getInetAddress(ip);
762         for (Redirection r : f.redirections) {
763             if (r.user != null && !user.equals(r.user)) {
764                 continue;
765             }
766             if (r.snm != null && !r.snm.matches(addr)) {
767                 continue;
768             }
769             for (String n : r.nodes) {
770                 if (myname.equals(n)) {
771                     return (null);
772                 }
773             }
774             if (r.nodes.length == 0) {
775                 return (null);
776             }
777             return (r.nodes[rrcntr++ % r.nodes.length]);
778         }
779         return (null);
780     }
781
782     /**
783      * Get a provisioned configuration parameter
784      */
785     public String getProvParam(String name) {
786         return (params.get(name));
787     }
788
789     /**
790      * Get all the DestInfos
791      */
792     public DestInfo[] getAllDests() {
793         return (alldests);
794     }
795
796     /**
797      * Get the targets for a feed
798      *
799      * @param feedid The feed ID
800      * @return The targets this feed should be delivered to
801      */
802     public Target[] getTargets(String feedid) {
803         if (feedid == null) {
804             return (new Target[0]);
805         }
806         Feed f = feeds.get(feedid);
807         if (f == null) {
808             return (new Target[0]);
809         }
810         return (f.targets);
811     }
812
813     /**
814      * Get the feed ID for a subscription
815      *
816      * @param subid The subscription ID
817      * @return The feed ID
818      */
819     public String getFeedId(String subid) {
820         DestInfo di = subinfo.get(subid);
821         if (di == null) {
822             return (null);
823         }
824         return (di.getLogData());
825     }
826
827     /**
828      * Get the spool directory for a subscription
829      *
830      * @param subid The subscription ID
831      * @return The spool directory
832      */
833     public String getSpoolDir(String subid) {
834         DestInfo di = subinfo.get(subid);
835         if (di == null) {
836             return (null);
837         }
838         return (di.getSpool());
839     }
840
841     /**
842      * Get the Authorization value this node uses
843      *
844      * @return The Authorization header value for this node
845      */
846     public String getMyAuth() {
847         return (myauth);
848     }
849
850 }