/******************************************************************************* * ============LICENSE_START================================================== * * org.onap.dmaap * * =========================================================================== * * Copyright © 2017 AT&T Intellectual Property. All rights reserved. * * =========================================================================== * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * ============LICENSE_END==================================================== * * * * ECOMP is a trademark and service mark of AT&T Intellectual Property. * * ******************************************************************************/ package org.onap.dmaap.datarouter.node; import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import org.jetbrains.annotations.NotNull; /** * Processed configuration for this node. * *

The NodeConfig represents a processed configuration from the Data Router provisioning server. Each time * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one * discarded. */ public class NodeConfig { private static final String PUBLISHER_NOT_PERMITTED = "Publisher not permitted for this feed"; private static EELFLogger logger = EELFManager.getInstance().getLogger(NodeConfig.class); private HashMap params = new HashMap<>(); private HashMap feeds = new HashMap<>(); private HashMap nodeinfo = new HashMap<>(); private HashMap subinfo = new HashMap<>(); private HashMap nodes = new HashMap<>(); private HashMap provSubscriptions = new HashMap<>(); private String myname; private String myauth; private DestInfo[] alldests; private int rrcntr; /** * Process the raw provisioning data to configure this node. * * @param pd The parsed provisioning data * @param myname My name as seen by external systems * @param spooldir The directory where temporary files live * @param port The port number for URLs * @param nodeauthkey The keying string used to generate node authentication credentials */ public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) { this.myname = myname; for (ProvParam p : pd.getParams()) { params.put(p.getName(), p.getValue()); } ArrayList destInfos = addDestInfoToNodeConfig(pd, myname, spooldir, port, nodeauthkey); PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[0]), pd.getHops()); HashMap> rdtab = addSubRedirInfoToNodeConfig(pd); HashMap> pfutab = addFeedUsersToNodeConfig(pd); HashMap egrtab = addEgressRoutesToNodeConfig(pd, myname); HashMap> pfstab = addFeedSubnetToNodeConfig(pd); HashSet allfeeds = addFeedsToNodeConfig(pd); HashMap feedTargets = addSubsToNodeConfig(pd, spooldir, destInfos, pf, egrtab, allfeeds); alldests = destInfos.toArray(new DestInfo[0]); addFeedTargetsToNodeConfig(pd, rdtab, pfutab, pfstab, feedTargets); } @NotNull private ArrayList addDestInfoToNodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) { ArrayList destInfos = new ArrayList<>(); myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey); for (ProvNode pn : pd.getNodes()) { String commonName = pn.getCName(); if (nodeinfo.get(commonName) != null) { continue; } DestInfo di = new DestInfoBuilder().setName("n:" + commonName).setSpool(spooldir + "/n/" + commonName) .setSubid(null) .setLogdata("n2n-" + commonName).setUrl("https://" + commonName + ":" + port + "/internal/publish") .setAuthuser(commonName).setAuthentication(myauth).setMetaonly(false).setUse100(true) .setPrivilegedSubscriber(false).setFollowRedirects(false).setDecompress(false).createDestInfo(); (new File(di.getSpool())).mkdirs(); String auth = NodeUtils.getNodeAuthHdr(commonName, nodeauthkey); destInfos.add(di); nodeinfo.put(commonName, di); nodes.put(auth, new IsFrom(commonName)); } return destInfos; } @NotNull private HashMap> addSubRedirInfoToNodeConfig(ProvData pd) { HashMap> rdtab = new HashMap<>(); for (ProvForceIngress pfi : pd.getForceIngress()) { ArrayList redirections = rdtab.get(pfi.getFeedId()); if (redirections == null) { redirections = new ArrayList<>(); rdtab.put(pfi.getFeedId(), redirections); } Redirection redirection = new Redirection(); if (pfi.getSubnet() != null) { redirection.snm = new SubnetMatcher(pfi.getSubnet()); } redirection.user = pfi.getUser(); redirection.nodes = pfi.getNodes(); redirections.add(redirection); } return rdtab; } @NotNull private HashMap> addFeedUsersToNodeConfig(ProvData pd) { HashMap> pfutab = new HashMap<>(); for (ProvFeedUser pfu : pd.getFeedUsers()) { HashMap userInfo = pfutab.get(pfu.getFeedId()); if (userInfo == null) { userInfo = new HashMap<>(); pfutab.put(pfu.getFeedId(), userInfo); } userInfo.put(pfu.getCredentials(), pfu.getUser()); } return pfutab; } @NotNull private HashMap addEgressRoutesToNodeConfig(ProvData pd, String myname) { HashMap egrtab = new HashMap<>(); for (ProvForceEgress pfe : pd.getForceEgress()) { if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) { continue; } egrtab.put(pfe.getSubId(), pfe.getNode()); } return egrtab; } @NotNull private HashMap> addFeedSubnetToNodeConfig(ProvData pd) { HashMap> pfstab = new HashMap<>(); for (ProvFeedSubnet pfs : pd.getFeedSubnets()) { ArrayList subnetMatchers = pfstab.get(pfs.getFeedId()); if (subnetMatchers == null) { subnetMatchers = new ArrayList<>(); pfstab.put(pfs.getFeedId(), subnetMatchers); } subnetMatchers.add(new SubnetMatcher(pfs.getCidr())); } return pfstab; } @NotNull private HashSet addFeedsToNodeConfig(ProvData pd) { HashSet allfeeds = new HashSet<>(); for (ProvFeed pfx : pd.getFeeds()) { if (pfx.getStatus() == null) { allfeeds.add(pfx.getId()); } } return allfeeds; } @NotNull private HashMap addSubsToNodeConfig(ProvData pd, String spooldir, ArrayList destInfos, PathFinder pf, HashMap egrtab, HashSet allfeeds) { HashMap feedTargets = new HashMap<>(); for (ProvSubscription provSubscription : pd.getSubscriptions()) { String subId = provSubscription.getSubId(); String feedId = provSubscription.getFeedId(); if (isFeedOrSubKnown(allfeeds, subId, feedId)) { continue; } int sididx = 999; try { sididx = Integer.parseInt(subId); sididx -= sididx % 100; } catch (Exception e) { logger.error("NODE0517 Exception NodeConfig: " + e); } String subscriptionDirectory = sididx + "/" + subId; DestInfo destinationInfo = new DestInfo("s:" + subId, spooldir + "/s/" + subscriptionDirectory, provSubscription); (new File(destinationInfo.getSpool())).mkdirs(); destInfos.add(destinationInfo); provSubscriptions.put(subId, provSubscription); subinfo.put(subId, destinationInfo); String egr = egrtab.get(subId); if (egr != null) { subId = pf.getPath(egr) + subId; } StringBuilder sb = feedTargets.get(feedId); if (sb == null) { sb = new StringBuilder(); feedTargets.put(feedId, sb); } sb.append(' ').append(subId); } return feedTargets; } private void addFeedTargetsToNodeConfig(ProvData pd, HashMap> rdtab, HashMap> pfutab, HashMap> pfstab, HashMap feedTargets) { for (ProvFeed pfx : pd.getFeeds()) { String fid = pfx.getId(); Feed feed = feeds.get(fid); if (feed != null) { continue; } feed = new Feed(); feeds.put(fid, feed); feed.createdDate = pfx.getCreatedDate(); feed.loginfo = pfx.getLogData(); feed.status = pfx.getStatus(); /* * AAF changes: TDP EPIC US# 307413 * Passing aafInstance from ProvFeed to identify legacy/AAF feeds */ feed.aafInstance = pfx.getAafInstance(); ArrayList v1 = pfstab.get(fid); if (v1 == null) { feed.subnets = new SubnetMatcher[0]; } else { feed.subnets = v1.toArray(new SubnetMatcher[0]); } HashMap h1 = pfutab.get(fid); if (h1 == null) { h1 = new HashMap(); } feed.authusers = h1; ArrayList v2 = rdtab.get(fid); if (v2 == null) { feed.redirections = new Redirection[0]; } else { feed.redirections = v2.toArray(new Redirection[0]); } StringBuilder sb = feedTargets.get(fid); if (sb == null) { feed.targets = new Target[0]; } else { feed.targets = parseRouting(sb.toString()); } } } /** * Parse a target string into an array of targets. * * @param routing Target string * @return Array of targets. */ public Target[] parseRouting(String routing) { routing = routing.trim(); if ("".equals(routing)) { return (new Target[0]); } String[] routingTable = routing.split("\\s+"); HashMap tmap = new HashMap<>(); HashSet subset = new HashSet<>(); ArrayList targets = new ArrayList<>(); for (int i = 0; i < routingTable.length; i++) { String target = routingTable[i]; int index = target.indexOf('/'); if (index == -1) { addTarget(subset, targets, target); } else { addTargetWithRouting(tmap, targets, target, index); } } return (targets.toArray(new Target[0])); } /** * Check whether this is a valid node-to-node transfer. * * @param credentials Credentials offered by the supposed node * @param ip IP address the request came from */ public boolean isAnotherNode(String credentials, String ip) { IsFrom node = nodes.get(credentials); return (node != null && node.isFrom(ip)); } /** * Check whether publication is allowed. * * @param feedid The ID of the feed being requested. * @param credentials The offered credentials * @param ip The requesting IP address */ public String isPublishPermitted(String feedid, String credentials, String ip) { Feed feed = feeds.get(feedid); String nf = "Feed does not exist"; if (feed != null) { nf = feed.status; } if (nf != null) { return (nf); } String user = feed.authusers.get(credentials); if (user == null) { return (PUBLISHER_NOT_PERMITTED); } if (feed.subnets.length == 0) { return (null); } byte[] addr = NodeUtils.getInetAddress(ip); for (SubnetMatcher snm : feed.subnets) { if (snm.matches(addr)) { return (null); } } return (PUBLISHER_NOT_PERMITTED); } /** * Check whether publication is allowed for AAF Feed. * * @param feedid The ID of the feed being requested. * @param ip The requesting IP address */ public String isPublishPermitted(String feedid, String ip) { Feed feed = feeds.get(feedid); String nf = "Feed does not exist"; if (feed != null) { nf = feed.status; } if (nf != null) { return nf; } if (feed.subnets.length == 0) { return null; } byte[] addr = NodeUtils.getInetAddress(ip); for (SubnetMatcher snm : feed.subnets) { if (snm.matches(addr)) { return null; } } return PUBLISHER_NOT_PERMITTED; } /** * Check whether delete file is allowed. * * @param subId The ID of the subscription being requested. */ public boolean isDeletePermitted(String subId) { ProvSubscription provSubscription = provSubscriptions.get(subId); return provSubscription.isPrivilegedSubscriber(); } /** * Get authenticated user. */ public String getAuthUser(String feedid, String credentials) { return (feeds.get(feedid).authusers.get(credentials)); } /** * AAF changes: TDP EPIC US# 307413 Check AAF_instance for feed ID. * * @param feedid The ID of the feed specified */ public String getAafInstance(String feedid) { Feed feed = feeds.get(feedid); return feed.aafInstance; } /** * Check if the request should be redirected to a different ingress node. */ public String getIngressNode(String feedid, String user, String ip) { Feed feed = feeds.get(feedid); if (feed.redirections.length == 0) { return (null); } byte[] addr = NodeUtils.getInetAddress(ip); for (Redirection r : feed.redirections) { if ((r.user != null && !user.equals(r.user)) || (r.snm != null && !r.snm.matches(addr))) { continue; } for (String n : r.nodes) { if (myname.equals(n)) { return (null); } } if (r.nodes.length == 0) { return (null); } return (r.nodes[rrcntr++ % r.nodes.length]); } return (null); } /** * Get a provisioned configuration parameter. */ public String getProvParam(String name) { return (params.get(name)); } /** * Get all the DestInfos. */ public DestInfo[] getAllDests() { return (alldests); } /** * Get the targets for a feed. * * @param feedid The feed ID * @return The targets this feed should be delivered to */ public Target[] getTargets(String feedid) { if (feedid == null) { return (new Target[0]); } Feed feed = feeds.get(feedid); if (feed == null) { return (new Target[0]); } return (feed.targets); } /** * Get the creation date for a feed. * * @param feedid The feed ID * @return the timestamp of creation date of feed id passed */ public String getCreatedDate(String feedid) { Feed feed = feeds.get(feedid); return (feed.createdDate); } /** * Get the feed ID for a subscription. * * @param subid The subscription ID * @return The feed ID */ public String getFeedId(String subid) { DestInfo di = subinfo.get(subid); if (di == null) { return (null); } return (di.getLogData()); } /** * Get the spool directory for a subscription. * * @param subid The subscription ID * @return The spool directory */ public String getSpoolDir(String subid) { DestInfo di = subinfo.get(subid); if (di == null) { return (null); } return (di.getSpool()); } /** * Get the Authorization value this node uses. * * @return The Authorization header value for this node */ public String getMyAuth() { return (myauth); } private boolean isFeedOrSubKnown(HashSet allfeeds, String subId, String feedId) { return !allfeeds.contains(feedId) || subinfo.get(subId) != null; } private void addTargetWithRouting(HashMap tmap, ArrayList targets, String target, int index) { String node = target.substring(0, index); String rtg = target.substring(index + 1); DestInfo di = nodeinfo.get(node); if (di == null) { targets.add(new Target(null, target)); } else { Target tt = tmap.get(node); if (tt == null) { tt = new Target(di, rtg); tmap.put(node, tt); targets.add(tt); } else { tt.addRouting(rtg); } } } private void addTarget(HashSet subset, ArrayList targets, String target) { DestInfo destInfo = subinfo.get(target); if (destInfo == null) { targets.add(new Target(null, target)); } else { if (!subset.contains(target)) { subset.add(target); targets.add(new Target(destInfo, null)); } } } /** * Raw configuration entry for a data router node. */ public static class ProvNode { private String cname; /** * Construct a node configuration entry. * * @param cname The cname of the node. */ public ProvNode(String cname) { this.cname = cname; } /** * Get the cname of the node. */ public String getCName() { return (cname); } } /** * Raw configuration entry for a provisioning parameter. */ public static class ProvParam { private String name; private String value; /** * Construct a provisioning parameter configuration entry. * * @param name The name of the parameter. * @param value The value of the parameter. */ public ProvParam(String name, String value) { this.name = name; this.value = value; } /** * Get the name of the parameter. */ public String getName() { return (name); } /** * Get the value of the parameter. */ public String getValue() { return (value); } } /** * Raw configuration entry for a data feed. */ public static class ProvFeed { private String id; private String logdata; private String status; private String createdDate; /* * AAF changes: TDP EPIC US# 307413 * Passing aafInstance from to identify legacy/AAF feeds */ private String aafInstance; /** * Construct a feed configuration entry. * * @param id The feed ID of the entry. * @param logdata String for log entries about the entry. * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or * null if it is valid. */ public ProvFeed(String id, String logdata, String status, String createdDate, String aafInstance) { this.id = id; this.logdata = logdata; this.status = status; this.createdDate = createdDate; this.aafInstance = aafInstance; } /** * Get the created date of the data feed. */ public String getCreatedDate() { return (createdDate); } /** * Get the aafInstance of the data feed. */ public String getAafInstance() { return aafInstance; } /** * Get the feed id of the data feed. */ public String getId() { return (id); } /** * Get the log data of the data feed. */ public String getLogData() { return (logdata); } /** * Get the status of the data feed. */ public String getStatus() { return (status); } } /** * Raw configuration entry for a feed user. */ public static class ProvFeedUser { private String feedid; private String user; private String credentials; /** * Construct a feed user configuration entry. * * @param feedid The feed id. * @param user The user that will publish to the feed. * @param credentials The Authorization header the user will use to publish. */ public ProvFeedUser(String feedid, String user, String credentials) { this.feedid = feedid; this.user = user; this.credentials = credentials; } /** * Get the feed id of the feed user. */ public String getFeedId() { return (feedid); } /** * Get the user for the feed user. */ public String getUser() { return (user); } /** * Get the credentials for the feed user. */ public String getCredentials() { return (credentials); } } /** * Raw configuration entry for a feed subnet. */ public static class ProvFeedSubnet { private String feedid; private String cidr; /** * Construct a feed subnet configuration entry. * * @param feedid The feed ID * @param cidr The CIDR allowed to publish to the feed. */ public ProvFeedSubnet(String feedid, String cidr) { this.feedid = feedid; this.cidr = cidr; } /** * Get the feed id of the feed subnet. */ public String getFeedId() { return (feedid); } /** * Get the CIDR of the feed subnet. */ public String getCidr() { return (cidr); } } /** * Raw configuration entry for a subscription. */ public static class ProvSubscription { private String subid; private String feedid; private String url; private String authuser; private String credentials; private boolean metaonly; private boolean use100; private boolean privilegedSubscriber; private boolean followRedirect; private boolean decompress; /** * Construct a subscription configuration entry. * * @param subid The subscription ID * @param feedid The feed ID * @param url The base delivery URL (not including the fileid) * @param authuser The user in the credentials used to deliver * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the * Authorization header. * @param metaonly Is this a meta data only subscription? * @param use100 Should we send Expect: 100-continue? * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file * @param followRedirect Is follow redirect of destination enabled? * @param decompress To see if they want their information compressed or decompressed */ public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials, boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean followRedirect, boolean decompress) { this.subid = subid; this.feedid = feedid; this.url = url; this.authuser = authuser; this.credentials = credentials; this.metaonly = metaonly; this.use100 = use100; this.privilegedSubscriber = privilegedSubscriber; this.followRedirect = followRedirect; this.decompress = decompress; } /** * Get the subscription ID. */ public String getSubId() { return (subid); } /** * Get the feed ID. */ public String getFeedId() { return (feedid); } /** * Get the delivery URL. */ public String getURL() { return (url); } /** * Get the user. */ public String getAuthUser() { return (authuser); } /** * Get the delivery credentials. */ public String getCredentials() { return (credentials); } /** * Is this a meta data only subscription. */ public boolean isMetaDataOnly() { return (metaonly); } /** * Should we send Expect: 100-continue. */ public boolean isUsing100() { return (use100); } /** * Can we wait to receive a delete file call before deleting file. */ public boolean isPrivilegedSubscriber() { return (privilegedSubscriber); } /** * Should I decompress the file before sending it on. */ public boolean isDecompress() { return (decompress); } /** * New field is added - FOLLOW_REDIRECTS feature iTrack:DATARTR-17 - 1706 Get the followRedirect of this * destination. */ boolean getFollowRedirect() { return (followRedirect); } } /** * Raw configuration entry for controlled ingress to the data router node. */ public static class ProvForceIngress { private String feedid; private String subnet; private String user; private String[] nodes; /** * Construct a forced ingress configuration entry. * * @param feedid The feed ID that this entry applies to * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all * publisher IP addresses * @param user The publishing user this entry applies to or "" if it applies to all publishing users. * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to. */ public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) { this.feedid = feedid; this.subnet = subnet; this.user = user; //Sonar fix if (nodes == null) { this.nodes = new String[0]; } else { this.nodes = Arrays.copyOf(nodes, nodes.length); } } /** * Get the feed ID. */ public String getFeedId() { return (feedid); } /** * Get the subnet. */ public String getSubnet() { return (subnet); } /** * Get the user. */ public String getUser() { return (user); } /** * Get the node. */ public String[] getNodes() { return (nodes); } } /** * Raw configuration entry for controlled egress from the data router. */ public static class ProvForceEgress { private String subid; private String node; /** * Construct a forced egress configuration entry. * * @param subid The subscription ID the subscription with forced egress * @param node The node handling deliveries for this subscription */ public ProvForceEgress(String subid, String node) { this.subid = subid; this.node = node; } /** * Get the subscription ID. */ public String getSubId() { return (subid); } /** * Get the node. */ public String getNode() { return (node); } } /** * Raw configuration entry for routing within the data router network. */ public static class ProvHop { private String from; private String to; private String via; /** * Construct a hop entry. * * @param from The FQDN of the node with the data to be delivered * @param to The FQDN of the node that will deliver to the subscriber * @param via The FQDN of the node where the from node should send the data */ public ProvHop(String from, String to, String via) { this.from = from; this.to = to; this.via = via; } /** * A human readable description of this entry. */ public String toString() { return ("Hop " + from + "->" + to + " via " + via); } /** * Get the from node. */ public String getFrom() { return (from); } /** * Get the to node. */ public String getTo() { return (to); } /** * Get the next intermediate node. */ public String getVia() { return (via); } } private static class Redirection { SubnetMatcher snm; String user; String[] nodes; } private static class Feed { String loginfo; String status; SubnetMatcher[] subnets; HashMap authusers = new HashMap<>(); Redirection[] redirections; Target[] targets; String createdDate; String aafInstance; } }