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