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