Skip to content

Commit 1d53a2d

Browse files
Merge pull request #13 from maricaantonacci/devel
Updating dependencies for CVE-2019-14806 Handling token expiration
2 parents eb4830f + 28b261a commit 1d53a2d

13 files changed

+204
-157
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ git clone https://github.com/maricaantonacci/orchestrator-dashboard.git
133133
cd orchestrator-dashboard
134134
python3 -m venv venv
135135
source venv/bin/activate
136-
pip install -r requirements.txt
136+
pip3 install -r requirements.txt
137137
```
138138

139139
Start the dashboard app:

app/__init__.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
app = Flask(__name__)
99
app.wsgi_app = ProxyFix(app.wsgi_app)
10-
app.secret_key="this.is.my.secretkey"
10+
app.secret_key="30bb7cf2-1fef-4d26-83f0-8096b6dcc7a3"
1111
app.config.from_json('config.json')
1212

1313
iam_base_url=app.config['IAM_BASE_URL']
@@ -19,6 +19,7 @@
1919
"iam", __name__,
2020
client_id=app.config['IAM_CLIENT_ID'],
2121
client_secret=app.config['IAM_CLIENT_SECRET'],
22+
scope='openid email profile offline_access',
2223
base_url=iam_base_url,
2324
token_url=iam_token_url,
2425
auto_refresh_url=iam_refresh_url,
@@ -27,7 +28,7 @@
2728
)
2829
app.register_blueprint(iam_blueprint, url_prefix="/login")
2930

30-
from app import routes
31+
from app import routes, errors
3132

3233
if __name__ == "__main__":
3334
app.run(host='0.0.0.0')

app/config-sample.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"ORCHESTRATOR_URL": "https://indigo-paas.cloud.ba.infn.it/orchestrator",
66
"SLAM_URL": "https://indigo-slam.cloud.ba.infn.it:8443",
77
"CMDB_URL": "https://indigo-paas.cloud.ba.infn.it/cmdb",
8-
"TOSCA_TEMPLATES_DIR": "/opt/tosca-templates"
8+
"TOSCA_TEMPLATES_DIR": "/opt/tosca-templates",
9+
"SUPPORT_EMAIL": "support@example.com"
910
}

app/errors.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from flask import render_template, request
2+
from app import app
3+
4+
@app.errorhandler(404)
5+
def page_not_found(error):
6+
app.logger.error('Page not found: %s', (request.path))
7+
return render_template('404.html'), 404
8+
9+
10+
@app.errorhandler(500)
11+
def internal_server_error(error):
12+
app.logger.error('Server Error: %s', (error))
13+
return render_template('500.html', support_email=app.config.get('SUPPORT_EMAIL')), 500

app/routes.py

+62-116
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from app import app, iam_blueprint, iam_base_url
1+
from app import app, iam_blueprint, iam_base_url, sla as sla
22
from flask import json, current_app, render_template, request, redirect, url_for, flash, session
33
import requests, json
44
import yaml
55
import io, os, sys
66
from fnmatch import fnmatch
77
from hashlib import md5
8+
from functools import wraps
89

910

1011
def to_pretty_json(value):
@@ -21,6 +22,7 @@ def avatar(email, size):
2122

2223
toscaDir = app.config.get('TOSCA_TEMPLATES_DIR') + "/"
2324
tosca_pars_dir = app.config.get('TOSCA_PARAMETERS_DIR')
25+
orchestratorUrl = app.config.get('ORCHESTRATOR_URL')
2426

2527
toscaTemplates = []
2628
for path, subdirs, files in os.walk(toscaDir):
@@ -34,7 +36,7 @@ def avatar(email, size):
3436
toscaInfo = {}
3537
for tosca in toscaTemplates:
3638
with io.open( toscaDir + tosca) as stream:
37-
template = yaml.load(stream)
39+
template = yaml.full_load(stream)
3840

3941
toscaInfo[tosca] = {
4042
"valid": True,
@@ -78,83 +80,53 @@ def avatar(email, size):
7880
tosca_pars_file = os.path.join(fpath, fname)
7981
with io.open(tosca_pars_file) as pars_file:
8082
toscaInfo[tosca]['enable_config_form'] = True
81-
pars_data = yaml.load(pars_file)
83+
pars_data = yaml.full_load(pars_file)
8284
toscaInfo[tosca]['inputs'] = pars_data["inputs"]
8385
if "tabs" in pars_data:
8486
toscaInfo[tosca]['tabs'] = pars_data["tabs"]
8587

8688

8789
app.logger.debug("Extracted TOSCA INFO: " + json.dumps(toscaInfo))
8890

89-
orchestratorUrl = app.config.get('ORCHESTRATOR_URL')
90-
slamUrl = app.config.get('SLAM_URL')
91-
cmdbUrl = app.config.get('CMDB_URL')
92-
slam_cert = app.config.get('SLAM_CERT')
91+
92+
def authorized_with_valid_token(f):
93+
@wraps(f)
94+
def decorated_function(*args, **kwargs):
95+
96+
97+
if not iam_blueprint.session.authorized or 'username' not in session:
98+
return redirect(url_for('login'))
99+
100+
if iam_blueprint.session.token['expires_in'] < 20:
101+
app.logger.debug("Force refresh token")
102+
iam_blueprint.session.get('/userinfo')
103+
104+
return f(*args, **kwargs)
105+
106+
return decorated_function
93107

94108
@app.route('/settings')
109+
@authorized_with_valid_token
95110
def show_settings():
96-
if not iam_blueprint.session.authorized:
97-
return redirect(url_for('login'))
98111
return render_template('settings.html', orchestrator_url=orchestratorUrl, iam_url=iam_base_url)
99112

100113
@app.route('/login')
101114
def login():
102115
session.clear()
103116
return render_template('home.html')
104117

105-
106-
def get_sla_extra_info(access_token, service_id):
107-
headers = {'Authorization': 'bearer %s' % (access_token)}
108-
url = cmdbUrl + "/service/id/" + service_id
109-
response = requests.get(url, headers=headers, timeout=20)
110-
response.raise_for_status()
111-
app.logger.info(json.dumps(response.json()['data']['service_type']))
112-
113-
service_type=response.json()['data']['service_type']
114-
sitename=response.json()['data']['sitename']
115-
if 'properties' in response.json()['data']:
116-
if 'gpu_support' in response.json()['data']['properties']:
117-
service_type = service_type + " (gpu_support: " + str(response.json()['data']['properties']['gpu_support']) + ")"
118-
119-
return sitename, service_type
120-
121-
def get_slas(access_token):
122-
123-
headers = {'Authorization': 'bearer %s' % (access_token)}
124-
125-
url = slamUrl + "/rest/slam/preferences/" + session['organisation_name']
126-
verify = True
127-
if slam_cert:
128-
verify = slam_cert
129-
response = requests.get(url, headers=headers, timeout=20, verify=verify)
130-
app.logger.info("SLA response status: " + str(response.status_code))
131-
132-
response.raise_for_status()
133-
app.logger.info("SLA response: " + json.dumps(response.json()))
134-
slas = response.json()['sla']
135-
136-
for i in range(len(slas)):
137-
sitename, service_type = get_sla_extra_info(access_token,slas[i]['services'][0]['service_id'])
138-
slas[i]['service_type']=service_type
139-
slas[i]['sitename']=sitename
140-
141-
return slas
142-
143118
@app.route('/slas')
119+
@authorized_with_valid_token
144120
def getslas():
145121

146-
if not iam_blueprint.session.authorized:
147-
return redirect(url_for('login'))
148-
149122
slas={}
150123

151124
try:
152125
access_token = iam_blueprint.token['access_token']
153-
slas = get_slas(access_token)
126+
slas = sla.get_slas(access_token)
154127

155128
except Exception as e:
156129
flash("Error retrieving SLAs list: \n" + str(e), 'warning')
157-
return redirect(url_for('home'))
158130

159131
return render_template('sla.html', slas=slas)
160132

@@ -163,29 +135,24 @@ def getslas():
163135
def home():
164136
if not iam_blueprint.session.authorized:
165137
return redirect(url_for('login'))
166-
try:
167-
account_info = iam_blueprint.session.get("/userinfo")
138+
139+
account_info = iam_blueprint.session.get("/userinfo")
168140

169-
if account_info.ok:
170-
account_info_json = account_info.json()
171-
session['username'] = account_info_json['name']
172-
session['gravatar'] = avatar(account_info_json['email'], 26)
173-
session['organisation_name'] = account_info_json['organisation_name']
174-
access_token = iam_blueprint.token['access_token']
141+
if account_info.ok:
142+
account_info_json = account_info.json()
143+
session['username'] = account_info_json['name']
144+
session['gravatar'] = avatar(account_info_json['email'], 26)
145+
session['organisation_name'] = account_info_json['organisation_name']
146+
access_token = iam_blueprint.token['access_token']
175147

176-
return render_template('portfolio.html', templates=toscaInfo)
148+
return render_template('portfolio.html', templates=toscaInfo)
177149

178-
except Exception as e:
179-
app.logger.error("Error: " + str(e))
180-
return redirect(url_for('logout'))
181150

182151
@app.route('/deployments')
152+
@authorized_with_valid_token
183153
def showdeployments():
184154

185-
if not iam_blueprint.session.authorized:
186-
return redirect(url_for('login'))
187-
try:
188-
access_token = iam_blueprint.token['access_token']
155+
access_token = iam_blueprint.session.token['access_token']
189156

190157
headers = {'Authorization': 'bearer %s' % (access_token)}
191158

@@ -199,17 +166,13 @@ def showdeployments():
199166
deployments = response.json()["content"]
200167
app.logger.debug("Deployments: " + str(deployments))
201168
return render_template('deployments.html', deployments=deployments)
202-
except Exception as e:
203-
app.logger.error("Error: " + str(e))
204-
return redirect(url_for('logout'))
169+
205170

206171

207172
@app.route('/template/<depid>')
173+
@authorized_with_valid_token
208174
def deptemplate(depid=None):
209175

210-
if not iam_blueprint.session.authorized:
211-
return redirect(url_for('login'))
212-
213176
access_token = iam_blueprint.session.token['access_token']
214177
headers = {'Authorization': 'bearer %s' % (access_token)}
215178

@@ -224,11 +187,9 @@ def deptemplate(depid=None):
224187
return render_template('deptemplate.html', template=template)
225188
#
226189
@app.route('/delete/<depid>')
190+
@authorized_with_valid_token
227191
def depdel(depid=None):
228192

229-
if not iam_blueprint.session.authorized:
230-
return redirect(url_for('login'))
231-
232193
access_token = iam_blueprint.session.token['access_token']
233194
headers = {'Authorization': 'bearer %s' % (access_token)}
234195
url = orchestratorUrl + "/deployments/" + depid
@@ -241,22 +202,14 @@ def depdel(depid=None):
241202

242203

243204
@app.route('/configure')
205+
@authorized_with_valid_token
244206
def configure():
245-
if not iam_blueprint.session.authorized:
246-
return redirect(url_for('login'))
247207

248208
access_token = iam_blueprint.session.token['access_token']
249209

250-
251-
252210
selected_tosca = request.args['selected_tosca']
253211

254-
try:
255-
slas = get_slas(access_token)
256-
257-
except Exception as e:
258-
flash("Error retrieving SLAs list: \n" + str(e), 'warning')
259-
return redirect(url_for('home'))
212+
slas = sla.get_slas(access_token)
260213

261214
return render_template('createdep.html',
262215
template=toscaInfo[selected_tosca],
@@ -269,60 +222,53 @@ def add_sla_to_template(template, sla_id):
269222

270223
template['topology_template']['policies'] = [
271224
{"deploy_on_specific_site": {"type": "tosca.policies.Placement", "properties": {"sla_id": sla_id}}}]
272-
app.logger.info(yaml.dump(template, default_flow_style=False))
225+
app.logger.debug(yaml.dump(template, default_flow_style=False))
273226

274227
return template
275228
#
276229
#
277230
@app.route('/submit', methods=['POST'])
231+
@authorized_with_valid_token
278232
def createdep():
279233

280-
if not iam_blueprint.session.authorized:
281-
return redirect(url_for('login'))
282-
283234
access_token = iam_blueprint.session.token['access_token']
284235

285236
app.logger.debug("Form data: " + json.dumps(request.form.to_dict()))
286237

287-
try:
288-
with io.open( toscaDir + request.args.get('template')) as stream:
289-
template = yaml.load(stream)
238+
with io.open( toscaDir + request.args.get('template')) as stream:
239+
template = yaml.full_load(stream)
290240

291-
form_data = request.form.to_dict()
292-
293-
params={}
294-
if 'extra_opts.keepLastAttempt' in form_data:
295-
params['keepLastAttempt'] = 'true'
296-
else:
297-
params['keepLastAttempt'] = 'false'
241+
form_data = request.form.to_dict()
242+
243+
params={}
244+
if 'extra_opts.keepLastAttempt' in form_data:
245+
params['keepLastAttempt'] = 'true'
246+
else:
247+
params['keepLastAttempt'] = 'false'
298248

299-
if form_data['extra_opts.schedtype'] == "man":
300-
template = add_sla_to_template(template, form_data['extra_opts.selectedSLA'])
249+
if form_data['extra_opts.schedtype'] == "man":
250+
template = add_sla_to_template(template, form_data['extra_opts.selectedSLA'])
301251

302-
inputs = { k:v for (k,v) in form_data.items() if not k.startswith("extra_opts.") }
252+
inputs = { k:v for (k,v) in form_data.items() if not k.startswith("extra_opts.") }
303253

304-
app.logger.debug("Parameters: " + json.dumps(inputs))
254+
app.logger.debug("Parameters: " + json.dumps(inputs))
305255

306-
payload = { "template" : yaml.dump(template,default_flow_style=False, sort_keys=False), "parameters": inputs }
256+
payload = { "template" : yaml.dump(template,default_flow_style=False, sort_keys=False), "parameters": inputs }
307257

308258

309-
url = orchestratorUrl + "/deployments/"
310-
headers = {'Content-Type': 'application/json', 'Authorization': 'bearer %s' % (access_token)}
311-
response = requests.post(url, json=payload, params=params, headers=headers)
259+
url = orchestratorUrl + "/deployments/"
260+
headers = {'Content-Type': 'application/json', 'Authorization': 'bearer %s' % (access_token)}
261+
response = requests.post(url, json=payload, params=params, headers=headers)
312262

313-
if not response.ok:
314-
flash("Error submitting deployment: \n" + response.text)
263+
if not response.ok:
264+
flash("Error submitting deployment: \n" + response.text)
315265

316-
return redirect(url_for('showdeployments'))
266+
return redirect(url_for('showdeployments'))
317267

318-
except Exception as e:
319-
flash("Error submitting deployment:" + str(e) + ". Please retry")
320-
return redirect(url_for('home'))
321268

322269

323270
@app.route('/logout')
324271
def logout():
325272
session.clear()
326273
iam_blueprint.session.get("/logout")
327-
# del iam_blueprint.session.token
328274
return redirect(url_for('login'))

0 commit comments

Comments
 (0)