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
BUGS.md
1 1  
2 2 # BUGS
3 3  
  4 +- self.testapp.get_json_filename_of_test(test_id) retorna None quando test_id nao existe.
4 5 - a revisao do teste não mostra as imagens.
5 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 7 - melhorar o botao de autorizar (desliga-se), usar antes um botao?
... ...
serve.py
... ... @@ -11,6 +11,7 @@ import mimetypes
11 11 import signal
12 12 import asyncio
13 13 import json
  14 +import functools
14 15  
15 16 # user installed libraries
16 17 import tornado.ioloop
... ... @@ -22,10 +23,21 @@ from tornado import template, gen
22 23 from app import App, AppException
23 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 39 # Web Application. Routes to handler classes.
28   -# -------------------------------------------------------------------------
  40 +# ----------------------------------------------------------------------------
29 41 class WebApplication(tornado.web.Application):
30 42 def __init__(self, testapp, debug=False):
31 43 handlers = [
... ... @@ -52,9 +64,9 @@ class WebApplication(tornado.web.Application):
52 64 self.testapp = testapp
53 65  
54 66  
55   -# -------------------------------------------------------------------------
  67 +# ----------------------------------------------------------------------------
56 68 # Base handler. Other handlers will inherit this one.
57   -# -------------------------------------------------------------------------
  69 +# ----------------------------------------------------------------------------
58 70 class BaseHandler(tornado.web.RequestHandler):
59 71 @property
60 72 def testapp(self):
... ... @@ -66,9 +78,9 @@ class BaseHandler(tornado.web.RequestHandler):
66 78 return cookie.decode('utf-8')
67 79  
68 80  
69   -# -------------------------------------------------------------------------
  81 +# ----------------------------------------------------------------------------
70 82 # /login
71   -# -------------------------------------------------------------------------
  83 +# ----------------------------------------------------------------------------
72 84 class LoginHandler(BaseHandler):
73 85 SUPPORTED_METHODS = ['GET', 'POST']
74 86  
... ... @@ -84,12 +96,12 @@ class LoginHandler(BaseHandler):
84 96 self.set_secure_cookie("user", str(uid), expires_days=30)
85 97 self.redirect(self.get_argument("next", "/"))
86 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 103 # /logout
92   -# -------------------------------------------------------------------------
  104 +# ----------------------------------------------------------------------------
93 105 class LogoutHandler(BaseHandler):
94 106 @tornado.web.authenticated
95 107 def get(self):
... ... @@ -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 129 # Serves files from the /public subdir of the topics.
105 130 # Based on https://bhch.github.io/posts/2017/12/serving-large-files-with-tornado-safely-without-blocking/
106 131 # ----------------------------------------------------------------------------
107 132 class FileHandler(BaseHandler):
  133 + SUPPORTED_METHODS = ['GET']
108 134 chunk_size = 512 * 1024 # serve up to 512 KiB multiple times
109 135  
110 136 @tornado.web.authenticated
... ... @@ -171,14 +197,15 @@ class TestHandler(BaseHandler):
171 197 'success': 'question-success.html',
172 198 }
173 199  
174   - # GET
  200 + # --- GET
175 201 @tornado.web.authenticated
176 202 def get(self):
177 203 uid = self.current_user
  204 + # FIXME make generate async?
178 205 t = self.testapp.get_student_test(uid) or self.testapp.generate_test(uid)
179 206 self.render('test.html', t=t, md=md_to_html, templ=self._templates)
180 207  
181   - # POST
  208 + # --- POST
182 209 @tornado.web.authenticated
183 210 async def post(self):
184 211 uid = self.current_user
... ... @@ -211,6 +238,19 @@ class TestHandler(BaseHandler):
211 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 254 # --- REVIEW -------------------------------------------------------------
215 255 class ReviewHandler(BaseHandler):
216 256 _templates = {
... ... @@ -230,15 +270,12 @@ class ReviewHandler(BaseHandler):
230 270 }
231 271  
232 272 @tornado.web.authenticated
  273 + @admin_only
233 274 def get(self):
234   - uid = self.current_user
235   - if uid != '0':
236   - raise tornado.web.HTTPError(404)
237   -
238 275 test_id = self.get_query_argument('test_id', None)
239 276 logging.info(f'Review test {test_id}.')
240 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 279 except:
243 280 raise tornado.web.HTTPError(404, 'Test ID not found.')
244 281  
... ... @@ -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 293 class AdminHandler(BaseHandler):
281 294 SUPPORTED_METHODS = ['GET', 'POST']
282 295  
283 296 @tornado.web.authenticated
  297 + @admin_only
284 298 def get(self):
285   - if self.current_user != '0':
286   - raise tornado.web.HTTPError(403)
287   -
288 299 cmd = self.get_query_argument('cmd', default=None)
289 300  
290 301 if cmd == 'students_table':
... ... @@ -307,10 +318,8 @@ class AdminHandler(BaseHandler):
307 318 self.render('admin.html')
308 319  
309 320 @tornado.web.authenticated
  321 + @admin_only
310 322 def post(self):
311   - if self.current_user != '0':
312   - self.redirect('/')
313   -
314 323 cmd = self.get_body_argument('cmd', None)
315 324 value = self.get_body_argument('value', None)
316 325  
... ...