def end(self): """Clean up the associated objects This coroutine calls :meth:`src.wsclass.WSClass.end` for all objects in ``self.ws_objects`` and it removes ``self`` from ``self.__class__.clients``. This coroutine is setup to be called when the WebSocket connection closes or when the program ends. """ try: exceptions = [] for ws_object in self.ws_objects.values(): try: yield ws_object.end() except: exceptions.append( exc_info() ) for exception in exceptions: print_exception(*exception) self.__class__.clients.discard(self) msg.code_debug( msg.join_path( __name__, self.end.__qualname__), 'Connection closed! {0} ' '({0.request.remote_ip})'.format(self) ) except: raise
def send_message(self, message): """Send a message ``self.send_function`` is used if it was specified during object creation. If not, ``self.execute_actions`` is used. :param dict message: The message to be sent. :raises MsgIsNotDictError: If ``message`` is not a dictionary. :raises NoMessageTypeError: If ``message`` doesn't have the ``'type'`` key. :raises NoActionForMsgTypeError: If ``self.send_function`` wasn't specified during object creation and there's no registered action for this message type. """ _path = msg.join_path(self._path, 'send_message') if not isinstance(message, dict): raise MsgIsNotDictError(message) if 'type' not in message: raise NoMessageTypeError(message) msg.code_debug(_path, 'Sending message: {}.'.format(message)) if self.send_function is None: self.execute_actions(message) else: IOLoop.current().spawn_callback(self.send_function, message)
def __init__(self, handler): _path = msg.join_path(self._path, '__init__') self.handler = handler self.pub_subs = { 'w': self.handler.ws_pub_sub, 'd': mb, 'l': self.handler.local_pub_sub, } for attr_name in dir(self): attribute = getattr(self, attr_name) if hasattr(attribute, 'msg_types'): for _type, channels in attribute.msg_types: msg.code_debug( _path, 'Adding action: %r ...' % attribute ) self.register_action_in( msg_type=_type, action=attribute, channels=channels) finalize( self, msg.code_debug, self._path, 'Deleting WSClass {0} from {0.handler} ' '...'.format(self) )
def end(self): """Clean up the associated objects This coroutine calls :meth:`src.wsclass.WSClass.end` for all objects in ``self.ws_objects`` and it removes ``self`` from ``self.__class__.clients``. This coroutine is setup to be called when the WebSocket connection closes or when the program ends. """ try: exceptions = [] for ws_object in self.ws_objects.values(): try: yield ws_object.end() except: exceptions.append(exc_info()) for exception in exceptions: print_exception(*exception) self.__class__.clients.discard(self) msg.code_debug( msg.join_path(__name__, self.end.__qualname__), 'Connection closed! {0} ' '({0.request.remote_ip})'.format(self)) except: raise
def request_disc_doc(self): _path = msg.join_path(self._path, "request_disc_doc") def _req_disc_doc(): dd = self.disc_doc_client.request("https://accounts.google.com/.well-known/" "openid-configuration", "GET") return self.decode_httplib2_json(dd) if self.disc_doc is None: msg.code_debug(_path, "Requesting discovery document ...") with ThreadPoolExecutor(1) as thread: self.__class__.disc_doc = thread.submit(_req_disc_doc) self.disc_doc = yield self.__class__.disc_doc # Este yield tiene que ir dentro, ya que el # thread no se comenzará a ejecutar si no se # yieldea y no se puede comenzar a ejecutar # fuera del with ya que ahí no existe ... o # algo asi XD :C self.__class__.disc_doc = None msg.code_debug(_path, "self.__class__.disc_doc = None") msg.code_debug(_path, "Discovery document arrived!") else: msg.code_debug(_path, "Waiting for discovery document ...") self.disc_doc = yield self.disc_doc msg.code_debug(_path, "Got the discovery document!")
def get(self): try: redirect_uri = urlunparse( (self.get_scheme(), self.request.host, conf.login_path, '', '', '') ) #remember the user for a longer period of time remember = self.get_argument('remember', False) state = jwt.encode({'remember': remember}, secrets['simple']) flow = oa2_client.OAuth2WebServerFlow( google_secrets['web']['client_id'], google_secrets['web']['client_secret'], scope = 'openid profile', redirect_uri = redirect_uri, state = state) auth_code = self.get_argument('code', False) if not auth_code: auth_uri = flow.step1_get_authorize_url() self.redirect(auth_uri) else: with ThreadPoolExecutor(1) as thread: credentials = yield thread.submit( flow.step2_exchange, auth_code) #Intercambiar el codigo antes que nada para #evitar ataques yield self.request_disc_doc() userinfo_endpoint = \ self.disc_doc['userinfo_endpoint'] http_auth = credentials.authorize( httplib2.Http()) with ThreadPoolExecutor(1) as thread: userinfo = yield thread.submit( http_auth.request, userinfo_endpoint) userinfo = self.decode_httplib2_json( userinfo) #https://developers.google.com/+/api/ #openidconnect/getOpenIdConnect user = yield db.User.from_google_userinfo( userinfo) token = jwt.encode({'id': user.id, 'exp': self.get_exp()}, user.secret) messages.code_debug(self.path+'.get', 'Rendering login.html ...') self.render('login.html', token=token) except oa2_client.FlowExchangeError: self.render('boxes.html', critical='Error de autenticación!')
def request_disc_doc(self): _path = msg.join_path(self._path, 'request_disc_doc') def _req_disc_doc(): dd = self.disc_doc_client.request( 'https://accounts.google.com/.well-known/' 'openid-configuration', 'GET') return self.decode_httplib2_json(dd) if self.disc_doc is None: msg.code_debug(_path, 'Requesting discovery document ...') with ThreadPoolExecutor(1) as thread: self.__class__.disc_doc = thread.submit(_req_disc_doc) self.disc_doc = \ yield self.__class__.disc_doc # Este yield tiene que ir dentro, ya que el # thread no se comenzará a ejecutar si no se # yieldea y no se puede comenzar a ejecutar # fuera del with ya que ahí no existe ... o # algo asi XD :C self.__class__.disc_doc = None msg.code_debug(_path, 'self.__class__.disc_doc = None') msg.code_debug(_path, 'Discovery document arrived!') else: msg.code_debug(_path, 'Waiting for discovery document ...') self.disc_doc = yield self.disc_doc msg.code_debug(_path, 'Got the discovery document!')
def get(self, room_code): try: classes = {'system-panel'} messages.code_debug('controller.GUIHandler.get', 'Rendering boxes.html ...') if room_code: c = yield db.Code(room_code) if c.code_type is db.CodeType.room: classes.add('teacher-panel') else: classes.add('student-panel') self.render('boxes.html', classes=classes, room_code=room_code) else: classes.update({'teacher-panel', 'student-panel'}) self.render('boxes.html', classes=classes) except db.NoObjectReturnedFromDB: self.render('boxes.html', critical='El código escaneado no está ' 'registrado!')
def get(self, room_code): """Render the application.""" try: classes = {'system-panel'} # room_code must be passed to the template as a # handler's attribute, because it is used in the # home panel. self.room_code = room_code if room_code: c = yield db.Code.get(room_code) if c.code_type is db.CodeType.room: classes.update( {'teacher-panel', 'room-code-panel'} ) else: classes.update( {'student-panel', 'seat-code-panel'}) else: classes.update({'teacher-panel', 'student-panel'}) msg.code_debug('controller.GUIHandler.get', 'Rendering boxes.html ...') self.render('boxes.html', classes=classes) except db.NoObjectReturnedFromDB: self.render( 'boxes.html', critical='El código escaneado no está ' 'registrado!' )
def get(self, room_code): """Render the application.""" try: classes = {'system'} # room_code must be passed to the template as a # handler's attribute, because it is used in the # home panel. self.room_code = room_code if room_code is None: classes.update({'desktop'}) else: c = yield db.Code.get(room_code) if c.code_type is db.CodeType.room: classes.update({'teacher'}) else: classes.update({'student'}) msg.code_debug('controller.GUIHandler.get', 'Rendering boxes.html ...') self.render('boxes.html', classes=classes) except db.NoObjectReturnedFromDB: self.render('boxes.html', classes=classes, critical='El código escaneado no está ' 'registrado!')
def __call__(self, method): _path = '.'.join((self._path, '__call__')) msg.code_debug( _path, 'Subscribing method {!r} to {!r} message types ' '...'.format(method, self.msg_types)) method.msg_types = self.msg_types return method
def on_close(self): MSGHandler.clients.remove(self) messages.code_debug( self.path+'.on_close', 'Connection closed! %s (%s)' % (self, self.request.remote_ip) )
def get(self): _path = msg.join_path(self._path, 'get') try: redirect_uri = urlunparse((self.get_scheme(), self.request.host, conf.login_path, '', '', '')) # remember the user for a longer period of time remember = self.get_argument('remember', False) room_code = self.get_argument('room_code', False) state = jwt.encode({ 'remember': remember, 'room_code': room_code }, secrets['simple']) flow = oa2_client.OAuth2WebServerFlow( google_secrets['web']['client_id'], google_secrets['web']['client_secret'], scope='openid profile', redirect_uri=redirect_uri, state=state) auth_code = self.get_argument('code', False) if not auth_code: auth_uri = flow.step1_get_authorize_url() self.redirect(auth_uri) else: with ThreadPoolExecutor(1) as thread: credentials = yield thread.submit(flow.step2_exchange, auth_code) # Intercambiar el codigo antes que nada para # evitar ataques yield self.request_disc_doc() userinfo_endpoint = \ self.disc_doc['userinfo_endpoint'] http_auth = credentials.authorize(httplib2.Http()) with ThreadPoolExecutor(1) as thread: userinfo = yield thread.submit(http_auth.request, userinfo_endpoint) userinfo = self.decode_httplib2_json(userinfo) # https://developers.google.com/+/api/ # openidconnect/getOpenIdConnect user = yield db.User.from_google_userinfo(userinfo) token = jwt.encode({ 'id': user.id, 'exp': self.get_exp() }, user.secret) msg.code_debug(_path, 'Rendering login.html ...') self.render('login.html', token=token) except oa2_client.FlowExchangeError: self.render('boxes.html', classes={'system'}, critical='Error de autenticación!')
def __call__(self, method): _path = '.'.join((self._path, '__call__')) msg.code_debug( _path, 'Subscribing method {!r} to {!r} message types ' '...'.format(method, self.msg_types) ) method.msg_types = self.msg_types return method
def load_wsclasses(package, handler): package = get_module(package) python_modules = load_python_modules(package) for module in python_modules: for member in module.__dict__.values(): if isclass(member) and issubclass(member, WSClass): messages.code_debug('src.load.load_wsclasses', 'Adding WSClass {}.'.format(member)) handler.add_class(member)
def raise_if_all_attr_def(obj, *attributes): from src.messages import code_debug if all_attr_defined(obj, *attributes): raise else: code_debug( 'src.utils.raise_if_all_attr_def', 'An exception was suppressed. ' '{}, {}.'.format(obj, attributes) )
def open(self): messages.code_debug( self.path+'.open', 'New connection established! %s (%s)' % (self, self.request.remote_ip) ) self.actions = {} self.wsobjects = [wsclass(self) for wsclass in self.wsclasses] self.__class__.clients.add(self) self.__class__.client_count += 1
def load_wsclasses(package, handler): package = get_module(package) python_modules = load_python_modules(package) for module in python_modules: for member in module.__dict__.values(): if isclass(member) and issubclass(member, WSClass): messages.code_debug( 'src.load.load_wsclasses', 'Adding WSClass {}.'.format(member) ) handler.add_class(member)
def initialize(self): _path = msg.join_path(self._path, "initialize") msg.code_debug(_path, "New connection established! {0} " "({0.request.remote_ip})".format(self)) self.local_pub_sub = OwnerPubSub(name="local_pub_sub") self.ws_pub_sub = OwnerPubSub(name="ws_pub_sub", send_function=self.write_message) self.ws_objects = {ws_class: ws_class(self) for ws_class in self.ws_classes} self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None
def get(self): _path = msg.join_path(self._path, "get") try: redirect_uri = urlunparse((self.get_scheme(), self.request.host, conf.login_path, "", "", "")) # remember the user for a longer period of time remember = self.get_argument("remember", False) room_code = self.get_argument("room_code", False) state = jwt.encode({"remember": remember, "room_code": room_code}, secrets["simple"]) flow = oa2_client.OAuth2WebServerFlow( google_secrets["web"]["client_id"], google_secrets["web"]["client_secret"], scope="openid profile", redirect_uri=redirect_uri, state=state, ) auth_code = self.get_argument("code", False) if not auth_code: auth_uri = flow.step1_get_authorize_url() self.redirect(auth_uri) else: with ThreadPoolExecutor(1) as thread: credentials = yield thread.submit(flow.step2_exchange, auth_code) # Intercambiar el codigo antes que nada para # evitar ataques yield self.request_disc_doc() userinfo_endpoint = self.disc_doc["userinfo_endpoint"] http_auth = credentials.authorize(httplib2.Http()) with ThreadPoolExecutor(1) as thread: userinfo = yield thread.submit(http_auth.request, userinfo_endpoint) userinfo = self.decode_httplib2_json(userinfo) # https://developers.google.com/+/api/ # openidconnect/getOpenIdConnect user = yield db.User.from_google_userinfo(userinfo) token = jwt.encode({"id": user.id, "exp": self.get_exp()}, user.secret) msg.code_debug(_path, "Rendering login.html ...") self.render("login.html", token=token) except oa2_client.FlowExchangeError: self.render("boxes.html", classes={"system"}, critical="Error de autenticación!")
def send_message(self, message): """Send a message ``self.send_function`` is used if it was specified during object creation. If not, ``self.execute_actions`` is used. The message will be sent on the next iteration of the IOLoop. If you need to send the message immediately use "self.send_function" directly. :param dict message: The message to be sent. :raises NotDictError: If ``message`` is not a dictionary. :raises NoMessageTypeError: If ``message`` doesn't have the ``'type'`` key. :raises NoActionForMsgTypeError: If ``self.send_function`` wasn't specified during object creation and there's no registered action for this message type. """ _path = msg.join_path(self._path, 'send_message') if not isinstance(message, dict): raise NotDictError('message', message) if 'type' not in message: raise NoMessageTypeError(message) msg.code_debug( _path, 'Sending message: {}.'.format(message) ) if self.send_function is None: self.execute_actions(message) else: IOLoop.current().spawn_callback( self.send_function, message)
def execute_actions(self, message): """Execute actions associated to the type of message :param dict message: The message to be sent :raises NoMessageTypeError: If ``message`` doesn't have the ``'type'`` key. :raises NoActionForMsgTypeError: If there's no registered action for this message type. :raises NotDictError: If ``message`` is not an instance of ``dict``. """ _path = msg.join_path(self._path, 'execute_actions') msg.code_debug( _path, 'Message arrived: {}.'.format(message) ) try: for action in self.actions[message['type']]: IOLoop.current().spawn_callback(action, message) except TypeError as te: if not isinstance(message, dict): nde = NotDictError('message', message) raise nde from te else: raise except KeyError as ke: if 'type' not in message: raise NoMessageTypeError(message) from ke elif message['type'] not in self.actions: nafmte = NoActionForMsgTypeError(message, self.name) raise nafmte from ke else: raise
def initialize(self): _path = msg.join_path(self._path, 'initialize') msg.code_debug( _path, 'New connection established! {0} ' '({0.request.remote_ip})'.format(self)) self.local_pub_sub = OwnerPubSub(name='local_pub_sub') self.ws_pub_sub = OwnerPubSub(name='ws_pub_sub', send_function=self.write_message) self.ws_objects = { ws_class: ws_class(self) for ws_class in self.ws_classes } self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None
def on_message(self, message): messages.code_debug(self.path+'.on_message', 'Message arrived: %r.' % message) try: message = json.loads(message) for action in self.actions[message['type']]: action(message) except KeyError: if 'type' in message: self.send_error('wrongMessageType', message, 'The client has sent a ' 'message of an ' 'unrecognized type.') else: self.send_malformed_message_error(message) except ValueError: self.send_malformed_message_error(message)
def execute_actions(self, message): """Execute actions associated to the type of message :param dict message: The message to be sent :raises NoMessageTypeError: If ``message`` doesn't have the ``'type'`` key. :raises NoActionForMsgTypeError: If there's no registered action for this message type. :raises NotDictError: If ``message`` is not an instance of ``dict``. """ _path = msg.join_path(self._path, 'execute_actions') msg.code_debug(_path, 'Message arrived: {}.'.format(message)) try: for action in self.actions[message['type']]: IOLoop.current().spawn_callback(action, message) except TypeError as te: if not isinstance(message, dict): nde = NotDictError('message', message) raise nde from te else: raise except KeyError as ke: if 'type' not in message: raise NoMessageTypeError(message) from ke elif message['type'] not in self.actions: nafmte = NoActionForMsgTypeError(message, self.name) raise nafmte from ke else: raise
def request_disc_doc(self): code_path = \ 'controller.LoginHandler.request_disc_doc' def _req_disc_doc(): dd = self.disc_doc_client.request( 'https://accounts.google.com/.well-known/' 'openid-configuration', 'GET') return self.decode_httplib2_json(dd) if self.disc_doc == None: messages.code_debug(code_path, 'Requesting discovery document ...') with ThreadPoolExecutor(1) as thread: self.__class__.disc_doc = thread.submit( _req_disc_doc) self.disc_doc = \ yield self.__class__.disc_doc #Este yield tiene que ir dentro, ya que el #thread no se comenzará a ejecutar si no se #yieldea y no se puede comenzar a ejecutar #fuera del with ya que ahí no existe ... o #algo asi XD :C self.__class__.disc_doc = None messages.code_debug(code_path, 'self.__class__.disc_doc = None') messages.code_debug(code_path, 'Discovery document arrived!') else: messages.code_debug(code_path, 'Waiting for discovery document ...') self.disc_doc = yield self.disc_doc messages.code_debug(code_path, 'Got the discovery document!')
def get(self, room_code): """Render the application.""" try: classes = {"system"} # room_code must be passed to the template as a # handler's attribute, because it is used in the # home panel. self.room_code = room_code if room_code is None: classes.update({"desktop"}) else: c = yield db.Code.get(room_code) if c.code_type is db.CodeType.room: classes.update({"teacher"}) else: classes.update({"student"}) msg.code_debug("controller.GUIHandler.get", "Rendering boxes.html ...") self.render("boxes.html", classes=classes) except db.NoObjectReturnedFromDB: self.render("boxes.html", classes=classes, critical="El código escaneado no está " "registrado!")