Commit 9caa0ee3836526b7006836d22f1b96fda3bd4874

Authored by Miguel Barão
1 parent 1245246a
Exists in master and in 1 other branch dev

Added decorator @admin_only to the handlers that are restricted to the administrator.

This also uniformizes the http response to 403 Forbidden.
Showing 2 changed files with 57 additions and 47 deletions   Show diff stats
1 1
2 # BUGS 2 # BUGS
3 3
  4 +- self.testapp.get_json_filename_of_test(test_id) retorna None quando test_id nao existe.
4 - a revisao do teste não mostra as imagens. 5 - a revisao do teste não mostra as imagens.
5 - se aluno tem teste activo e é allowed uma segunda vez, deve manter o mesmo teste. adicionar opcao para eliminar um teste em curso. 6 - se aluno tem teste activo e é allowed uma segunda vez, deve manter o mesmo teste. adicionar opcao para eliminar um teste em curso.
6 - melhorar o botao de autorizar (desliga-se), usar antes um botao? 7 - melhorar o botao de autorizar (desliga-se), usar antes um botao?
@@ -11,6 +11,7 @@ import mimetypes @@ -11,6 +11,7 @@ import mimetypes
11 import signal 11 import signal
12 import asyncio 12 import asyncio
13 import json 13 import json
  14 +import functools
14 15
15 # user installed libraries 16 # user installed libraries
16 import tornado.ioloop 17 import tornado.ioloop
@@ -22,10 +23,21 @@ from tornado import template, gen @@ -22,10 +23,21 @@ from tornado import template, gen
22 from app import App, AppException 23 from app import App, AppException
23 from tools import load_yaml, md_to_html 24 from tools import load_yaml, md_to_html
24 25
  26 +# ----------------------------------------------------------------------------
  27 +# Decorator used to restrict access only to the administrator
  28 +# ----------------------------------------------------------------------------
  29 +def admin_only(func):
  30 + @functools.wraps(func)
  31 + def wrapper(self, *args, **kwargs):
  32 + if self.current_user != '0':
  33 + raise tornado.web.HTTPError(403) # forbidden
  34 + else:
  35 + func(self, *args, **kwargs)
  36 + return wrapper
25 37
26 -# ------------------------------------------------------------------------- 38 +# ----------------------------------------------------------------------------
27 # Web Application. Routes to handler classes. 39 # Web Application. Routes to handler classes.
28 -# ------------------------------------------------------------------------- 40 +# ----------------------------------------------------------------------------
29 class WebApplication(tornado.web.Application): 41 class WebApplication(tornado.web.Application):
30 def __init__(self, testapp, debug=False): 42 def __init__(self, testapp, debug=False):
31 handlers = [ 43 handlers = [
@@ -52,9 +64,9 @@ class WebApplication(tornado.web.Application): @@ -52,9 +64,9 @@ class WebApplication(tornado.web.Application):
52 self.testapp = testapp 64 self.testapp = testapp
53 65
54 66
55 -# ------------------------------------------------------------------------- 67 +# ----------------------------------------------------------------------------
56 # Base handler. Other handlers will inherit this one. 68 # Base handler. Other handlers will inherit this one.
57 -# ------------------------------------------------------------------------- 69 +# ----------------------------------------------------------------------------
58 class BaseHandler(tornado.web.RequestHandler): 70 class BaseHandler(tornado.web.RequestHandler):
59 @property 71 @property
60 def testapp(self): 72 def testapp(self):
@@ -66,9 +78,9 @@ class BaseHandler(tornado.web.RequestHandler): @@ -66,9 +78,9 @@ class BaseHandler(tornado.web.RequestHandler):
66 return cookie.decode('utf-8') 78 return cookie.decode('utf-8')
67 79
68 80
69 -# ------------------------------------------------------------------------- 81 +# ----------------------------------------------------------------------------
70 # /login 82 # /login
71 -# ------------------------------------------------------------------------- 83 +# ----------------------------------------------------------------------------
72 class LoginHandler(BaseHandler): 84 class LoginHandler(BaseHandler):
73 SUPPORTED_METHODS = ['GET', 'POST'] 85 SUPPORTED_METHODS = ['GET', 'POST']
74 86
@@ -84,12 +96,12 @@ class LoginHandler(BaseHandler): @@ -84,12 +96,12 @@ class LoginHandler(BaseHandler):
84 self.set_secure_cookie("user", str(uid), expires_days=30) 96 self.set_secure_cookie("user", str(uid), expires_days=30)
85 self.redirect(self.get_argument("next", "/")) 97 self.redirect(self.get_argument("next", "/"))
86 else: 98 else:
87 - self.render("login.html",  
88 - error='Não autorizado ou número/senha inválido') 99 + self.render("login.html", error='Não autorizado ou número/senha inválido')
89 100
90 -# ------------------------------------------------------------------------- 101 +
  102 +# ----------------------------------------------------------------------------
91 # /logout 103 # /logout
92 -# ------------------------------------------------------------------------- 104 +# ----------------------------------------------------------------------------
93 class LogoutHandler(BaseHandler): 105 class LogoutHandler(BaseHandler):
94 @tornado.web.authenticated 106 @tornado.web.authenticated
95 def get(self): 107 def get(self):
@@ -101,10 +113,24 @@ class LogoutHandler(BaseHandler): @@ -101,10 +113,24 @@ class LogoutHandler(BaseHandler):
101 113
102 114
103 # ---------------------------------------------------------------------------- 115 # ----------------------------------------------------------------------------
  116 +# handles root / to redirect students to /test and admininistrator to /admin
  117 +# ----------------------------------------------------------------------------
  118 +# FIXME list available tests
  119 +class RootHandler(BaseHandler):
  120 + @tornado.web.authenticated
  121 + def get(self):
  122 + if self.current_user == '0':
  123 + self.redirect('/admin')
  124 + else:
  125 + self.redirect('/test')
  126 +
  127 +
  128 +# ----------------------------------------------------------------------------
104 # Serves files from the /public subdir of the topics. 129 # Serves files from the /public subdir of the topics.
105 # Based on https://bhch.github.io/posts/2017/12/serving-large-files-with-tornado-safely-without-blocking/ 130 # Based on https://bhch.github.io/posts/2017/12/serving-large-files-with-tornado-safely-without-blocking/
106 # ---------------------------------------------------------------------------- 131 # ----------------------------------------------------------------------------
107 class FileHandler(BaseHandler): 132 class FileHandler(BaseHandler):
  133 + SUPPORTED_METHODS = ['GET']
108 chunk_size = 512 * 1024 # serve up to 512 KiB multiple times 134 chunk_size = 512 * 1024 # serve up to 512 KiB multiple times
109 135
110 @tornado.web.authenticated 136 @tornado.web.authenticated
@@ -171,14 +197,15 @@ class TestHandler(BaseHandler): @@ -171,14 +197,15 @@ class TestHandler(BaseHandler):
171 'success': 'question-success.html', 197 'success': 'question-success.html',
172 } 198 }
173 199
174 - # GET 200 + # --- GET
175 @tornado.web.authenticated 201 @tornado.web.authenticated
176 def get(self): 202 def get(self):
177 uid = self.current_user 203 uid = self.current_user
  204 + # FIXME make generate async?
178 t = self.testapp.get_student_test(uid) or self.testapp.generate_test(uid) 205 t = self.testapp.get_student_test(uid) or self.testapp.generate_test(uid)
179 self.render('test.html', t=t, md=md_to_html, templ=self._templates) 206 self.render('test.html', t=t, md=md_to_html, templ=self._templates)
180 207
181 - # POST 208 + # --- POST
182 @tornado.web.authenticated 209 @tornado.web.authenticated
183 async def post(self): 210 async def post(self):
184 uid = self.current_user 211 uid = self.current_user
@@ -211,6 +238,19 @@ class TestHandler(BaseHandler): @@ -211,6 +238,19 @@ class TestHandler(BaseHandler):
211 self.render('grade.html', t=t, allgrades=self.testapp.get_student_grades_from_all_tests(uid)) 238 self.render('grade.html', t=t, allgrades=self.testapp.get_student_grades_from_all_tests(uid))
212 239
213 240
  241 +# -------------------------------------------------------------------------
  242 +# FIXME this should be a post in the test with command giveup instead of correct...
  243 +# class GiveupHandler(BaseHandler):
  244 +# @tornado.web.authenticated
  245 +# def get(self):
  246 +# uid = self.current_user
  247 +# t = self.testapp.giveup_test(uid)
  248 +# self.testapp.logout(uid)
  249 +
  250 +# # --- Show result to student
  251 +# self.render('grade.html', t=t, allgrades=self.testapp.get_student_grades_from_all_tests(uid))
  252 +
  253 +
214 # --- REVIEW ------------------------------------------------------------- 254 # --- REVIEW -------------------------------------------------------------
215 class ReviewHandler(BaseHandler): 255 class ReviewHandler(BaseHandler):
216 _templates = { 256 _templates = {
@@ -230,15 +270,12 @@ class ReviewHandler(BaseHandler): @@ -230,15 +270,12 @@ class ReviewHandler(BaseHandler):
230 } 270 }
231 271
232 @tornado.web.authenticated 272 @tornado.web.authenticated
  273 + @admin_only
233 def get(self): 274 def get(self):
234 - uid = self.current_user  
235 - if uid != '0':  
236 - raise tornado.web.HTTPError(404)  
237 -  
238 test_id = self.get_query_argument('test_id', None) 275 test_id = self.get_query_argument('test_id', None)
239 logging.info(f'Review test {test_id}.') 276 logging.info(f'Review test {test_id}.')
240 try: 277 try:
241 - fname = self.testapp.get_json_filename_of_test(test_id) 278 + fname = self.testapp.get_json_filename_of_test(test_id) # FIXME is returning None if nonexistent
242 except: 279 except:
243 raise tornado.web.HTTPError(404, 'Test ID not found.') 280 raise tornado.web.HTTPError(404, 'Test ID not found.')
244 281
@@ -253,38 +290,12 @@ class ReviewHandler(BaseHandler): @@ -253,38 +290,12 @@ class ReviewHandler(BaseHandler):
253 290
254 291
255 # ------------------------------------------------------------------------- 292 # -------------------------------------------------------------------------
256 -# FIXME this should be a post in the test with command giveup instead of correct...  
257 -class GiveupHandler(BaseHandler):  
258 - @tornado.web.authenticated  
259 - def get(self):  
260 - uid = self.current_user  
261 - t = self.testapp.giveup_test(uid)  
262 - self.testapp.logout(uid)  
263 -  
264 - # --- Show result to student  
265 - self.render('grade.html', t=t, allgrades=self.testapp.get_student_grades_from_all_tests(uid))  
266 -  
267 -  
268 -# -------------------------------------------------------------------------  
269 -# FIXME list available tests  
270 -class RootHandler(BaseHandler):  
271 - @tornado.web.authenticated  
272 - def get(self):  
273 - if self.current_user == '0':  
274 - self.redirect('/admin')  
275 - else:  
276 - self.redirect('/test')  
277 -  
278 -  
279 -# -------------------------------------------------------------------------  
280 class AdminHandler(BaseHandler): 293 class AdminHandler(BaseHandler):
281 SUPPORTED_METHODS = ['GET', 'POST'] 294 SUPPORTED_METHODS = ['GET', 'POST']
282 295
283 @tornado.web.authenticated 296 @tornado.web.authenticated
  297 + @admin_only
284 def get(self): 298 def get(self):
285 - if self.current_user != '0':  
286 - raise tornado.web.HTTPError(403)  
287 -  
288 cmd = self.get_query_argument('cmd', default=None) 299 cmd = self.get_query_argument('cmd', default=None)
289 300
290 if cmd == 'students_table': 301 if cmd == 'students_table':
@@ -307,10 +318,8 @@ class AdminHandler(BaseHandler): @@ -307,10 +318,8 @@ class AdminHandler(BaseHandler):
307 self.render('admin.html') 318 self.render('admin.html')
308 319
309 @tornado.web.authenticated 320 @tornado.web.authenticated
  321 + @admin_only
310 def post(self): 322 def post(self):
311 - if self.current_user != '0':  
312 - self.redirect('/')  
313 -  
314 cmd = self.get_body_argument('cmd', None) 323 cmd = self.get_body_argument('cmd', None)
315 value = self.get_body_argument('value', None) 324 value = self.get_body_argument('value', None)
316 325