From 76bfb287b51bd97c9ca0bfc4e7760b7ee8e15b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Thu, 8 Oct 2015 18:56:48 +0200 Subject: Reworked session handling. * All requests have a session ID, except for the initial create_session system messages. * User session can be in an authenticated or anonymous state. * In anonymous state it is not possible to perform user actions. * Logout has been implemented. * Sessions timeout and are cleared after a period of inactivity (1 hour). * Bugfixed the lang setting handling. * Renamed get_problem -> get_current_solution, return only the user's current solution, not the whole problem data. --- server/handlers.py | 78 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) (limited to 'server/handlers.py') diff --git a/server/handlers.py b/server/handlers.py index a1bfbd7..1ab09f3 100644 --- a/server/handlers.py +++ b/server/handlers.py @@ -16,25 +16,20 @@ class CodeqService(object): pass -class ProblemList(CodeqService): - """List all available problems to the client. +class CreateSession(CodeqService): + """Creates a new anonymous session. + To promote the session to the authenticated status, a subsequent + login or signup must follow. """ session_is_optional = True def process(self, request): - js = request.data - language = js.get('language') - if language is None: - request.reply({'code': 1, 'message': 'Language was not provided'}) - else: - request.reply({'code': 0, 'message': 'ok', 'problems': server.problems.list_problems(language)}) + request.reply({'code': 0, 'message': 'OK', 'sid': server.user_session.UserSession().get_sid()}) class Login(CodeqService): - """Logs in a client, creating a new session. + """Logs in a client, authenticating the session. """ - session_is_optional = True - def process(self, request): js = request.data username = js.get('username') @@ -44,35 +39,39 @@ class Login(CodeqService): elif password is None: request.reply({'code': 2, 'message': 'Password was not provided'}) else: + session = request.session try: - session = server.user_session.authenticate_and_create_session(username, password) + session.login(username, password) except AuthenticationFailed: request.reply({'code': 3, 'message': 'Username or password do not match'}) else: - if request.session: - request.session.destroy() settings = session.get_settings() - request.reply({'code': 0, 'message': 'OK', 'sid':session.get_sid(), 'settings':settings}) + request.reply({'code': 0, 'message': 'OK', 'settings': settings}) -class Signup(CodeqService): - session_is_optional = True +class Logout(CodeqService): + def process(self, request): + request.session.destroy() + request.reply({'code': 0, 'message': 'OK'}) + +class Signup(CodeqService): def process(self, request): js = request.data username = js.get('username') password = js.get('password') + lang = js.get('lang') or 'en' if username is None: request.reply({'code': 1, 'message': 'Username was not provided'}) elif password is None: request.reply({'code': 2, 'message': 'Password was not provided'}) else: try: - server.user_session.signup(username, password) + request.session.signup(username, password, lang) except UserExists: request.reply({'code': 3, 'message': 'Username already exists'}) except SignupFailed: - request.reply({'code': 4, 'message': 'Signn up failed'}) + request.reply({'code': 4, 'message': 'Sign-up failed'}) else: request.reply({'code': 0, 'message': 'OK'}) @@ -92,7 +91,7 @@ class ChangePassword(CodeqService): request.reply({'code': 0, 'message': 'OK'}) -class Settings(CodeqService): +class UpdateSettings(CodeqService): def process(self, request): js = request.data settings = js.get('settings') @@ -101,7 +100,6 @@ class Settings(CodeqService): else: try: request.session.update_settings(settings) - request.session.write_settings_to_db() except NoSuchSession: request.reply({'code': 2, 'message': 'No such session'}) else: @@ -246,20 +244,15 @@ class Test(CodeqService): request.reply({'code': 0, 'message': 'ok', 'hints': hints}) -class GetProblem(CodeqService): +class GetCurrentSolution(CodeqService): def process(self, request): js = request.data - language = js.get('language') - problem_group = js.get('problem_group') - problem = js.get('problem') - if language is None: - request.reply({'code': 1, 'message': 'Language identifier not given'}) - elif problem_group is None: - request.reply({'code': 2, 'message': 'Problem group identifier not given'}) - elif problem is None: - request.reply({'code': 3, 'message': 'Problem identifier not given'}) + problem_id = js.get('problem_id') + if problem_id is None: + request.reply({'code': 1, 'message': 'Problem ID not specified'}) else: - request.reply({'code': 0, 'message': 'ok', 'data': request.session.get_problem_data(language, problem_group, problem)}) + request.reply({'code': 0, 'message': 'ok', 'data': request.session.current_solution(problem_id)}) + class LoadProblem(CodeqService): def process(self, request): @@ -273,26 +266,28 @@ class LoadProblem(CodeqService): else: request.reply({'code': 0, 'message': 'OK'}) + class EndProblem(CodeqService): def process(self, request): request.session.end_language_session() request.end() + # maps actions to their handlers incoming_handlers = { - 'list_problems': ProblemList(), + 'create_session': CreateSession(), 'login': Login(), 'signup': Signup(), 'change_password': ChangePassword(), - 'get_problem': GetProblem(), - 'logout': None, + 'get_current_solution': GetCurrentSolution(), + 'logout': Logout(), 'activity': Activity(), 'query': Query(), 'python_exec': PythonExec(), 'python_push': PythonPush(), 'python_stop': PythonStop(), 'hint': Hint(), - 'settings': Settings(), + 'update_settings': UpdateSettings(), 'test': Test(), 'load_problem': LoadProblem(), 'end_problem': EndProblem() @@ -323,6 +318,7 @@ class Request(object): """ if data is None: self.end() + return if self._original_sid is not None: sid = data.get('sid') if sid is None: @@ -356,10 +352,18 @@ def _invoke_handler(handler, request): logging.error('ERROR: the request was not concluded!') request.reply({'code': -1, 'message': 'Request processing did not provide a reply'}) logging.debug('Processing finished') + except NotLoggedIn: + if request.is_finished: + logging.error('Caught the NotLoggedIn exception, but the request has already been replied to!') + else: + request.reply({'code': -1, 'message': 'Cannot execute the request: not logged in'}) except Exception as e: logging.critical('ERROR: data processing failed: ' + str(e)) logging.critical(traceback.format_exc()) - request.reply({'code': -1, 'message': 'Internal error: ' + str(e)}) + if request.is_finished: + logging.critical('The request has already been replied to, the client will not be aware of the error!') + else: + request.reply({'code': -1, 'message': 'Internal error: ' + str(e)}) def serve_request(json_obj): if not isinstance(json_obj, dict): -- cgit v1.2.1