Commit e36dfd6c93c5a790a11b44d17737f7864a941aa8

Authored by Miguel Barão
1 parent 9f52cf60
Exists in master and in 1 other branch dev

- Fixed simultaneous logins of same user on different devices.

will invalidate older ones.
1 1
2 # BUGS 2 # BUGS
3 3
4 -- esta a permitir 2 logins em simultaneo do mesmo user. fica tudo baralhado...  
5 - shift-enter não está a funcionar 4 - shift-enter não está a funcionar
6 - falha intermitent no file handler quando o browser envia 2 GET requests ao mesmo tempo (porquê?) 5 - falha intermitent no file handler quando o browser envia 2 GET requests ao mesmo tempo (porquê?)
7 - nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido. 6 - nos topicos learn.yaml, qd falha acrescenta no fim. nao faz sentido.
@@ -34,6 +33,7 @@ @@ -34,6 +33,7 @@
34 33
35 # FIXED 34 # FIXED
36 35
  36 +- esta a permitir 2 logins em simultaneo do mesmo user. fica tudo baralhado se mxerem em simultaneo...
37 - errar no ultimo topico nao mostra solucao? 37 - errar no ultimo topico nao mostra solucao?
38 - quando a pergunta devolve comments, este é apresentado, mas fica persistente nas tentativas seguintes. devia ser limpo apos a segunda submissao. 38 - quando a pergunta devolve comments, este é apresentado, mas fica persistente nas tentativas seguintes. devia ser limpo apos a segunda submissao.
39 - na definicao dos topicos, indicar: 39 - na definicao dos topicos, indicar:
@@ -73,22 +73,22 @@ class LearnApp(object): @@ -73,22 +73,22 @@ class LearnApp(object):
73 # login 73 # login
74 # ------------------------------------------------------------------------ 74 # ------------------------------------------------------------------------
75 async def login(self, uid, try_pw): 75 async def login(self, uid, try_pw):
76 - if uid.startswith('l'): # remove prefix 'l'  
77 - uid = uid[1:]  
78 -  
79 with self.db_session() as s: 76 with self.db_session() as s:
80 try: 77 try:
81 name, password = s.query(Student.name, Student.password).filter_by(id=uid).one() 78 name, password = s.query(Student.name, Student.password).filter_by(id=uid).one()
82 except: 79 except:
83 - logger.info(f'User "{uid}" does not exist!') 80 + logger.info(f'User "{uid}" does not exist')
84 return False 81 return False
85 82
86 pw_ok = await check_password(try_pw, password) # async bcrypt 83 pw_ok = await check_password(try_pw, password) # async bcrypt
  84 +
87 if pw_ok: 85 if pw_ok:
88 if uid in self.online: 86 if uid in self.online:
89 - logger.warning(f'User "{uid}" already logged in, overwriting state') 87 + logger.warning(f'User "{uid}" already logged in, overwriting')
  88 + counter = self.online[uid]['counter']
90 else: 89 else:
91 - logger.info(f'User "{uid}" logged in successfully') 90 + logger.info(f'User "{uid}" logged in')
  91 + counter = 0
92 92
93 with self.db_session() as s: 93 with self.db_session() as s:
94 tt = s.query(StudentTopic).filter_by(student_id=uid) 94 tt = s.query(StudentTopic).filter_by(student_id=uid)
@@ -102,10 +102,11 @@ class LearnApp(object): @@ -102,10 +102,11 @@ class LearnApp(object):
102 'number': uid, 102 'number': uid,
103 'name': name, 103 'name': name,
104 'state': StudentKnowledge(deps=self.deps, factory=self.factory, state=state), 104 'state': StudentKnowledge(deps=self.deps, factory=self.factory, state=state),
  105 + 'counter': counter + 1, # counts simultaneous logins
105 } 106 }
106 107
107 else: 108 else:
108 - logger.info(f'User "{uid}" wrong password!') 109 + logger.info(f'User "{uid}" wrong password')
109 110
110 return pw_ok 111 return pw_ok
111 112
@@ -313,6 +314,10 @@ class LearnApp(object): @@ -313,6 +314,10 @@ class LearnApp(object):
313 # ======================================================================== 314 # ========================================================================
314 315
315 # ------------------------------------------------------------------------ 316 # ------------------------------------------------------------------------
  317 + def get_login_counter(self, uid):
  318 + return self.online[uid]['counter']
  319 +
  320 + # ------------------------------------------------------------------------
316 def get_student_name(self, uid): 321 def get_student_name(self, uid):
317 return self.online[uid].get('name', '') 322 return self.online[uid].get('name', '')
318 323
@@ -75,13 +75,12 @@ class BaseHandler(tornado.web.RequestHandler): @@ -75,13 +75,12 @@ class BaseHandler(tornado.web.RequestHandler):
75 return self.application.learn 75 return self.application.learn
76 76
77 def get_current_user(self): 77 def get_current_user(self):
78 - cookie = self.get_secure_cookie("user") 78 + cookie = self.get_secure_cookie('user')
79 if cookie: 79 if cookie:
80 - user = cookie.decode('utf-8')  
81 - return user  
82 - # FIXME if the cookie exists but user is not in learn.online, this will force new login and store new (duplicate?) cookie. is this correct??  
83 - # if user in self.learn.online:  
84 - # return user 80 + counter = self.get_secure_cookie('counter')
  81 + uid = cookie.decode('utf-8')
  82 + if counter.decode('utf-8') == str(self.learn.get_login_counter(uid)):
  83 + return uid
85 84
86 # ---------------------------------------------------------------------------- 85 # ----------------------------------------------------------------------------
87 # /auth/login and /auth/logout 86 # /auth/login and /auth/logout
@@ -97,10 +96,11 @@ class LoginHandler(BaseHandler): @@ -97,10 +96,11 @@ class LoginHandler(BaseHandler):
97 login_ok = await self.learn.login(uid, pw) 96 login_ok = await self.learn.login(uid, pw)
98 97
99 if login_ok: 98 if login_ok:
100 - self.set_secure_cookie("user", str(uid), expires_days=30)  
101 - self.redirect(self.get_argument("next", "/")) 99 + self.set_secure_cookie('user', uid) # expires_days=30
  100 + self.set_secure_cookie('counter', str(self.learn.get_login_counter(uid)))
  101 + self.redirect('/')
102 else: 102 else:
103 - self.render("login.html", error='Número ou senha incorrectos') 103 + self.render('login.html', error='Número ou senha incorrectos')
104 104
105 105
106 # ---------------------------------------------------------------------------- 106 # ----------------------------------------------------------------------------
@@ -108,7 +108,8 @@ class LogoutHandler(BaseHandler): @@ -108,7 +108,8 @@ class LogoutHandler(BaseHandler):
108 @tornado.web.authenticated 108 @tornado.web.authenticated
109 def get(self): 109 def get(self):
110 self.clear_cookie('user') 110 self.clear_cookie('user')
111 - self.redirect(self.get_argument('next', '/')) 111 + self.clear_cookie('counter')
  112 + self.redirect('/')
112 113
113 def on_finish(self): 114 def on_finish(self):
114 self.learn.logout(self.current_user) 115 self.learn.logout(self.current_user)
@@ -309,7 +310,7 @@ class QuestionHandler(BaseHandler): @@ -309,7 +310,7 @@ class QuestionHandler(BaseHandler):
309 } 310 }
310 311
311 else: 312 else:
312 - logger.error(f'Unknown action: {action}') 313 + logging.error(f'Unknown action: {action}')
313 314
314 self.write(response) 315 self.write(response)
315 316
templates/topic.html
@@ -78,7 +78,7 @@ @@ -78,7 +78,7 @@
78 78
79 <!-- <div class="container"> --> 79 <!-- <div class="container"> -->
80 <div class="row"> 80 <div class="row">
81 - <div class="col col-9"> 81 + <div class="col col-lg-9">
82 <div class="my-5" id="content"> 82 <div class="my-5" id="content">
83 <form action="/question" method="post" id="question_form" autocomplete="off"> 83 <form action="/question" method="post" id="question_form" autocomplete="off">
84 {% module xsrf_form_html() %} 84 {% module xsrf_form_html() %}
@@ -91,7 +91,7 @@ @@ -91,7 +91,7 @@
91 </div> 91 </div>
92 92
93 </div> <!-- col --> 93 </div> <!-- col -->
94 - <div class="col col-3"> 94 + <div class="col col-lg-3">
95 <button class="btn btn-primary btn-lg btn-block my-5" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Responder</button> 95 <button class="btn btn-primary btn-lg btn-block my-5" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Responder</button>
96 96
97 <div id="right" style="display: none"> 97 <div id="right" style="display: none">