5
5
import re
6
6
import json
7
7
import requests
8
+ import base64
8
9
import urllib
9
10
from flask import Flask , render_template , jsonify , request , redirect , make_response , flash , url_for
10
11
from flask_caching import Cache
19
20
if os .getenv ('UDF' , None ):
20
21
app .config ['ce_info' ] = get_ce_info ()
21
22
app .config ['UDF' ] = True
23
+ app .config ['SESSION_COOKIE_SECURE' ] = True
22
24
app .config ['base_url' ] = "mcn-lab.f5demos.com"
23
25
app .config ['CACHE_TYPE' ] = 'SimpleCache'
24
26
cache = Cache (app )
25
27
app .secret_key = "blahblahblah"
28
+ data_cookie = "mcnp-ac-data"
29
+ cookie_age = 86400
26
30
27
31
session = get_runner_session ()
28
32
session .headers .update ({"User-Agent" : "MCN-Lab-Runner/1.0" })
@@ -47,42 +51,104 @@ def validate_eph_ns(input_name):
47
51
48
52
def get_eph_ns () -> str :
49
53
"""check if ephemeral namespace is set"""
50
- this_eph_ns = request .cookies .get ('eph_ns' , None )
51
- return this_eph_ns
54
+ try :
55
+ cookie_b64 = request .cookies .get (data_cookie , None )
56
+ if cookie_b64 :
57
+ return get_cookie_prop (cookie_b64 , 'eph_ns' )
58
+ except Exception :
59
+ print ("Error getting ephemeral NS" )
60
+ return None
61
+
62
+ def get_site () -> str :
63
+ """check if ephemeral namespace is set"""
64
+ if app .config ['ce_info' ]:
65
+ return app .config ['ce_info' ].get ("site_name" , None )
66
+ return None
67
+
68
+ def update_cookie_prop (cookie_b64 , prop , value ):
69
+ """Update a property in a base64 encoded JSON cookie."""
70
+ try :
71
+ json_bytes = base64 .b64decode (cookie_b64 )
72
+ json_str = json_bytes .decode ('utf-8' )
73
+ cookie_data = json .loads (json_str )
74
+ if not isinstance (cookie_data , dict ):
75
+ raise ValueError ("Cookie data is not a dictionary." )
76
+ cookie_data [prop ] = value
77
+ updated = json .dumps (cookie_data )
78
+ base64_bytes = base64 .b64encode (updated .encode ('utf-8' ))
79
+ return base64_bytes .decode ('utf-8' )
80
+ except json .JSONDecodeError :
81
+ print ("Error decoding JSON from cookie." )
82
+ """TBD: this is not what we want."""
83
+ return encode_data ({})
84
+ except Exception as e :
85
+ print (f"An error occurred: { e } " )
86
+ return encode_data ({})
87
+
88
+ def get_cookie_prop (cookie_b64 , prop ):
89
+ """get a cookie prop"""
90
+ try :
91
+ json_bytes = base64 .b64decode (cookie_b64 )
92
+ json_str = json_bytes .decode ('utf-8' )
93
+ c_dict = json .loads (json_str )
94
+ return c_dict [prop ]
95
+ except json .JSONDecodeError :
96
+ print ("Error decoding cookie data" )
97
+ return None
98
+
99
+ def encode_data (data ):
100
+ """Encode dictionary to Base64-encoded JSON."""
101
+ json_str = json .dumps (data )
102
+ base64_bytes = base64 .b64encode (json_str .encode ('utf-8' ))
103
+ return base64_bytes .decode ('utf-8' )
52
104
105
+ def decode_data (encoded_data ):
106
+ """Decode Base64-encoded JSON to dictionary."""
107
+ json_bytes = base64 .b64decode (encoded_data )
108
+ json_str = json_bytes .decode ('utf-8' )
109
+ return json .loads (json_str )
110
+
53
111
@app .errorhandler (404 )
54
112
@app .errorhandler (500 )
55
113
def return_err (err ):
56
114
"""common error handler"""
57
- img = {
58
- 404 : "/static/404.png" ,
59
- 500 : "/static/500.png"
60
- }
61
- return render_template ("error.html" , err_img = img [err .code ])
115
+ return render_template ("error.html" , code = err .code )
116
+
117
+ @app .route ('/cookie' )
118
+ def cookie_err ():
119
+ """cookie error"""
120
+ return render_template ("cookie.html" )
62
121
63
122
@app .after_request
64
123
def cache_control (response ):
65
124
"""cache control"""
66
125
if request .path .startswith ("/static/" ) and request .path .endswith (".png" ):
67
126
response .headers ['Cache-Control' ] = 'public, max-age=3600'
68
127
return response
128
+
129
+ @app .before_request
130
+ def ensure_cookie ():
131
+ """Ensure that the cookie is present, otherwise redirect to the cookie page."""
132
+ if not request .path .startswith ('/static' ):
133
+ if (request .path not in ['/' , '/cookie' ]) and (data_cookie not in request .cookies ):
134
+ return redirect ('/cookie' )
69
135
70
136
@app .route ('/' )
71
137
def index ():
72
138
"""index page"""
73
- html = render_md ("markdown/welcome.md" )
74
- return render_template ('standard.html' ,
75
- title = "MCN Practical: Overview" ,
76
- content = html
139
+ html = render_template ('welcome.html' ,
140
+ title = "MCN Practical: Welcome"
77
141
)
142
+ response = make_response (html )
143
+ if data_cookie not in request .cookies :
144
+ response .set_cookie (data_cookie , encode_data ({}), max_age = cookie_age )
145
+ return response
78
146
79
147
@app .route ('/overview' )
80
- def arch ():
81
- """arch page"""
82
- html = render_md ("markdown/overview.md" )
83
- return render_template ('standard.html' ,
84
- title = "MCN Practical: Architecture" ,
85
- content = html
148
+ def overview ():
149
+ """overview page"""
150
+ return render_template ('overview.html' ,
151
+ title = "MCN Practical: Overview"
86
152
)
87
153
88
154
@app .route ('/setup' , methods = ['GET' , 'POST' ])
@@ -97,92 +163,58 @@ def setup():
97
163
flash ("Invalid ephemeral namespace." , "danger" )
98
164
return redirect (url_for ('setup' ))
99
165
response = make_response (redirect ('/setup' ))
100
- response .set_cookie ('eph_ns' , this_eph_ns , max_age = 60 * 60 * 24 )
166
+ cookie_b64 = request .cookies .get ('mcnp-ac-data' , encode_data ({}))
167
+ cookie_data = update_cookie_prop (cookie_b64 , 'eph_ns' , this_eph_ns )
168
+ response .set_cookie (data_cookie , cookie_data )
101
169
flash ('Ephemeral namespace successfully set.' , "success" )
102
170
return response
103
171
if action == 'clear' :
104
172
response = make_response (redirect ('/setup' ))
105
- response .set_cookie ('eph_ns' , '' , expires = 0 )
173
+ cookie_b64 = request .cookies .get ('mcnp-ac-data' , encode_data ({}))
174
+ cookie_data = update_cookie_prop (cookie_b64 , 'eph_ns' , None )
175
+ response .set_cookie (data_cookie , cookie_data )
106
176
flash ("Ephemeral namespace cleared." , "info" )
107
177
return response
108
- html = render_md ("markdown/setup.md" )
109
178
return render_template ('setup.html' ,
110
179
title = "MCN Practical: Setup" ,
111
- content = html ,
112
180
ns = ns
113
181
)
114
182
115
- @app .route ('/_ce_status' )
116
- @cache .cached (timeout = 30 )
117
- def ce_state ():
118
- """get ce state (internal route)"""
119
- data = get_ce_state (app .config ['ce_info' ])
120
- return data
121
-
122
183
@app .route ('/loadbalancing' )
123
184
def lb ():
124
185
"""lb page"""
125
186
ns = get_eph_ns ()
126
- html = render_md ( "markdown/lb.md" )
127
- return render_template ('exercise_standard .html' ,
187
+ site = get_site ( )
188
+ return render_template ('loadbalancing .html' ,
128
189
title = "MCN Practical: LB" ,
129
- content = html ,
190
+ site = site ,
130
191
ns = ns
131
192
)
132
193
133
194
@app .route ('/route' )
134
195
def path ():
135
196
"""routing page"""
136
197
ns = get_eph_ns ()
137
- html = render_md ("markdown/route.md" )
138
- return render_template ('exercise_standard.html' ,
198
+ return render_template ('route.html' ,
139
199
title = "MCN Practical: HTTP Routing" ,
140
- content = html ,
141
- ns = ns ,
142
-
200
+ ns = ns
143
201
)
144
202
145
203
@app .route ('/manipulation' )
146
204
def header ():
147
205
"""manipulation page"""
148
206
ns = get_eph_ns ()
149
- html = render_md ("markdown/manipulation.md" )
150
- return render_template ('exercise_standard.html' ,
207
+ return render_template ('manipulation.html' ,
151
208
title = "MCN Practical: Manipulation" ,
152
- content = html ,
153
209
ns = ns
154
210
)
155
211
156
212
@app .route ('/portability' )
157
213
def port ():
158
214
"""portability page"""
159
215
ns = get_eph_ns ()
160
- html = render_md ("markdown/portability.md" )
161
- return render_template ('exercise_standard.html' ,
216
+ return render_template ('portability.html' ,
162
217
title = "MCN Practical: Portability" ,
163
- content = html ,
164
- ns = ns
165
- )
166
-
167
- @app .route ('/vnet' )
168
- def vnet ():
169
- """vnet page"""
170
- ns = get_eph_ns ()
171
- html = render_md ("markdown/reference.md" )
172
- return render_template ('coming-soon.html' ,
173
- title = "MCN Practical: Reference" ,
174
- content = html ,
175
- ns = ns
176
- )
177
-
178
- @app .route ('/netpolicy' )
179
- def netp ():
180
- """netpolicy page"""
181
- ns = get_eph_ns ()
182
- html = render_md ("markdown/reference.md" )
183
- return render_template ('coming-soon.html' ,
184
- title = "MCN Practical: Reference" ,
185
- content = html ,
186
218
ns = ns
187
219
)
188
220
@@ -200,14 +232,12 @@ def ref():
200
232
@app .route ('/score' )
201
233
def score ():
202
234
"""scoreboard page"""
203
- ns = get_eph_ns ()
204
- score_cookie = request .cookies .get ('score' , '%7B%7D' )
205
- print (score_cookie )
206
235
try :
207
- decoded_cookie = urllib .parse .unquote (score_cookie )
208
- enc_score = json .loads (decoded_cookie )
209
- this_score = {urllib .parse .unquote (k ): v for k , v in enc_score .items ()}
210
- except json .JSONDecodeError :
236
+ cookie_b64 = request .cookies .get (data_cookie )
237
+ this_score = get_cookie_prop (cookie_b64 , 'score' )
238
+ """raise a LabException"""
239
+ except Exception :
240
+ print ("Error getting score" )
211
241
this_score = {}
212
242
try :
213
243
p_score = score_get_results (this_score )
@@ -218,19 +248,29 @@ def score():
218
248
port_table = score_build_table (p_score , 'port' , 'Portability' )
219
249
except LabException as e :
220
250
print (f"Couldn't build score table: { e } " )
221
- response = make_response ( render_template ('score.html' ,
251
+ return render_template ('score.html' ,
222
252
title = "MCN Practical: Scoreboard" ,
223
253
over_table = over_table ,
224
254
lb_table = lb_table ,
225
255
route_table = route_table ,
226
256
manip_table = manip_table ,
227
257
port_table = port_table ,
228
- ns = ns
229
- ))
230
- response .headers ['Cache-Control' ] = 'no-cache, no-store, must-revalidate'
231
- response .headers ['Pragma' ] = 'no-cache'
232
- response .headers ['Expires' ] = '0'
233
- return response
258
+ )
259
+
260
+ @app .route ('/test' )
261
+ def test ():
262
+ """test page"""
263
+ ns = get_eph_ns ()
264
+ return render_template ('test.html' ,
265
+ title = "MCN Practical: Test" ,
266
+ ns = ns
267
+ )
268
+
269
+ @app .route ('/_ce_status' )
270
+ def ce_state ():
271
+ """get ce state (internal route)"""
272
+ data = get_ce_state (app .config ['ce_info' ])
273
+ return data
234
274
235
275
@app .route ('/_test1' )
236
276
def ex_test ():
@@ -359,11 +399,12 @@ def manip2():
359
399
"""Second Manip Test"""
360
400
try :
361
401
ns = get_eph_ns ()
402
+ site = get_site ()
362
403
if not ns :
363
404
raise LabException ("Ephemeral NS not set" )
364
405
base_url = app .config ['base_url' ]
365
406
url = f"https://{ ns } .{ base_url } /"
366
- t_headers = { "x-mcn-namespace" : ns , "x-mcn-src-site" : app . config [ "ce_info" ][ "site_name" ] }
407
+ t_headers = { "x-mcn-namespace" : ns , "x-mcn-src-site" : site }
367
408
r_data = cloudapp_req_headers (session , url , 7 , t_headers )
368
409
return jsonify (status = 'success' , data = r_data )
369
410
except (LabException , ValueError ) as e :
@@ -415,7 +456,6 @@ def port2():
415
456
"""Friend test"""
416
457
try :
417
458
data = request .get_json ()
418
- print (data )
419
459
eph_ns = data ['userInput' ]
420
460
url = f"https://{ eph_ns } .{ app .config ['base_url' ]} /"
421
461
data = cloudapp_fetch (session , url , 7 , 'info' , {"method" : "GET" , "path" : "/" })
0 commit comments