Fix bug 1595 - Can not create Openstack vim due to special characters in username
[osm/NBI.git] / osm_nbi / html_out.py
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10 # implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """
15 Contains html text in variables to make and html response
16 """
17
18 import yaml
19 from http import HTTPStatus
20 from html import escape as html_escape
21
22 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
23
24 html_start = """
25 <!DOCTYPE html>
26 <html>
27 <head>
28 <link href="/osm/static/style.css" rel="stylesheet">
29 <title>Welcome to OSM</title>
30 <link rel="shortcut icon" href="/osm/static/favicon.ico">
31 </head>
32 <body>
33 <div id="osm_topmenu">
34 <div>
35 <a href="https://osm.etsi.org"> <img src="/osm/static/OSM-logo.png" height="42" width="100"
36 style="vertical-align:middle"> </a>
37 <a>( {} )</a>
38 <a href="/osm/pdu/v1/pdu_descriptors">PDUs </a>
39 <a href="/osm/vnfpkgm/v1/vnf_packages">VNFDs </a>
40 <a href="/osm/nsd/v1/ns_descriptors">NSDs </a>
41 <a href="/osm/nslcm/v1/ns_instances">NSs </a>
42 <a href="/osm/nst/v1/netslice_templates">NSTDs </a>
43 <a href="/osm/nsilcm/v1/netslice_instances">NSIs </a>
44 <a href="/osm/admin/v1/users">USERs </a>
45 <a href="/osm/admin/v1/projects">PROJECTs </a>
46 <a href="/osm/admin/v1/tokens">TOKENs </a>
47 <a href="/osm/admin/v1/vim_accounts">VIMs </a>
48 <a href="/osm/admin/v1/wim_accounts">WIMs </a>
49 <a href="/osm/admin/v1/sdns">SDNs </a>
50 <a href="/osm/admin/v1/k8sclusters">K8s_clusters </a>
51 <a href="/osm/admin/v1/k8srepos">K8s_repos </a>
52 <a href="/osm/nslcm/v1/subscriptions">NS_Subs </a>
53 <a href="/osm/admin/v1/tokens?METHOD=DELETE">logout </a>
54 </div>
55 </div>
56 """
57
58 html_body = """
59 <h1>{item}</h1>
60 """
61
62 html_end = """
63 </body>
64 </html>
65 """
66
67 html_body_error = "<h2> Error <pre>{}</pre> </h2>"
68
69
70 html_auth2 = """
71 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
72 <html>
73 <head><META http-equiv="Content-Type" content="text/html; charset=UTF-8">
74 <link href="/osm/static/style.css" rel="stylesheet">
75 <title>OSM Login</title>
76 <link rel="shortcut icon" href="/osm/static/favicon.ico">
77 </head>
78 <body>
79 <div id="osm_header">
80 <div>
81 <a href="https://osm.etsi.org"> <h1><img src="/osm/static/OSM-logo.png" style="vertical-align:middle"></h1> </a>
82 </div>
83 </div>
84 <div id="osm_error_message">
85 <h1>{error}</h1>
86 </div>
87 <div class="gerritBody" id="osm_body">
88 <h1>Sign in to OSM</h1>
89 <form action="/osm/admin/v1/tokens" id="login_form" method="POST">
90 <table style="border: 0;">
91 <tr><th>Username</th><td><input id="f_user" name="username" size="25" tabindex="1" type="text"></td></tr>
92 <tr><th>Password</th><td><input id="f_pass" name="password" size="25" tabindex="2" type="password"></td></tr>
93 <tr><td><input tabindex="3" type="submit" value="Sign In"></td></tr>
94 </table>
95 </form>
96 <div style="clear: both; margin-top: 15px; padding-top: 2px; margin-bottom: 15px;">
97 <div id="osm_footer">
98 <div></div>
99 </div>
100 </div>
101 </div>
102 <script src="/osm/static/login.js"> </script>
103 </body>
104 </html>
105 """
106
107 html_upload_body = """
108 <form action="/osm{}" method="post" enctype="multipart/form-data">
109 <h3> <table style="border: 0;"> <tr>
110 <td> Upload {} descriptor (tar.gz) file: <input type="file" name="descriptor_file"/> </td>
111 <td> <input type="submit" value="Upload"/> </td>
112 </tr> </table> </h3>
113 </form>
114 """
115
116 html_nslcmop_body = """
117 <a href="/osm/nslcm/v1/ns_lcm_op_occs?nsInstanceId={id}">nslcm operations </a>
118 <a href="/osm/nslcm/v1/vnf_instances?nsr-id-ref={id}">VNFRS </a>
119 <form action="/osm/nslcm/v1/ns_instances/{id}/terminate" method="post" enctype="multipart/form-data">
120 <h3> <table style="border: 0;"> <tr>
121 <td> <input type="submit" value="Terminate"/> </td>
122 </tr> </table> </h3>
123 </form>
124 """
125
126 html_nsilcmop_body = """
127 <a href="/osm/nsilcm/v1/nsi_lcm_op_occs?netsliceInstanceId={id}">nsilcm operations </a>
128 <form action="/osm/nsilcm/v1/netslice_instances/{id}/terminate" method="post" enctype="multipart/form-data">
129 <h3> <table style="border: 0;"> <tr>
130 <td> <input type="submit" value="Terminate"/> </td>
131 </tr> </table> </h3>
132 </form>
133 """
134
135 html_vnfpackage_body = (
136 """<a href="/osm/vnfpkgm/v1/vnf_packages/{id}/artifacts">Artifacts </a>"""
137 )
138 html_nspackage_body = (
139 """<a href="/osm/nsd/v1/ns_descriptors/{id}/artifacts">Artifacts </a>"""
140 )
141
142
143 def format(data, request, response, toke_info):
144 """
145 Format a nice html response, depending on the data
146 :param data:
147 :param request: cherrypy request
148 :param response: cherrypy response
149 :return: string with teh html response
150 """
151 response.headers["Content-Type"] = "text/html"
152 if response.status == HTTPStatus.UNAUTHORIZED.value:
153 if response.headers.get("WWW-Authenticate") and request.config.get(
154 "auth.allow_basic_authentication"
155 ):
156 response.headers["WWW-Authenticate"] = (
157 "Basic" + response.headers["WWW-Authenticate"][6:]
158 )
159 return
160 else:
161 return html_auth2.format(error=data)
162 if request.path_info in ("/version", "/system"):
163 return (
164 "<pre>"
165 + yaml.safe_dump(
166 data, explicit_start=False, indent=4, default_flow_style=False
167 )
168 + "</pre>"
169 )
170 body = html_body.format(item=html_escape(request.path_info))
171 if response.status and response.status > 202:
172 # input request.path_info (URL) can contain XSS that are translated into output error detail
173 body += html_body_error.format(
174 html_escape(
175 yaml.safe_dump(
176 data, explicit_start=True, indent=4, default_flow_style=False
177 )
178 )
179 )
180 elif isinstance(data, (list, tuple)):
181 if request.path_info == "/vnfpkgm/v1/vnf_packages":
182 body += html_upload_body.format(request.path_info + "_content", "VNFD")
183 elif request.path_info == "/nsd/v1/ns_descriptors":
184 body += html_upload_body.format(request.path_info + "_content", "NSD")
185 elif request.path_info == "/nst/v1/nst_templates":
186 body += html_upload_body.format(request.path_info + "_content", "NSTD")
187 for k in data:
188 if isinstance(k, dict):
189 data_id = k.pop("_id", None)
190 elif isinstance(k, str):
191 data_id = k
192 body += '<p> <a href="/osm/{url}/{id}">{id}</a>: {t} </p>'.format(
193 url=request.path_info, id=data_id, t=html_escape(str(k))
194 )
195 elif isinstance(data, dict):
196 if "Location" in response.headers:
197 body += '<a href="{}"> show </a>'.format(response.headers["Location"])
198 else:
199 _id = request.path_info[request.path_info.rfind("/") + 1 :]
200 body += '<a href="/osm/{}?METHOD=DELETE"> <img src="/osm/static/delete.png" height="25" width="25"> </a>'.format(
201 request.path_info
202 )
203 if request.path_info.startswith(
204 "/nslcm/v1/ns_instances_content/"
205 ) or request.path_info.startswith("/nslcm/v1/ns_instances/"):
206 body += html_nslcmop_body.format(id=_id)
207 elif request.path_info.startswith(
208 "/nsilcm/v1/netslice_instances_content/"
209 ) or request.path_info.startswith("/nsilcm/v1/netslice_instances/"):
210 body += html_nsilcmop_body.format(id=_id)
211 elif request.path_info.startswith(
212 "/vnfpkgm/v1/vnf_packages/"
213 ) or request.path_info.startswith("/vnfpkgm/v1/vnf_packages_content/"):
214 body += html_vnfpackage_body.format(id=_id)
215 elif request.path_info.startswith(
216 "/nsd/v1/ns_descriptors/"
217 ) or request.path_info.startswith("/nsd/v1/ns_descriptors_content/"):
218 body += html_nspackage_body.format(id=_id)
219 body += (
220 "<pre>"
221 + html_escape(
222 yaml.safe_dump(
223 data, explicit_start=True, indent=4, default_flow_style=False
224 )
225 )
226 + "</pre>"
227 )
228 elif data is None:
229 if request.method == "DELETE" or "METHOD=DELETE" in request.query_string:
230 body += "<pre> deleted </pre>"
231 else:
232 body = html_escape(str(data))
233 user_text = " "
234 if toke_info:
235 if toke_info.get("username"):
236 user_text += "user: {}".format(toke_info.get("username"))
237 if toke_info.get("project_id"):
238 user_text += ", project: {}".format(toke_info.get("project_name"))
239 return html_start.format(user_text) + body + html_end
240 # yaml.safe_dump(data, explicit_start=True, indent=4, default_flow_style=False)
241 # tags=False,
242 # encoding='utf-8', allow_unicode=True)