예제 #1
0
    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
예제 #2
0
    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)
예제 #3
0
    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)
        )
예제 #4
0
    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
예제 #5
0
    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!")
예제 #6
0
    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!')
예제 #7
0
    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!')
예제 #8
0
 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!')
예제 #9
0
    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!'
            )
예제 #10
0
    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)
        )
예제 #11
0
    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!')
예제 #12
0
 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
예제 #13
0
 def on_close(self):
     MSGHandler.clients.remove(self)
     
     messages.code_debug(
         self.path+'.on_close',
         'Connection closed! %s (%s)' %
             (self, self.request.remote_ip)
     )
예제 #14
0
    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!')
예제 #15
0
 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
예제 #16
0
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)
예제 #17
0
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)
        )
예제 #18
0
 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
예제 #19
0
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)
예제 #20
0
    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
예제 #21
0
    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!")
예제 #22
0
    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)
예제 #23
0
    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
예제 #24
0
    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
예제 #25
0
 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)
예제 #26
0
    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
예제 #27
0
 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!')
예제 #28
0
    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!")