1 /*******************************************************************************
\r
2 * ============LICENSE_START==================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * ===========================================================================
\r
7 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * * you may not use this file except in compliance with the License.
\r
9 * * You may obtain a copy of the License at
\r
11 * * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * * Unless required by applicable law or agreed to in writing, software
\r
14 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * * See the License for the specific language governing permissions and
\r
17 * * limitations under the License.
\r
18 * * ============LICENSE_END====================================================
\r
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
25 package org.onap.dmaap.datarouter.provisioning.beans;
\r
27 import java.net.InetAddress;
\r
28 import java.net.UnknownHostException;
\r
29 import java.sql.Connection;
\r
30 import java.sql.PreparedStatement;
\r
31 import java.sql.ResultSet;
\r
32 import java.sql.SQLException;
\r
33 import java.sql.Statement;
\r
34 import java.util.ArrayList;
\r
35 import java.util.Collection;
\r
36 import java.util.Set;
\r
37 import java.util.SortedSet;
\r
38 import java.util.TreeSet;
\r
40 import javax.servlet.http.HttpServletRequest;
\r
42 import com.att.eelf.configuration.EELFLogger;
\r
43 import com.att.eelf.configuration.EELFManager;
\r
44 import org.apache.commons.codec.binary.Base64;
\r
45 import org.json.JSONArray;
\r
46 import org.json.JSONObject;
\r
47 import org.onap.dmaap.datarouter.provisioning.utils.DB;
\r
50 * The representation of one route in the Ingress Route Table.
\r
52 * @author Robert P. Eby
\r
53 * @version $Id: IngressRoute.java,v 1.3 2013/12/16 20:30:23 eby Exp $
\r
55 public class IngressRoute extends NodeClass implements Comparable<IngressRoute> {
\r
57 private static EELFLogger intlogger = EELFManager.getInstance().getLogger("InternalLog");
\r
58 private static final String SQLEXCEPTION = "SQLException: ";
\r
59 private final int seq;
\r
60 private final int feedid;
\r
61 private final String userid;
\r
62 private final String subnet;
\r
63 private int nodelist;
\r
64 private SortedSet<String> nodes;
\r
67 * Get all IngressRoutes in the database, sorted in order according to their sequence field.
\r
69 * @return a sorted set of IngressRoutes
\r
71 public static SortedSet<IngressRoute> getAllIngressRoutes() {
\r
72 return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES");
\r
76 * Get all IngressRoutes in the database with a particular sequence number.
\r
78 * @param seq the sequence number
\r
79 * @return a set of IngressRoutes
\r
81 public static Set<IngressRoute> getIngressRoutesForSeq(int seq) {
\r
82 return getAllIngressRoutesForSQL(
\r
83 "select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = " + seq);
\r
86 private static SortedSet<IngressRoute> getAllIngressRoutesForSQL(String sql) {
\r
87 SortedSet<IngressRoute> set = new TreeSet<IngressRoute>();
\r
90 @SuppressWarnings("resource")
\r
91 Connection conn = db.getConnection();
\r
92 try (Statement stmt = conn.createStatement()) {
\r
93 try (ResultSet rs = stmt.executeQuery(sql)) {
\r
95 int seq = rs.getInt("SEQUENCE");
\r
96 int feedid = rs.getInt("FEEDID");
\r
97 String user = rs.getString("USERID");
\r
98 String subnet = rs.getString("SUBNET");
\r
99 int nodeset = rs.getInt("NODESET");
\r
100 set.add(new IngressRoute(seq, feedid, user, subnet, nodeset));
\r
105 } catch (SQLException e) {
\r
106 intlogger.error("PROV0001 getAllIngressRoutesForSQL: " + e.getMessage(), e);
\r
112 * Get the maximum node set ID in use in the DB.
\r
114 * @return the integer value of the maximum
\r
116 public static int getMaxNodeSetID() {
\r
117 return getMax("select max(SETID) as MAX from NODESETS");
\r
121 * Get the maximum node sequence number in use in the DB.
\r
123 * @return the integer value of the maximum
\r
125 public static int getMaxSequence() {
\r
126 return getMax("select max(SEQUENCE) as MAX from INGRESS_ROUTES");
\r
129 private static int getMax(String sql) {
\r
133 @SuppressWarnings("resource")
\r
134 Connection conn = db.getConnection();
\r
135 try (Statement stmt = conn.createStatement()) {
\r
136 try (ResultSet rs = stmt.executeQuery(sql)) {
\r
138 rv = rs.getInt("MAX");
\r
143 } catch (SQLException e) {
\r
144 intlogger.error("PROV0002 getMax: " + e.getMessage(), e);
\r
150 * Get an Ingress Route for a particular feed ID, user, and subnet
\r
152 * @param feedid the Feed ID to look for
\r
153 * @param user the user name to look for
\r
154 * @param subnet the subnet to look for
\r
155 * @return the Ingress Route, or null of there is none
\r
157 public static IngressRoute getIngressRoute(int feedid, String user, String subnet) {
\r
158 IngressRoute v = null;
\r
159 PreparedStatement ps = null;
\r
162 @SuppressWarnings("resource")
\r
163 Connection conn = db.getConnection();
\r
164 String sql = "select SEQUENCE, NODESET from INGRESS_ROUTES where FEEDID = ? AND USERID = ? and SUBNET = ?";
\r
165 ps = conn.prepareStatement(sql);
\r
166 ps.setInt(1, feedid);
\r
167 ps.setString(2, user);
\r
168 ps.setString(3, subnet);
\r
169 try (ResultSet rs = ps.executeQuery()) {
\r
171 int seq = rs.getInt("SEQUENCE");
\r
172 int nodeset = rs.getInt("NODESET");
\r
173 v = new IngressRoute(seq, feedid, user, subnet, nodeset);
\r
178 } catch (SQLException e) {
\r
179 intlogger.error("PROV0003 getIngressRoute: " + e.getMessage(), e);
\r
185 } catch (SQLException e) {
\r
186 intlogger.error(SQLEXCEPTION + e.getMessage(), e);
\r
193 * Get a collection of all Ingress Routes with a particular sequence number.
\r
195 * @param seq the sequence number to look for
\r
196 * @return the collection (may be empty).
\r
198 public static Collection<IngressRoute> getIngressRoute(int seq) {
\r
199 Collection<IngressRoute> rv = new ArrayList<IngressRoute>();
\r
202 @SuppressWarnings("resource")
\r
203 Connection conn = db.getConnection();
\r
204 String sql = "select FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = ?";
\r
205 try (PreparedStatement ps = conn.prepareStatement(sql)) {
\r
207 try (ResultSet rs = ps.executeQuery()) {
\r
208 while (rs.next()) {
\r
209 int feedid = rs.getInt("FEEDID");
\r
210 String user = rs.getString("USERID");
\r
211 String subnet = rs.getString("SUBNET");
\r
212 int nodeset = rs.getInt("NODESET");
\r
213 rv.add(new IngressRoute(seq, feedid, user, subnet, nodeset));
\r
218 } catch (SQLException e) {
\r
219 intlogger.error("PROV0004 getIngressRoute: " + e.getMessage(), e);
\r
224 public IngressRoute(int seq, int feedid, String user, String subnet, Collection<String> nodes)
\r
225 throws IllegalArgumentException {
\r
226 this(seq, feedid, user, subnet);
\r
227 this.nodelist = -1;
\r
228 this.nodes = new TreeSet<String>(nodes);
\r
231 public IngressRoute(int seq, int feedid, String user, String subnet, int nodeset)
\r
232 throws IllegalArgumentException {
\r
233 this(seq, feedid, user, subnet);
\r
234 this.nodelist = nodeset;
\r
235 this.nodes = new TreeSet<String>(readNodes());
\r
238 private IngressRoute(int seq, int feedid, String user, String subnet)
\r
239 throws IllegalArgumentException {
\r
241 this.feedid = feedid;
\r
242 this.userid = (user == null) ? "-" : user;
\r
243 this.subnet = (subnet == null) ? "-" : subnet;
\r
244 this.nodelist = -1;
\r
246 if (Feed.getFeedById(feedid) == null) {
\r
247 throw new IllegalArgumentException("No such feed: " + feedid);
\r
249 if (!this.subnet.equals("-")) {
\r
250 SubnetMatcher sm = new SubnetMatcher(subnet);
\r
251 if (!sm.isValid()) {
\r
252 throw new IllegalArgumentException("Invalid subnet: " + subnet);
\r
257 public IngressRoute(JSONObject jo) {
\r
258 this.seq = jo.optInt("seq");
\r
259 this.feedid = jo.optInt("feedid");
\r
260 String t = jo.optString("user");
\r
261 this.userid = t.equals("") ? "-" : t;
\r
262 t = jo.optString("subnet");
\r
263 this.subnet = t.equals("") ? "-" : t;
\r
264 this.nodelist = -1;
\r
265 this.nodes = new TreeSet<String>();
\r
266 JSONArray ja = jo.getJSONArray("node");
\r
267 for (int i = 0; i < ja.length(); i++) {
\r
268 this.nodes.add(ja.getString(i));
\r
273 * Does this particular IngressRoute match a request, represented by feedid and req? To match, <i>feedid</i> must
\r
274 * match the feed ID in the route, the user in the route (if specified) must match the user in the request, and the
\r
275 * subnet in the route (if specified) must match the subnet from the request.
\r
277 * @param feedid the feedid for this request
\r
278 * @param req the remainder of the request
\r
279 * @return true if a match, false otherwise
\r
281 public boolean matches(int feedid, HttpServletRequest req) {
\r
283 if (this.feedid != feedid) {
\r
287 // Get user from request and compare
\r
288 // Note: we don't check the password; the node will do that
\r
289 if (userid.length() > 0 && !userid.equals("-")) {
\r
290 String credentials = req.getHeader("Authorization");
\r
291 if (credentials == null || !credentials.startsWith("Basic ")) {
\r
294 String t = new String(Base64.decodeBase64(credentials.substring(6)));
\r
295 int ix = t.indexOf(':');
\r
297 t = t.substring(0, ix);
\r
299 if (!t.equals(this.userid)) {
\r
304 // If this route has a subnet, match it against the requester's IP addr
\r
305 if (subnet.length() > 0 && !subnet.equals("-")) {
\r
307 InetAddress inet = InetAddress.getByName(req.getRemoteAddr());
\r
308 SubnetMatcher sm = new SubnetMatcher(subnet);
\r
309 return sm.matches(inet.getAddress());
\r
310 } catch (UnknownHostException e) {
\r
311 intlogger.error("PROV0008 matches: " + e.getMessage(), e);
\r
319 * Compare IP addresses as byte arrays to a subnet specified as a CIDR. Taken from
\r
320 * org.onap.dmaap.datarouter.node.SubnetMatcher and modified somewhat.
\r
322 public class SubnetMatcher {
\r
327 private boolean valid;
\r
330 * Construct a subnet matcher given a CIDR
\r
332 * @param subnet The CIDR to match
\r
334 public SubnetMatcher(String subnet) {
\r
335 int i = subnet.lastIndexOf('/');
\r
338 sn = InetAddress.getByName(subnet).getAddress();
\r
341 } catch (UnknownHostException e) {
\r
342 intlogger.error("PROV0008 SubnetMatcher: " + e.getMessage(), e);
\r
348 int n = Integer.parseInt(subnet.substring(i + 1));
\r
350 sn = InetAddress.getByName(subnet.substring(0, i)).getAddress();
\r
352 } catch (UnknownHostException e) {
\r
353 intlogger.error("PROV0008 SubnetMatcher: " + e.getMessage(), e);
\r
357 mask = ((0xff00) >> (n % 8)) & 0xff;
\r
361 public boolean isValid() {
\r
366 * Is the IP address in the CIDR?
\r
368 * @param addr the IP address as bytes in network byte order
\r
369 * @return true if the IP address matches.
\r
371 public boolean matches(byte[] addr) {
\r
372 if (!valid || addr.length != sn.length) {
\r
375 for (int i = 0; i < len; i++) {
\r
376 if (addr[i] != sn[i]) {
\r
380 if (mask != 0 && ((addr[len] ^ sn[len]) & mask) != 0) {
\r
388 * Get the list of node names for this route.
\r
392 public SortedSet<String> getNodes() {
\r
396 private Collection<String> readNodes() {
\r
397 Collection<String> set = new TreeSet<>();
\r
400 @SuppressWarnings("resource")
\r
401 Connection conn = db.getConnection();
\r
402 String sql = "select NODEID from NODESETS where SETID = ?";
\r
403 try (PreparedStatement ps = conn.prepareStatement(sql)) {
\r
404 ps.setInt(1, nodelist);
\r
405 try (ResultSet rs = ps.executeQuery()) {
\r
406 while (rs.next()) {
\r
407 int id = rs.getInt("NODEID");
\r
408 set.add(lookupNodeID(id));
\r
413 } catch (SQLException e) {
\r
414 intlogger.error(SQLEXCEPTION + e.getMessage(), e);
\r
420 * Delete the IRT route having this IngressRoutes feed ID, user ID, and subnet from the database.
\r
422 * @return true if the delete succeeded
\r
425 public boolean doDelete(Connection c) {
\r
427 PreparedStatement ps = null;
\r
429 ps = c.prepareStatement("delete from INGRESS_ROUTES where FEEDID = ? and USERID = ? and SUBNET = ?");
\r
430 ps.setInt(1, feedid);
\r
431 ps.setString(2, userid);
\r
432 ps.setString(3, subnet);
\r
436 ps = c.prepareStatement("delete from NODESETS where SETID = ?");
\r
437 ps.setInt(1, nodelist);
\r
439 } catch (SQLException e) {
\r
441 intlogger.warn("PROV0007 doDelete: " + e.getMessage(), e);
\r
447 } catch (SQLException e) {
\r
448 intlogger.error(SQLEXCEPTION + e.getMessage(), e);
\r
454 @SuppressWarnings("resource")
\r
456 public boolean doInsert(Connection c) {
\r
457 boolean rv = false;
\r
458 PreparedStatement ps = null;
\r
460 // Create the NODESETS rows & set nodelist
\r
461 int set = getMaxNodeSetID() + 1;
\r
462 this.nodelist = set;
\r
463 for (String node : nodes) {
\r
464 int id = lookupNodeName(node);
\r
465 ps = c.prepareStatement("insert into NODESETS (SETID, NODEID) values (?,?)");
\r
466 ps.setInt(1, this.nodelist);
\r
472 // Create the INGRESS_ROUTES row
\r
473 ps = c.prepareStatement(
\r
474 "insert into INGRESS_ROUTES (SEQUENCE, FEEDID, USERID, SUBNET, NODESET) values (?, ?, ?, ?, ?)");
\r
475 ps.setInt(1, this.seq);
\r
476 ps.setInt(2, this.feedid);
\r
477 ps.setString(3, this.userid);
\r
478 ps.setString(4, this.subnet);
\r
479 ps.setInt(5, this.nodelist);
\r
483 } catch (SQLException e) {
\r
484 intlogger.warn("PROV0005 doInsert: " + e.getMessage(), e);
\r
490 } catch (SQLException e) {
\r
491 intlogger.error(SQLEXCEPTION + e.getMessage(), e);
\r
498 public boolean doUpdate(Connection c) {
\r
499 return doDelete(c) && doInsert(c);
\r
503 public JSONObject asJSONObject() {
\r
504 JSONObject jo = new JSONObject();
\r
505 jo.put("feedid", feedid);
\r
506 // Note: for user and subnet, null, "", and "-" are equivalent
\r
507 if (userid != null && !userid.equals("-") && !userid.equals("")) {
\r
508 jo.put("user", userid);
\r
510 if (subnet != null && !subnet.equals("-") && !subnet.equals("")) {
\r
511 jo.put("subnet", subnet);
\r
513 jo.put("seq", seq);
\r
514 jo.put("node", nodes);
\r
519 public String getKey() {
\r
521 .format("%d/%s/%s/%d", feedid, (userid == null) ? "" : userid, (subnet == null) ? "" : subnet, seq);
\r
525 public int hashCode() {
\r
526 return toString().hashCode();
\r
530 public boolean equals(Object obj) {
\r
531 if (!(obj instanceof IngressRoute)) {
\r
534 return this.compareTo((IngressRoute) obj) == 0;
\r
538 public int compareTo(IngressRoute in) {
\r
540 throw new NullPointerException();
\r
542 int n = this.feedid - in.feedid;
\r
546 n = this.seq - in.seq;
\r
550 n = this.userid.compareTo(in.userid);
\r
554 n = this.subnet.compareTo(in.subnet);
\r
558 return this.nodes.equals(in.nodes) ? 0 : 1;
\r
562 public String toString() {
\r
563 return String.format("INGRESS: feed=%d, userid=%s, subnet=%s, seq=%d", feedid, (userid == null) ? "" : userid,
\r
564 (subnet == null) ? "" : subnet, seq);
\r