Merge "Fix Vulnerabilities in BaseServlet"
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / RouteServlet.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;\r
26 \r
27 import java.io.IOException;\r
28 import java.util.Set;\r
29 \r
30 import javax.servlet.http.HttpServletRequest;\r
31 import javax.servlet.http.HttpServletResponse;\r
32 \r
33 import org.json.JSONException;\r
34 import org.json.JSONObject;\r
35 import org.onap.dmaap.datarouter.provisioning.beans.Deleteable;\r
36 import org.onap.dmaap.datarouter.provisioning.beans.EgressRoute;\r
37 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;\r
38 import org.onap.dmaap.datarouter.provisioning.beans.IngressRoute;\r
39 import org.onap.dmaap.datarouter.provisioning.beans.Insertable;\r
40 import org.onap.dmaap.datarouter.provisioning.beans.NetworkRoute;\r
41 import org.onap.dmaap.datarouter.provisioning.beans.NodeClass;\r
42 \r
43 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;\r
44 \r
45 /**\r
46  * <p>\r
47  * This servlet handles requests to URLs under /internal/route/ on the provisioning server.\r
48  * This part of the URL tree is used to manipulate the Data Router routing tables.\r
49  * These include:\r
50  * </p>\r
51  * <div class="contentContainer">\r
52  * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">\r
53  * <caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>\r
54  * <tr>\r
55  *   <th class="colFirst" width="35%">URL Path</th>\r
56  *   <th class="colOne">Method</th>\r
57  *   <th class="colLast">Purpose</th>\r
58  * </tr>\r
59  * <tr class="altColor">\r
60  *   <td class="colFirst">/internal/route/</td>\r
61  *   <td class="colOne">GET</td>\r
62  *   <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>\r
63  * </tr>\r
64  * <tr class="rowColor">\r
65  *   <td class="colFirst" rowspan="2">/internal/route/ingress/</td>\r
66  *   <td class="colOne">GET</td>\r
67  *   <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>\r
68  * </tr>\r
69  * <tr class="rowColor">\r
70  *   <td class="colOne">POST</td>\r
71  *   <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>\r
72  * <tr class="altColor">\r
73  *   <td class="colFirst" rowspan="2">/internal/route/egress/</td>\r
74  *   <td class="colOne">GET</td>\r
75  *   <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>\r
76  * </tr>\r
77  * <tr class="altColor">\r
78  *   <td class="colOne">POST</td>\r
79  *   <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>\r
80  * <tr class="rowColor">\r
81  *   <td class="colFirst" rowspan="2">/internal/route/network/</td>\r
82  *   <td class="colOne">GET</td>\r
83  *   <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>\r
84  * </tr>\r
85  * <tr class="rowColor">\r
86  *   <td class="colOne">POST</td>\r
87  *   <td class="colLast">used to create a new entry in the network routing table (NRT).</td>\r
88  * </tr>\r
89  * <tr class="altColor">\r
90  *   <td class="colFirst">/internal/route/ingress/&lt;feed&gt;/&lt;user&gt;/&lt;subnet&gt;</td>\r
91  *   <td class="colOne">DELETE</td>\r
92  *   <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.\r
93  *   The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>\r
94  * </tr>\r
95  * <tr class="rowColor">\r
96  *   <td class="colFirst">/internal/route/ingress/&lt;seq&gt;</td>\r
97  *   <td class="colOne">DELETE</td>\r
98  *   <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>\r
99  * </tr>\r
100  * <tr class="altColor">\r
101  *   <td class="colFirst">/internal/route/egress/&lt;sub&gt;</td>\r
102  *   <td class="colOne">DELETE</td>\r
103  *   <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>\r
104  * </tr>\r
105  * <tr class="rowColor">\r
106  *   <td class="colFirst">/internal/route/network/&lt;fromnode&gt;/&lt;tonode&gt;</td>\r
107  *   <td class="colOne">DELETE</td>\r
108  *   <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>\r
109  *   and <i>tonode</i>.</td>\r
110  * </tr>\r
111  * </table>\r
112  * <p>\r
113  * Authorization to use these URLs is a little different than for other URLs on the provisioning server.\r
114  * For the most part, the IP address that the request comes from should be either:\r
115  * </p>\r
116  * <ol>\r
117  * <li>an IP address of a provisioning server, or</li>\r
118  * <li>the IP address of a node, or</li>\r
119  * <li>an IP address from the "<i>special subnet</i>" which is configured with\r
120  * the PROV_SPECIAL_SUBNET parameter.\r
121  * </ol>\r
122  * <p>\r
123  * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the\r
124  * active server (using the {@link ProxyServlet}) if it is up and reachable.\r
125  * </p>\r
126  *\r
127  * @author Robert Eby\r
128  * @version $Id$\r
129  */\r
130 @SuppressWarnings("serial")\r
131 public class RouteServlet extends ProxyServlet {\r
132     /**\r
133      * DELETE route table entries by deleting part of the route table tree.\r
134      */\r
135     @Override\r
136     public void doDelete(HttpServletRequest req, HttpServletResponse resp) {\r
137         EventLogRecord elr = new EventLogRecord(req);\r
138         if (!isAuthorizedForInternal(req)) {\r
139             elr.setMessage("Unauthorized.");\r
140             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
141             eventlogger.info(elr);\r
142             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, "Unauthorized.", eventlogger);\r
143             return;\r
144         }\r
145         if (isProxyOK(req) && isProxyServer()) {\r
146             try {\r
147                 super.doDelete(req, resp);\r
148             } catch (IOException ioe) {\r
149                 eventlogger.error("IOException" + ioe.getMessage());\r
150             }\r
151             return;\r
152         }\r
153 \r
154         String path = req.getPathInfo();\r
155         String[] parts = path.substring(1).split("/");\r
156         Deleteable[] d = null;\r
157         if (parts[0].equals("ingress")) {\r
158             if (parts.length == 4) {\r
159                 // /internal/route/ingress/<feed>/<user>/<subnet>\r
160                 try {\r
161                     int feedid = Integer.parseInt(parts[1]);\r
162                     IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));\r
163                     if (er == null) {\r
164                         sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.", eventlogger);\r
165                         return;\r
166                     }\r
167                     d = new Deleteable[] { er };\r
168                 } catch (NumberFormatException e) {\r
169                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid feed ID in 'delete ingress' command.", eventlogger);\r
170                     return;\r
171                 }\r
172             } else if (parts.length == 2) {\r
173                 // /internal/route/ingress/<seq>\r
174                 try {\r
175                     int seq = Integer.parseInt(parts[1]);\r
176                     Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);\r
177                     d = set.toArray(new Deleteable[0]);\r
178                 } catch (NumberFormatException e) {\r
179                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid sequence number in 'delete ingress' command.", eventlogger);\r
180                     return;\r
181                 }\r
182             } else {\r
183                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete ingress' command.", eventlogger);\r
184                 return;\r
185             }\r
186         } else if (parts[0].equals("egress")) {\r
187             if (parts.length == 2) {\r
188                 // /internal/route/egress/<sub>\r
189                 try {\r
190                     int subid = Integer.parseInt(parts[1]);\r
191                     EgressRoute er = EgressRoute.getEgressRoute(subid);\r
192                     if (er == null) {\r
193                         sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "The specified egress route does not exist.", eventlogger);\r
194                         return;\r
195                     }\r
196                     d = new Deleteable[] { er };\r
197                 } catch (NumberFormatException e) {\r
198                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid sub ID in 'delete egress' command.", eventlogger);\r
199                     return;\r
200                 }\r
201             } else {\r
202                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete egress' command.", eventlogger);\r
203                 return;\r
204             }\r
205         } else if (parts[0].equals("network")) {\r
206             if (parts.length == 3) {\r
207                 // /internal/route/network/<from>/<to>\r
208                 try {//\r
209                     NetworkRoute nr = new NetworkRoute(\r
210                         NodeClass.normalizeNodename(parts[1]),\r
211                         NodeClass.normalizeNodename(parts[2])\r
212                     );\r
213                     d = new Deleteable[] { nr };\r
214                 } catch (IllegalArgumentException e) {\r
215                     sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "The specified network route does not exist.", eventlogger);\r
216                     return;\r
217                 }\r
218             } else {\r
219                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete network' command.", eventlogger);\r
220                 return;\r
221             }\r
222         }\r
223         if (d == null) {\r
224             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Bad URL.", eventlogger);\r
225             return;\r
226         }\r
227         boolean rv = true;\r
228         for (Deleteable dd : d) {\r
229             rv &= doDelete(dd);\r
230         }\r
231         if (rv) {\r
232             elr.setResult(HttpServletResponse.SC_OK);\r
233             eventlogger.info(elr);\r
234             resp.setStatus(HttpServletResponse.SC_OK);\r
235             provisioningDataChanged();\r
236             provisioningParametersChanged();\r
237         } else {\r
238             // Something went wrong with the DELETE\r
239             elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
240             eventlogger.info(elr);\r
241             sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, eventlogger);\r
242         }\r
243     }\r
244     /**\r
245      * GET route table entries from the route table tree specified by the URL path.\r
246      */\r
247     @Override\r
248     public void doGet(HttpServletRequest req, HttpServletResponse resp) {\r
249         EventLogRecord elr = new EventLogRecord(req);\r
250         if (!isAuthorizedForInternal(req)) {\r
251             elr.setMessage("Unauthorized.");\r
252             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
253             eventlogger.info(elr);\r
254             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, "Unauthorized.", eventlogger);\r
255             return;\r
256         }\r
257         if (isProxyOK(req) && isProxyServer()) {\r
258             try {\r
259                 super.doGet(req, resp);\r
260             } catch (IOException ioe) {\r
261                 eventlogger.error("IOException" + ioe.getMessage());\r
262             }\r
263             return;\r
264         }\r
265 \r
266         String path = req.getPathInfo();\r
267         if (!path.endsWith("/"))\r
268             path += "/";\r
269         if (!path.equals("/") && !path.equals("/ingress/") && !path.equals("/egress/") && !path.equals("/network/")) {\r
270             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Bad URL.", eventlogger);\r
271             return;\r
272         }\r
273 \r
274         StringBuilder sb = new StringBuilder("{\n");\r
275         String px2 = "";\r
276         if (path.equals("/") || path.equals("/ingress/")) {\r
277             String pfx = "\n";\r
278             sb.append("\"ingress\": [");\r
279             for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {\r
280                 sb.append(pfx);\r
281                 sb.append(in.asJSONObject().toString());\r
282                 pfx = ",\n";\r
283             }\r
284             sb.append("\n]");\r
285             px2 = ",\n";\r
286         }\r
287 \r
288         if (path.equals("/") || path.equals("/egress/")) {\r
289             String pfx = "\n";\r
290             sb.append(px2);\r
291             sb.append("\"egress\": {");\r
292             for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {\r
293                 JSONObject jx = eg.asJSONObject();\r
294                 for (String key : jx.keySet()) {\r
295                     sb.append(pfx);\r
296                     sb.append("  \"").append(key).append("\": ");\r
297                     try {\r
298                         sb.append("\"").append(jx.getString(key)).append("\"");\r
299                     } catch (JSONException je) {\r
300                         eventlogger.error("JSONException" + je.getMessage());\r
301                     }\r
302                     pfx = ",\n";\r
303                 }\r
304             }\r
305             sb.append("\n}");\r
306             px2 = ",\n";\r
307         }\r
308 \r
309         if (path.equals("/") || path.equals("/network/")) {\r
310             String pfx = "\n";\r
311             sb.append(px2);\r
312             sb.append("\"routing\": [");\r
313             for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {\r
314                 sb.append(pfx);\r
315                 sb.append(ne.asJSONObject().toString());\r
316                 pfx = ",\n";\r
317             }\r
318             sb.append("\n]");\r
319         }\r
320         sb.append("}\n");\r
321         resp.setStatus(HttpServletResponse.SC_OK);\r
322         resp.setContentType("application/json");\r
323         try {\r
324             resp.getOutputStream().print(sb.toString());\r
325         } catch (IOException ioe) {\r
326             eventlogger.error("IOException" + ioe.getMessage());\r
327         }\r
328     }\r
329     /**\r
330      * PUT on &lt;/internal/route/*&gt; -- not supported.\r
331      */\r
332     @Override\r
333     public void doPut(HttpServletRequest req, HttpServletResponse resp) {\r
334         EventLogRecord elr = new EventLogRecord(req);\r
335         if (!isAuthorizedForInternal(req)) {\r
336             elr.setMessage("Unauthorized.");\r
337             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
338             eventlogger.info(elr);\r
339             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, "Unauthorized.", eventlogger);\r
340             return;\r
341         }\r
342         sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Bad URL.", eventlogger);\r
343     }\r
344     /**\r
345      * POST - modify existing route table entries in the route table tree specified by the URL path.\r
346      */\r
347     @Override\r
348     public void doPost(HttpServletRequest req, HttpServletResponse resp) {\r
349         EventLogRecord elr = new EventLogRecord(req);\r
350         if (!isAuthorizedForInternal(req)) {\r
351             elr.setMessage("Unauthorized.");\r
352             elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
353             eventlogger.info(elr);\r
354             sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, "Unauthorized.", eventlogger);\r
355             return;\r
356         }\r
357         if (isProxyOK(req) && isProxyServer()) {\r
358             try {\r
359                 super.doPost(req, resp);\r
360             } catch (IOException ioe) {\r
361                 intlogger.error("IOException" + ioe.getMessage());\r
362             }\r
363             return;\r
364         }\r
365         String path = req.getPathInfo();\r
366         Insertable[] ins = null;\r
367         if (path.startsWith("/ingress/")) {\r
368             // /internal/route/ingress/?feed=%s&amp;user=%s&amp;subnet=%s&amp;nodepatt=%s\r
369             try {\r
370                 // Although it probably doesn't make sense, you can install two identical routes in the IRT\r
371                 int feedid = Integer.parseInt(req.getParameter("feed"));\r
372                 String user = req.getParameter("user");\r
373                 if (user == null)\r
374                     user = "-";\r
375                 String subnet = req.getParameter("subnet");\r
376                 if (subnet == null)\r
377                     subnet = "-";\r
378                 String nodepatt = req.getParameter("nodepatt");\r
379                 String t = req.getParameter("seq");\r
380                 int seq = (t != null) ? Integer.parseInt(t) : (IngressRoute.getMaxSequence() + 100);\r
381                 ins = new Insertable[] { new IngressRoute(seq, feedid, user, subnet, NodeClass.lookupNodeNames(nodepatt)) };\r
382             } catch (Exception e) {\r
383                 intlogger.info(e);\r
384                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add ingress' command.", intlogger);\r
385                 return;\r
386             }\r
387         } else if (path.startsWith("/egress/")) {\r
388             // /internal/route/egress/?sub=%s&amp;node=%s\r
389             try {\r
390                 int subid = Integer.parseInt(req.getParameter("sub"));\r
391                 EgressRoute er = EgressRoute.getEgressRoute(subid);\r
392                 if (er != null) {\r
393                     sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "An egress route already exists for that subscriber.", intlogger);\r
394                     return;\r
395                 }\r
396                 String node = NodeClass.normalizeNodename(req.getParameter("node"));\r
397                 ins = new Insertable[] { new EgressRoute(subid, node) };\r
398             } catch (Exception e) {\r
399                 intlogger.info(e);\r
400                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add egress' command.", intlogger);\r
401                 return;\r
402             }\r
403         } else if (path.startsWith("/network/")) {\r
404             // /internal/route/network/?from=%s&amp;to=%s&amp;via=%s\r
405             try {\r
406                 String nfrom = req.getParameter("from");\r
407                 String nto   = req.getParameter("to");\r
408                 String nvia  = req.getParameter("via");\r
409                 if (nfrom == null || nto == null || nvia == null) {\r
410                     sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "Missing arguments in 'add network' command.", intlogger);\r
411                     return;\r
412                 }\r
413                 nfrom = NodeClass.normalizeNodename(nfrom);\r
414                 nto   = NodeClass.normalizeNodename(nto);\r
415                 nvia  = NodeClass.normalizeNodename(nvia);\r
416                 NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);\r
417                 for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {\r
418                     if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {\r
419                         sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "Network route table already contains a route for " + nfrom + " and " + nto, intlogger);\r
420                         return;\r
421                     }\r
422                 }\r
423                 ins = new Insertable[] { nr };\r
424             } catch (IllegalArgumentException e) {\r
425                 intlogger.info(e);\r
426                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add network' command.", intlogger);\r
427                 return;\r
428             }\r
429         }\r
430         if (ins == null) {\r
431             sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, "Bad URL.", intlogger);\r
432             return;\r
433         }\r
434         boolean rv = true;\r
435         for (Insertable dd : ins) {\r
436             rv &= doInsert(dd);\r
437         }\r
438         if (rv) {\r
439             elr.setResult(HttpServletResponse.SC_OK);\r
440             eventlogger.info(elr);\r
441             resp.setStatus(HttpServletResponse.SC_OK);\r
442             provisioningDataChanged();\r
443             provisioningParametersChanged();\r
444         } else {\r
445             // Something went wrong with the INSERT\r
446             elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
447             eventlogger.info(elr);\r
448             sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);\r
449         }\r
450     }\r
451 }\r