Merge "Add syntax check before pushing code"
[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 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
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 \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
65 \r
66     /**\r
67      * Get all IngressRoutes in the database, sorted in order according to their sequence field.\r
68      *\r
69      * @return a sorted set of IngressRoutes\r
70      */\r
71     public static SortedSet<IngressRoute> getAllIngressRoutes() {\r
72         return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES");\r
73     }\r
74 \r
75     /**\r
76      * Get all IngressRoutes in the database with a particular sequence number.\r
77      *\r
78      * @param seq the sequence number\r
79      * @return a set of IngressRoutes\r
80      */\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
84     }\r
85 \r
86     private static SortedSet<IngressRoute> getAllIngressRoutesForSQL(String sql) {\r
87         SortedSet<IngressRoute> set = new TreeSet<IngressRoute>();\r
88         try {\r
89             DB db = new DB();\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
94                     while (rs.next()) {\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
101                     }\r
102                 }\r
103             }\r
104             db.release(conn);\r
105         } catch (SQLException e) {\r
106             intlogger.error("PROV0001 getAllIngressRoutesForSQL: " + e.getMessage(), e);\r
107         }\r
108         return set;\r
109     }\r
110 \r
111     /**\r
112      * Get the maximum node set ID in use in the DB.\r
113      *\r
114      * @return the integer value of the maximum\r
115      */\r
116     public static int getMaxNodeSetID() {\r
117         return getMax("select max(SETID) as MAX from NODESETS");\r
118     }\r
119 \r
120     /**\r
121      * Get the maximum node sequence number in use in the DB.\r
122      *\r
123      * @return the integer value of the maximum\r
124      */\r
125     public static int getMaxSequence() {\r
126         return getMax("select max(SEQUENCE) as MAX from INGRESS_ROUTES");\r
127     }\r
128 \r
129     private static int getMax(String sql) {\r
130         int rv = 0;\r
131         try {\r
132             DB db = new DB();\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
137                     if (rs.next()) {\r
138                         rv = rs.getInt("MAX");\r
139                     }\r
140                 }\r
141             }\r
142             db.release(conn);\r
143         } catch (SQLException e) {\r
144             intlogger.error("PROV0002 getMax: " + e.getMessage(), e);\r
145         }\r
146         return rv;\r
147     }\r
148 \r
149     /**\r
150      * Get an Ingress Route for a particular feed ID, user, and subnet\r
151      *\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
156      */\r
157     public static IngressRoute getIngressRoute(int feedid, String user, String subnet) {\r
158         IngressRoute v = null;\r
159         PreparedStatement ps = null;\r
160         try {\r
161             DB db = new DB();\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
170                 if (rs.next()) {\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
174                 }\r
175             }\r
176             ps.close();\r
177             db.release(conn);\r
178         } catch (SQLException e) {\r
179             intlogger.error("PROV0003 getIngressRoute: " + e.getMessage(), e);\r
180         } finally {\r
181             try {\r
182                 if (ps != null) {\r
183                     ps.close();\r
184                 }\r
185             } catch (SQLException e) {\r
186                 intlogger.error(SQLEXCEPTION + e.getMessage(), e);\r
187             }\r
188         }\r
189         return v;\r
190     }\r
191 \r
192     /**\r
193      * Get a collection of all Ingress Routes with a particular sequence number.\r
194      *\r
195      * @param seq the sequence number to look for\r
196      * @return the collection (may be empty).\r
197      */\r
198     public static Collection<IngressRoute> getIngressRoute(int seq) {\r
199         Collection<IngressRoute> rv = new ArrayList<IngressRoute>();\r
200         try {\r
201             DB db = new DB();\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
206                 ps.setInt(1, seq);\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
214                     }\r
215                 }\r
216             }\r
217             db.release(conn);\r
218         } catch (SQLException e) {\r
219             intlogger.error("PROV0004 getIngressRoute: " + e.getMessage(), e);\r
220         }\r
221         return rv;\r
222     }\r
223 \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
229     }\r
230 \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
236     }\r
237 \r
238     private IngressRoute(int seq, int feedid, String user, String subnet)\r
239             throws IllegalArgumentException {\r
240         this.seq = seq;\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
245         this.nodes = null;\r
246         if (Feed.getFeedById(feedid) == null) {\r
247             throw new IllegalArgumentException("No such feed: " + feedid);\r
248         }\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
253             }\r
254         }\r
255     }\r
256 \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
269         }\r
270     }\r
271 \r
272     /**\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
276      *\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
280      */\r
281     public boolean matches(int feedid, HttpServletRequest req) {\r
282         // Check feedid\r
283         if (this.feedid != feedid) {\r
284             return false;\r
285         }\r
286 \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
292                 return false;\r
293             }\r
294             String t = new String(Base64.decodeBase64(credentials.substring(6)));\r
295             int ix = t.indexOf(':');\r
296             if (ix >= 0) {\r
297                 t = t.substring(0, ix);\r
298             }\r
299             if (!t.equals(this.userid)) {\r
300                 return false;\r
301             }\r
302         }\r
303 \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
306             try {\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
312                 return false;\r
313             }\r
314         }\r
315         return true;\r
316     }\r
317 \r
318     /**\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
321      */\r
322     public class SubnetMatcher {\r
323 \r
324         private byte[] sn;\r
325         private int len;\r
326         private int mask;\r
327         private boolean valid;\r
328 \r
329         /**\r
330          * Construct a subnet matcher given a CIDR\r
331          *\r
332          * @param subnet The CIDR to match\r
333          */\r
334         public SubnetMatcher(String subnet) {\r
335             int i = subnet.lastIndexOf('/');\r
336             if (i == -1) {\r
337                 try {\r
338                     sn = InetAddress.getByName(subnet).getAddress();\r
339                     len = sn.length;\r
340                     valid = true;\r
341                 } catch (UnknownHostException e) {\r
342                     intlogger.error("PROV0008 SubnetMatcher: " + e.getMessage(), e);\r
343                     len = 0;\r
344                     valid = false;\r
345                 }\r
346                 mask = 0;\r
347             } else {\r
348                 int n = Integer.parseInt(subnet.substring(i + 1));\r
349                 try {\r
350                     sn = InetAddress.getByName(subnet.substring(0, i)).getAddress();\r
351                     valid = true;\r
352                 } catch (UnknownHostException e) {\r
353                     intlogger.error("PROV0008 SubnetMatcher: " + e.getMessage(), e);\r
354                     valid = false;\r
355                 }\r
356                 len = n / 8;\r
357                 mask = ((0xff00) >> (n % 8)) & 0xff;\r
358             }\r
359         }\r
360 \r
361         public boolean isValid() {\r
362             return valid;\r
363         }\r
364 \r
365         /**\r
366          * Is the IP address in the CIDR?\r
367          *\r
368          * @param addr the IP address as bytes in network byte order\r
369          * @return true if the IP address matches.\r
370          */\r
371         public boolean matches(byte[] addr) {\r
372             if (!valid || addr.length != sn.length) {\r
373                 return false;\r
374             }\r
375             for (int i = 0; i < len; i++) {\r
376                 if (addr[i] != sn[i]) {\r
377                     return false;\r
378                 }\r
379             }\r
380             if (mask != 0 && ((addr[len] ^ sn[len]) & mask) != 0) {\r
381                 return false;\r
382             }\r
383             return true;\r
384         }\r
385     }\r
386 \r
387     /**\r
388      * Get the list of node names for this route.\r
389      *\r
390      * @return the list\r
391      */\r
392     public SortedSet<String> getNodes() {\r
393         return this.nodes;\r
394     }\r
395 \r
396     private Collection<String> readNodes() {\r
397         Collection<String> set = new TreeSet<>();\r
398         try {\r
399             DB db = new DB();\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
409                     }\r
410                 }\r
411             }\r
412             db.release(conn);\r
413         } catch (SQLException e) {\r
414             intlogger.error(SQLEXCEPTION + e.getMessage(), e);\r
415         }\r
416         return set;\r
417     }\r
418 \r
419     /**\r
420      * Delete the IRT route having this IngressRoutes feed ID, user ID, and subnet from the database.\r
421      *\r
422      * @return true if the delete succeeded\r
423      */\r
424     @Override\r
425     public boolean doDelete(Connection c) {\r
426         boolean rv = true;\r
427         PreparedStatement ps = null;\r
428         try {\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
433             ps.execute();\r
434             ps.close();\r
435 \r
436             ps = c.prepareStatement("delete from NODESETS where SETID = ?");\r
437             ps.setInt(1, nodelist);\r
438             ps.execute();\r
439         } catch (SQLException e) {\r
440             rv = false;\r
441             intlogger.warn("PROV0007 doDelete: " + e.getMessage(), e);\r
442         } finally {\r
443             try {\r
444                 if (ps != null) {\r
445                     ps.close();\r
446                 }\r
447             } catch (SQLException e) {\r
448                 intlogger.error(SQLEXCEPTION + e.getMessage(), e);\r
449             }\r
450         }\r
451         return rv;\r
452     }\r
453 \r
454     @SuppressWarnings("resource")\r
455     @Override\r
456     public boolean doInsert(Connection c) {\r
457         boolean rv = false;\r
458         PreparedStatement ps = null;\r
459         try {\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
467                 ps.setInt(2, id);\r
468                 ps.execute();\r
469                 ps.close();\r
470             }\r
471 \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
480             ps.execute();\r
481             ps.close();\r
482             rv = true;\r
483         } catch (SQLException e) {\r
484             intlogger.warn("PROV0005 doInsert: " + e.getMessage(), e);\r
485         } finally {\r
486             try {\r
487                 if (ps != null) {\r
488                     ps.close();\r
489                 }\r
490             } catch (SQLException e) {\r
491                 intlogger.error(SQLEXCEPTION + e.getMessage(), e);\r
492             }\r
493         }\r
494         return rv;\r
495     }\r
496 \r
497     @Override\r
498     public boolean doUpdate(Connection c) {\r
499         return doDelete(c) && doInsert(c);\r
500     }\r
501 \r
502     @Override\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
509         }\r
510         if (subnet != null && !subnet.equals("-") && !subnet.equals("")) {\r
511             jo.put("subnet", subnet);\r
512         }\r
513         jo.put("seq", seq);\r
514         jo.put("node", nodes);\r
515         return jo;\r
516     }\r
517 \r
518     @Override\r
519     public String getKey() {\r
520         return String\r
521                 .format("%d/%s/%s/%d", feedid, (userid == null) ? "" : userid, (subnet == null) ? "" : subnet, seq);\r
522     }\r
523 \r
524     @Override\r
525     public int hashCode() {\r
526         return toString().hashCode();\r
527     }\r
528 \r
529     @Override\r
530     public boolean equals(Object obj) {\r
531         if (!(obj instanceof IngressRoute)) {\r
532             return false;\r
533         }\r
534         return this.compareTo((IngressRoute) obj) == 0;\r
535     }\r
536 \r
537     @Override\r
538     public int compareTo(IngressRoute in) {\r
539         if (in == null) {\r
540             throw new NullPointerException();\r
541         }\r
542         int n = this.feedid - in.feedid;\r
543         if (n != 0) {\r
544             return n;\r
545         }\r
546         n = this.seq - in.seq;\r
547         if (n != 0) {\r
548             return n;\r
549         }\r
550         n = this.userid.compareTo(in.userid);\r
551         if (n != 0) {\r
552             return n;\r
553         }\r
554         n = this.subnet.compareTo(in.subnet);\r
555         if (n != 0) {\r
556             return n;\r
557         }\r
558         return this.nodes.equals(in.nodes) ? 0 : 1;\r
559     }\r
560 \r
561     @Override\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
565     }\r
566 }\r