Replacing att.com namespace
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / beans / IngressRoute.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\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
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \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
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning.beans;\r
26 \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
39 \r
40 import javax.servlet.http.HttpServletRequest;\r
41 \r
42 import org.apache.commons.codec.binary.Base64;\r
43 import org.apache.log4j.Logger;\r
44 import org.json.JSONArray;\r
45 import org.json.JSONObject;\r
46 import org.onap.dmaap.datarouter.provisioning.utils.DB;\r
47 \r
48 /**\r
49  * The representation of one route in the Ingress Route Table.\r
50  *\r
51  * @author Robert P. Eby\r
52  * @version $Id: IngressRoute.java,v 1.3 2013/12/16 20:30:23 eby Exp $\r
53  */\r
54 public class IngressRoute extends NodeClass implements Comparable<IngressRoute> {\r
55         private static Logger intlogger = Logger.getLogger("org.onap.dmaap.datarouter.provisioning.internal");\r
56         private final int seq;\r
57         private final int feedid;\r
58         private final String userid;\r
59         private final String subnet;\r
60         private int nodelist;\r
61         private SortedSet<String> nodes;\r
62 \r
63         /**\r
64          * Get all IngressRoutes in the database, sorted in order according to their sequence field.\r
65          * @return a sorted set of IngressRoutes\r
66          */\r
67         public static SortedSet<IngressRoute> getAllIngressRoutes() {\r
68                 return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES");\r
69         }\r
70         /**\r
71          * Get all IngressRoutes in the database with a particular sequence number.\r
72          * @param seq the sequence number\r
73          * @return a set of IngressRoutes\r
74          */\r
75         public static Set<IngressRoute> getIngressRoutesForSeq(int seq) {\r
76                 return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = "+seq);\r
77         }\r
78         private static SortedSet<IngressRoute> getAllIngressRoutesForSQL(String sql) {\r
79                 SortedSet<IngressRoute> set = new TreeSet<IngressRoute>();\r
80                 try {\r
81                         DB db = new DB();\r
82                         @SuppressWarnings("resource")\r
83                         Connection conn = db.getConnection();\r
84                         Statement  stmt = conn.createStatement();\r
85                         ResultSet rs = stmt.executeQuery(sql);\r
86                         while (rs.next()) {\r
87                                 int seq       = rs.getInt("SEQUENCE");\r
88                                 int feedid    = rs.getInt("FEEDID");\r
89                                 String user   = rs.getString("USERID");\r
90                                 String subnet = rs.getString("SUBNET");\r
91                                 int nodeset   = rs.getInt("NODESET");\r
92                                 set.add(new IngressRoute(seq, feedid, user, subnet, nodeset));\r
93                         }\r
94                         rs.close();\r
95                         stmt.close();\r
96                         db.release(conn);\r
97                 } catch (SQLException e) {\r
98                         e.printStackTrace();\r
99                 }\r
100                 return set;\r
101         }\r
102 \r
103         /**\r
104          * Get the maximum node set ID in use in the DB.\r
105          * @return the integer value of the maximum\r
106          */\r
107         public static int getMaxNodeSetID() {\r
108                 return getMax("select max(SETID) as MAX from NODESETS");\r
109         }\r
110         /**\r
111          * Get the maximum node sequence number in use in the DB.\r
112          * @return the integer value of the maximum\r
113          */\r
114         public static int getMaxSequence() {\r
115                 return getMax("select max(SEQUENCE) as MAX from INGRESS_ROUTES");\r
116         }\r
117         private static int getMax(String sql) {\r
118                 int rv = 0;\r
119                 try {\r
120                         DB db = new DB();\r
121                         @SuppressWarnings("resource")\r
122                         Connection conn = db.getConnection();\r
123                         Statement  stmt = conn.createStatement();\r
124                         ResultSet rs = stmt.executeQuery(sql);\r
125                         if (rs.next()) {\r
126                                 rv = rs.getInt("MAX");\r
127                         }\r
128                         rs.close();\r
129                         stmt.close();\r
130                         db.release(conn);\r
131                 } catch (SQLException e) {\r
132                         e.printStackTrace();\r
133                 }\r
134                 return rv;\r
135         }\r
136 \r
137         /**\r
138          * Get an Ingress Route for a particular feed ID, user, and subnet\r
139          * @param feedid the Feed ID to look for\r
140          * @param user the user name to look for\r
141          * @param subnet the subnet to look for\r
142          * @return the Ingress Route, or null of there is none\r
143          */\r
144         public static IngressRoute getIngressRoute(int feedid, String user, String subnet) {\r
145                 IngressRoute v = null;\r
146                 PreparedStatement ps = null;\r
147                 try {\r
148                         DB db = new DB();\r
149                         @SuppressWarnings("resource")\r
150                         Connection conn = db.getConnection();\r
151                         String sql = "select SEQUENCE, NODESET from INGRESS_ROUTES where FEEDID = ? AND USERID = ? and SUBNET = ?";\r
152                         ps = conn.prepareStatement(sql);\r
153                         ps.setInt(1, feedid);\r
154                         ps.setString(2, user);\r
155                         ps.setString(3, subnet);\r
156                         ResultSet rs = ps.executeQuery();\r
157                         if (rs.next()) {\r
158                                 int seq = rs.getInt("SEQUENCE");\r
159                                 int nodeset = rs.getInt("NODESET");\r
160                                 v = new IngressRoute(seq, feedid, user, subnet, nodeset);\r
161                         }\r
162                         rs.close();\r
163                         ps.close();\r
164                         db.release(conn);\r
165                 } catch (SQLException e) {\r
166                         e.printStackTrace();\r
167                 } finally {\r
168                         try {\r
169                                 ps.close();\r
170                         } catch (SQLException e) {\r
171                                 e.printStackTrace();\r
172                         }\r
173                 }\r
174                 return v;\r
175         }\r
176 \r
177         /**\r
178          * Get a collection of all Ingress Routes with a particular sequence number.\r
179          * @param seq the sequence number to look for\r
180          * @return the collection (may be empty).\r
181          */\r
182         public static Collection<IngressRoute> getIngressRoute(int seq) {\r
183                 Collection<IngressRoute> rv = new ArrayList<IngressRoute>();\r
184                 PreparedStatement ps = null;\r
185                 try {\r
186                         DB db = new DB();\r
187                         @SuppressWarnings("resource")\r
188                         Connection conn = db.getConnection();\r
189                         String sql = "select FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = ?";\r
190                         ps = conn.prepareStatement(sql);\r
191                         ps.setInt(1, seq);\r
192                         ResultSet rs = ps.executeQuery();\r
193                         while (rs.next()) {\r
194                                 int feedid = rs.getInt("FEEDID");\r
195                                 String user  = rs.getString("USERID");\r
196                                 String subnet = rs.getString("SUBNET");\r
197                                 int nodeset = rs.getInt("NODESET");\r
198                                 rv.add(new IngressRoute(seq, feedid, user, subnet, nodeset));\r
199                         }\r
200                         rs.close();\r
201                         ps.close();\r
202                         db.release(conn);\r
203                 } catch (SQLException e) {\r
204                         e.printStackTrace();\r
205                 } finally {\r
206                         try {\r
207                                 ps.close();\r
208                         } catch (SQLException e) {\r
209                                 e.printStackTrace();\r
210                         }\r
211                 }\r
212                 return rv;\r
213         }\r
214 \r
215         public IngressRoute(int seq, int feedid, String user, String subnet, Collection<String> nodes)\r
216                 throws IllegalArgumentException\r
217         {\r
218                 this(seq, feedid, user, subnet);\r
219                 this.nodelist = -1;\r
220                 this.nodes = new TreeSet<String>(nodes);\r
221         }\r
222 \r
223         public IngressRoute(int seq, int feedid, String user, String subnet, int nodeset)\r
224                 throws IllegalArgumentException\r
225         {\r
226                 this(seq, feedid, user, subnet);\r
227                 this.nodelist = nodeset;\r
228                 this.nodes = new TreeSet<String>(readNodes());\r
229         }\r
230 \r
231         private IngressRoute(int seq, int feedid, String user, String subnet)\r
232                 throws IllegalArgumentException\r
233         {\r
234                 this.seq = seq;\r
235                 this.feedid = feedid;\r
236                 this.userid = (user == null) ? "-" : user;\r
237                 this.subnet = (subnet == null) ? "-" : subnet;\r
238                 this.nodelist = -1;\r
239                 this.nodes = null;\r
240                 if (Feed.getFeedById(feedid) == null)\r
241                         throw new IllegalArgumentException("No such feed: "+feedid);\r
242                 if (!this.subnet.equals("-")) {\r
243                         SubnetMatcher sm = new SubnetMatcher(subnet);\r
244                         if (!sm.isValid())\r
245                                 throw new IllegalArgumentException("Invalid subnet: "+subnet);\r
246                 }\r
247         }\r
248 \r
249         public IngressRoute(JSONObject jo) {\r
250                 this.seq    = jo.optInt("seq");\r
251                 this.feedid = jo.optInt("feedid");\r
252                 String t    = jo.optString("user");\r
253                 this.userid = t.equals("") ? "-" : t;\r
254                 t           = jo.optString("subnet");\r
255                 this.subnet = t.equals("") ? "-" : t;\r
256                 this.nodelist = -1;\r
257                 this.nodes = new TreeSet<String>();\r
258                 JSONArray ja = jo.getJSONArray("node");\r
259                 for (int i = 0; i < ja.length(); i++)\r
260                         this.nodes.add(ja.getString(i));\r
261         }\r
262         /**\r
263          * Does this particular IngressRoute match a request, represented by feedid and req?\r
264          * To match, <i>feedid</i> must match the feed ID in the route, the user in the route\r
265          * (if specified) must match the user in the request, and the subnet in the route (if specified)\r
266          * must match the subnet from the request.\r
267          * @param feedid the feedid for this request\r
268          * @param req the remainder of the request\r
269          * @return true if a match, false otherwise\r
270          */\r
271         public boolean matches(int feedid, HttpServletRequest req) {\r
272                 // Check feedid\r
273                 if (this.feedid != feedid)\r
274                         return false;\r
275 \r
276                 // Get user from request and compare\r
277                 // Note: we don't check the password; the node will do that\r
278                 if (userid.length() > 0 && !userid.equals("-")) {\r
279                         String credentials = req.getHeader("Authorization");\r
280                         if (credentials == null || !credentials.startsWith("Basic "))\r
281                                 return false;\r
282                         String t = new String(Base64.decodeBase64(credentials.substring(6)));\r
283                         int ix = t.indexOf(':');\r
284                         if (ix >= 0)\r
285                                 t = t.substring(0, ix);\r
286                         if (!t.equals(this.userid))\r
287                                 return false;\r
288                 }\r
289 \r
290                 // If this route has a subnet, match it against the requester's IP addr\r
291                 if (subnet.length() > 0 && !subnet.equals("-")) {\r
292                         try {\r
293                                 InetAddress inet = InetAddress.getByName(req.getRemoteAddr());\r
294                                 SubnetMatcher sm = new SubnetMatcher(subnet);\r
295                                 return sm.matches(inet.getAddress());\r
296                         } catch (UnknownHostException e) {\r
297                                 return false;\r
298                         }\r
299                 }\r
300                 return true;\r
301         }\r
302 \r
303         /**\r
304          *      Compare IP addresses as byte arrays to a subnet specified as a CIDR.\r
305          *  Taken from org.onap.dmaap.datarouter.node.SubnetMatcher and modified somewhat.\r
306          */\r
307         public class SubnetMatcher {\r
308                 private byte[]  sn;\r
309                 private int     len;\r
310                 private int     mask;\r
311                 private boolean valid;\r
312 \r
313                 /**\r
314                  * Construct a subnet matcher given a CIDR\r
315                  * @param subnet        The CIDR to match\r
316                  */\r
317                 public SubnetMatcher(String subnet) {\r
318                         int i = subnet.lastIndexOf('/');\r
319                         if (i == -1) {\r
320                                 try {\r
321                                         sn = InetAddress.getByName(subnet).getAddress();\r
322                                         len = sn.length;\r
323                                         valid = true;\r
324                                 } catch (UnknownHostException e) {\r
325                                         len = 0;\r
326                                         valid = false;\r
327                                 }\r
328                                 mask = 0;\r
329                         } else {\r
330                                 int n = Integer.parseInt(subnet.substring(i + 1));\r
331                                 try {\r
332                                         sn = InetAddress.getByName(subnet.substring(0, i)).getAddress();\r
333                                         valid = true;\r
334                                 } catch (UnknownHostException e) {\r
335                                         valid = false;\r
336                                 }\r
337                                 len = n / 8;\r
338                                 mask = ((0xff00) >> (n % 8)) & 0xff;\r
339                         }\r
340                 }\r
341                 public boolean isValid() {\r
342                         return valid;\r
343                 }\r
344                 /**\r
345                  *      Is the IP address in the CIDR?\r
346                  *      @param addr the IP address as bytes in network byte order\r
347                  *      @return true if the IP address matches.\r
348                  */\r
349                 public boolean matches(byte[] addr) {\r
350                         if (!valid || addr.length != sn.length) {\r
351                                 return false;\r
352                         }\r
353                         for (int i = 0; i < len; i++) {\r
354                                 if (addr[i] != sn[i]) {\r
355                                         return false;\r
356                                 }\r
357                         }\r
358                         if (mask != 0 && ((addr[len] ^ sn[len]) & mask) != 0) {\r
359                                 return false;\r
360                         }\r
361                         return true;\r
362                 }\r
363         }\r
364 \r
365         /**\r
366          * Get the list of node names for this route.\r
367          * @return the list\r
368          */\r
369         public SortedSet<String> getNodes() {\r
370                 return this.nodes;\r
371         }\r
372 \r
373         private Collection<String> readNodes() {\r
374                 Collection<String> set = new TreeSet<String>();\r
375                 PreparedStatement ps = null;\r
376                 try {\r
377                         DB db = new DB();\r
378                         @SuppressWarnings("resource")\r
379                         Connection conn = db.getConnection();\r
380                         Statement  stmt = conn.createStatement();\r
381                         String sql = "select NODEID from NODESETS where SETID = ?";\r
382                         ps = conn.prepareStatement(sql);\r
383                         ps.setInt(1, nodelist);\r
384                         ResultSet rs = ps.executeQuery();\r
385                         while (rs.next()) {\r
386                                 int id = rs.getInt("NODEID");\r
387                                 set.add(lookupNodeID(id));\r
388                         }\r
389                         rs.close();\r
390                         stmt.close();\r
391                         db.release(conn);\r
392                 } catch (SQLException e) {\r
393                         e.printStackTrace();\r
394                 } finally {\r
395                         try {\r
396                                 ps.close();\r
397                         } catch (SQLException e) {\r
398                                 e.printStackTrace();\r
399                         }\r
400                 }\r
401                 return set;\r
402         }\r
403 \r
404         /**\r
405          * Delete the IRT route having this IngressRoutes feed ID, user ID, and subnet from the database.\r
406          * @return true if the delete succeeded\r
407          */\r
408         @Override\r
409         public boolean doDelete(Connection c) {\r
410                 boolean rv = true;\r
411                 PreparedStatement ps = null;\r
412                 try {\r
413                         ps = c.prepareStatement("delete from INGRESS_ROUTES where FEEDID = ? and USERID = ? and SUBNET = ?");\r
414                         ps.setInt(1, feedid);\r
415                         ps.setString(2, userid);\r
416                         ps.setString(3, subnet);\r
417                         ps.execute();\r
418                         ps.close();\r
419 \r
420                         ps = c.prepareStatement("delete from NODESETS where SETID = ?");\r
421                         ps.setInt(1, nodelist);\r
422                         ps.execute();\r
423                 } catch (SQLException e) {\r
424                         rv = false;\r
425                         intlogger.warn("PROV0007 doDelete: "+e.getMessage());\r
426                         e.printStackTrace();\r
427                 } finally {\r
428                         try {\r
429                                 ps.close();\r
430                         } catch (SQLException e) {\r
431                                 e.printStackTrace();\r
432                         }\r
433                 }\r
434                 return rv;\r
435         }\r
436 \r
437         @SuppressWarnings("resource")\r
438         @Override\r
439         public boolean doInsert(Connection c) {\r
440                 boolean rv = false;\r
441                 PreparedStatement ps = null;\r
442                 try {\r
443                         // Create the NODESETS rows & set nodelist\r
444                         int set = getMaxNodeSetID() + 1;\r
445                         this.nodelist = set;\r
446                         for (String node : nodes) {\r
447                                 int id = lookupNodeName(node);\r
448                                 ps = c.prepareStatement("insert into NODESETS (SETID, NODEID) values (?,?)");\r
449                                 ps.setInt(1, this.nodelist);\r
450                                 ps.setInt(2, id);\r
451                                 ps.execute();\r
452                                 ps.close();\r
453                         }\r
454 \r
455                         // Create the INGRESS_ROUTES row\r
456                         ps = c.prepareStatement("insert into INGRESS_ROUTES (SEQUENCE, FEEDID, USERID, SUBNET, NODESET) values (?, ?, ?, ?, ?)");\r
457                         ps.setInt(1, this.seq);\r
458                         ps.setInt(2, this.feedid);\r
459                         ps.setString(3, this.userid);\r
460                         ps.setString(4, this.subnet);\r
461                         ps.setInt(5, this.nodelist);\r
462                         ps.execute();\r
463                         ps.close();\r
464                         rv = true;\r
465                 } catch (SQLException e) {\r
466                         intlogger.warn("PROV0005 doInsert: "+e.getMessage());\r
467                         e.printStackTrace();\r
468                 } finally {\r
469                         try {\r
470                                 ps.close();\r
471                         } catch (SQLException e) {\r
472                                 e.printStackTrace();\r
473                         }\r
474                 }\r
475                 return rv;\r
476         }\r
477 \r
478         @Override\r
479         public boolean doUpdate(Connection c) {\r
480                 return doDelete(c) && doInsert(c);\r
481         }\r
482 \r
483         @Override\r
484         public JSONObject asJSONObject() {\r
485                 JSONObject jo = new JSONObject();\r
486                 jo.put("feedid", feedid);\r
487                 // Note: for user and subnet, null, "", and "-" are equivalent\r
488                 if (userid != null && !userid.equals("-") && !userid.equals(""))\r
489                         jo.put("user", userid);\r
490                 if (subnet != null && !subnet.equals("-") && !subnet.equals(""))\r
491                         jo.put("subnet", subnet);\r
492                 jo.put("seq", seq);\r
493                 jo.put("node", nodes);\r
494                 return jo;\r
495         }\r
496 \r
497         @Override\r
498         public String getKey() {\r
499                 return String.format("%d/%s/%s/%d", feedid, (userid == null)?"":userid, (subnet == null)?"":subnet, seq);\r
500         }\r
501 \r
502         @Override\r
503         public int hashCode() {\r
504                 return toString().hashCode();\r
505         }\r
506 \r
507         @Override\r
508         public boolean equals(Object obj) {\r
509                 try {\r
510                         if (!(obj instanceof IngressRoute))\r
511                                 return false;\r
512                         return this.compareTo((IngressRoute) obj) == 0;\r
513                 } catch (NullPointerException e) {\r
514                         return false;\r
515                 }\r
516         }\r
517 \r
518         @Override\r
519         public int compareTo(IngressRoute in) {\r
520                 if (in == null)\r
521                         throw new NullPointerException();\r
522                 int n = this.feedid - in.feedid;\r
523                 if (n != 0)\r
524                         return n;\r
525                 n = this.seq - in.seq;\r
526                 if (n != 0)\r
527                         return n;\r
528                 n = this.userid.compareTo(in.userid);\r
529                 if (n != 0)\r
530                         return n;\r
531                 n = this.subnet.compareTo(in.subnet);\r
532                 if (n != 0)\r
533                         return n;\r
534                 return this.nodes.equals(in.nodes) ? 0 : 1;\r
535         }\r
536 \r
537         @Override\r
538         public String toString() {\r
539                 return String.format("INGRESS: feed=%d, userid=%s, subnet=%s, seq=%d", feedid, (userid == null)?"":userid, (subnet == null)?"":subnet, seq);\r
540         }\r
541 }\r