Ejemplo n.º 1
0
    def post(self):
        result, error = {}, None
        params = params_from_request(self.request)
        project = params.pop('project')
        method = params.pop('method')
        data = params.get('data')
        if data is not None:
            try:
                data = json_decode(data)
            except Exception as e:
                logger.error(e)
            else:
                params["data"] = data

        project = self.application.get_project(project)
        if not project:
            error = self.application.PROJECT_NOT_FOUND
        else:
            result, error = yield self.application.process_call(project, method, params)

        self.set_header("Content-Type", "application/json")
        self.finish(json_encode({
            "body": result,
            "error": error
        }))
Ejemplo n.º 2
0
    def on_api_message(self, redis_message):
        """
        Got message from Redis, dispatch it into right message handler.
        """
        try:
            message = json_decode(redis_message[1])
        except ValueError:
            logger.error("Redis API - malformed JSON")
            return

        if not isinstance(message, dict):
            logger.error("Redis API - object expected")
            return

        project_id = message.get("project")
        if not project_id:
            logger.error("Redis API - project required")
            return

        data = message.get("data")
        if not data:
            logger.error("Redis API - data required")

        project, error = yield self.application.structure.get_project_by_id(project_id)
        if error:
            logger.error("Redis API - server error")
            return

        if not project:
            logger.error("Redis API - project not found")
            return

        _, error = yield self.application.process_api_data(project, data, False)
        if error:
            logger.error(error)
Ejemplo n.º 3
0
 def get_history(self, project_id, channel):
     history_list_key = self.get_history_list_key(project_id, channel)
     try:
         data = yield Task(self.worker.lrange, history_list_key, 0, -1)
     except StreamClosedError as e:
         raise Return((None, e))
     else:
         raise Return(([json_decode(x.decode()) for x in data], None))
Ejemplo n.º 4
0
 def get_history(self, project_id, channel):
     history_list_key = self.get_history_list_key(project_id, channel)
     try:
         data = yield Task(self.worker.lrange, history_list_key, 0, -1)
     except StreamClosedError as e:
         raise Return((None, e))
     else:
         raise Return(([json_decode(x.decode()) for x in data], None))
Ejemplo n.º 5
0
 def get_flash_message(self):
     """
     Returns the flash message.
     """
     cookie = self.get_secure_cookie(self.FLASH_COOKIE_NAME)
     if cookie:
         self.clear_cookie(self.FLASH_COOKIE_NAME)
         return json_decode(cookie)
     return None
Ejemplo n.º 6
0
def prepare_key_value(pair):
    if not pair:
        return
    key = pair[0].decode()
    try:
        value = json_decode(pair[1].decode())
    except ValueError:
        value = {}
    return key, value
Ejemplo n.º 7
0
 def get_flash_message(self):
     """
     Returns the flash message.
     """
     cookie = self.get_secure_cookie(self.FLASH_COOKIE_NAME)
     if cookie:
         self.clear_cookie(self.FLASH_COOKIE_NAME)
         return json_decode(cookie)
     return None
Ejemplo n.º 8
0
def decode_data(data):
    """
    Decode request body received from API client.
    """
    try:
        return json_decode(data)
    except Exception as err:
        logger.debug(err)
        return None
Ejemplo n.º 9
0
def prepare_key_value(pair):
    if not pair:
        return
    key = pair[0].decode()
    try:
        value = json_decode(pair[1].decode())
    except ValueError:
        value = {}
    return key, value
Ejemplo n.º 10
0
    def post(self, project_key):
        """
        Handle API HTTP requests.
        """
        timer = None
        if self.application.collector:
            timer = self.application.collector.get_timer('api_time')

        if not self.request.body:
            raise tornado.web.HTTPError(400, log_message="empty request")

        if self.request.headers.get("Content-Type", "").startswith("application/json"):
            # handle JSON requests if corresponding Content-Type specified
            encoded_data = self.request.body
            sign = self.request.headers.get("X-API-Sign")
        else:
            # handle application/x-www-form-urlencoded request
            sign = self.get_argument('sign', None)
            encoded_data = self.get_argument('data', None)

        if not sign:
            raise tornado.web.HTTPError(400, log_message="no data sign")

        if not encoded_data:
            raise tornado.web.HTTPError(400, log_message="no data")

        project = self.application.get_project(project_key)
        if not project:
            raise tornado.web.HTTPError(404, log_message="project not found")

        # use project secret to validate sign
        secret = project['secret']

        is_valid = auth.check_sign(
            secret, project_key, encoded_data, sign
        )

        if not is_valid:
            raise tornado.web.HTTPError(401, log_message="unauthorized")

        try:
            data = json_decode(encoded_data)
        except Exception as err:
            logger.debug(err)
            raise tornado.web.HTTPError(400, log_message="malformed data")

        multi_response, error = yield self.application.process_api_data(project, data)
        if error:
            raise tornado.web.HTTPError(400, log_message=error)

        if self.application.collector:
            self.application.collector.incr('api')
            timer.stop()

        self.json_response(multi_response.as_message())
Ejemplo n.º 11
0
    def update_channel_user_info(self, body, channel):
        """
        Try to extract channel specific user info from response body
        and keep it for channel.
        """
        try:
            info = json_decode(body)
        except Exception as e:
            logger.error(str(e))
            info = {}

        self.channel_user_info[channel] = info
Ejemplo n.º 12
0
    def on_redis_message(self, redis_message):
        """
        Got message from Redis, dispatch it into right message handler.
        """
        msg_type = redis_message[0]
        if six.PY3:
            msg_type = msg_type.decode()

        if msg_type != "message":
            return

        channel = redis_message[1]
        if six.PY3:
            channel = channel.decode()

        if channel == self.control_channel_name:
            yield self.handle_control_message(json_decode(redis_message[2]))
        elif channel == self.admin_channel_name:
            yield self.handle_admin_message(json_decode(redis_message[2]))
        else:
            yield self.handle_message(channel, redis_message[2])
Ejemplo n.º 13
0
    def on_redis_message(self, redis_message):
        """
        Got message from Redis, dispatch it into right message handler.
        """
        msg_type = redis_message[0]
        if six.PY3:
            msg_type = msg_type.decode()

        if msg_type != 'message':
            return

        channel = redis_message[1]
        if six.PY3:
            channel = channel.decode()

        if channel == self.control_channel_name:
            yield self.handle_control_message(json_decode(redis_message[2]))
        elif channel == self.admin_channel_name:
            yield self.handle_admin_message(json_decode(redis_message[2]))
        else:
            yield self.handle_message(channel, redis_message[2])
Ejemplo n.º 14
0
    def update_channel_info(self, body, channel):
        """
        Try to extract channel specific user info from response body
        and keep it for channel.
        """
        try:
            info = json_decode(body)
        except Exception as e:
            logger.error(str(e))
            info = {}

        self.channel_info[channel] = info
Ejemplo n.º 15
0
    def message_received(self, message):
        """
        Called when message from client received.
        """
        multi_response = MultiResponse()
        try:
            data = json_decode(message)
        except ValueError:
            logger.error('malformed JSON data')
            yield self.close_sock()
            raise Return((True, None))

        if isinstance(data, dict):
            # single object request
            response, err = yield self.process_obj(data)
            multi_response.add(response)
            if err:
                # error occurred, connection must be closed
                logger.error(err)
                yield self.sock.send(multi_response.as_message())
                yield self.close_sock()
                raise Return((True, None))

        elif isinstance(data, list):
            # multiple object request
            if len(data) > self.application.CLIENT_API_MESSAGE_LIMIT:
                logger.info("client API message limit exceeded")
                yield self.close_sock()
                raise Return((True, None))

            for obj in data:
                response, err = yield self.process_obj(obj)
                multi_response.add(response)
                if err:
                    # close connection in case of any error
                    logger.error(err)
                    yield self.sock.send(multi_response.as_message())
                    yield self.send_disconnect_message()
                    yield self.close_sock()
                    raise Return((True, None))

        else:
            logger.error('data not list and not dictionary')
            yield self.close_sock()
            raise Return((True, None))

        yield self.send(multi_response.as_message())

        raise Return((True, None))
Ejemplo n.º 16
0
    def message_received(self, message):
        """
        Called when message from client received.
        """
        multi_response = MultiResponse()
        try:
            data = json_decode(message)
        except ValueError:
            logger.error('malformed JSON data')
            yield self.close_sock()
            raise Return((True, None))

        if isinstance(data, dict):
            # single object request
            response, err = yield self.process_obj(data)
            multi_response.add(response)
            if err:
                # error occurred, connection must be closed
                logger.error(err)
                yield self.sock.send(multi_response.as_message())
                yield self.close_sock()
                raise Return((True, None))

        elif isinstance(data, list):
            # multiple object request
            if len(data) > self.application.CLIENT_API_MESSAGE_LIMIT:
                logger.info("client API message limit exceeded")
                yield self.close_sock()
                raise Return((True, None))

            for obj in data:
                response, err = yield self.process_obj(obj)
                multi_response.add(response)
                if err:
                    # close connection in case of any error
                    logger.error(err)
                    yield self.sock.send(multi_response.as_message())
                    yield self.send_disconnect_message()
                    yield self.close_sock()
                    raise Return((True, None))

        else:
            logger.error('data not list and not dictionary')
            yield self.close_sock()
            raise Return((True, None))

        yield self.send(multi_response.as_message())

        raise Return((True, None))
Ejemplo n.º 17
0
    def post_actions(self):
        params = params_from_request(self.request)
        method = params.pop('method')
        params.pop('_xsrf')
        data = params.get('data', None)
        if data is not None:
            try:
                data = json_decode(data)
            except Exception as e:
                logger.error(e)
            else:
                params["data"] = data

        result, error = yield self.application.process_call(
            self.project, method, params)

        self.set_header("Content-Type", "application/json")
        self.finish(json_encode({"body": result, "error": error}))
Ejemplo n.º 18
0
    def on_message(self, message):
        """
        The only method supported at moment - auth - used to
        authorize websocket connection.
        """
        try:
            data = json_decode(message)
        except ValueError:
            self.close()
            return

        try:
            method = data["method"]
            params = data["params"]
        except (TypeError, KeyError):
            self.close()
            return

        if method == "auth":
            try:
                token = params["token"]
            except (KeyError, TypeError):
                self.close()
                return
            else:
                user = decode_signed_value(
                    self.application.settings['cookie_secret'], 'token', token
                )
                if user:
                    self.subscribe()
                    self.send(json_encode({
                        "method": "auth",
                        "body": True
                    }))
                else:
                    self.send(json_encode({
                        "method": "auth",
                        "body": False
                    }))
                    self.close()
                    return
        else:
            self.close()
            return
Ejemplo n.º 19
0
    def post(self):
        json_data = self.get_argument("data")
        data = json_decode(json_data)
        res, err = yield self.application.structure.clear_structure()
        if err:
            raise tornado.web.HTTPError(500, log_message=str(err))

        for project in data.get("projects", []):
            res, err = yield self.application.structure.project_create(**project)
            if err:
                raise tornado.web.HTTPError(500, log_message=str(err))
            for namespace in data.get("namespaces", []):
                if namespace["project_id"] != project["_id"]:
                    continue
                res, err = yield self.application.structure.namespace_create(project, **namespace)
                if err:
                    raise tornado.web.HTTPError(500, log_message=str(err))

        self.redirect(self.reverse_url("main"))
Ejemplo n.º 20
0
    def post(self):
        json_data = self.get_argument("data")
        data = json_decode(json_data)
        res, err = yield self.application.structure.clear_structure()
        if err:
            raise tornado.web.HTTPError(500, log_message=str(err))

        for project in data.get("projects", []):
            res, err = yield self.application.structure.project_create(**project)
            if err:
                raise tornado.web.HTTPError(500, log_message=str(err))
            for namespace in data.get("namespaces", []):
                if namespace["project_id"] != project["_id"]:
                    continue
                res, err = yield self.application.structure.namespace_create(project, **namespace)
                if err:
                    raise tornado.web.HTTPError(500, log_message=str(err))

        self.redirect(self.reverse_url("main"))
Ejemplo n.º 21
0
    def post_actions(self):
        params = params_from_request(self.request)
        method = params.pop('method')
        params.pop('_xsrf')
        data = params.get('data', None)
        if data is not None:
            try:
                data = json_decode(data)
            except Exception as e:
                logger.error(e)
            else:
                params["data"] = data

        result, error = yield self.application.process_call(self.project, method, params)

        self.set_header("Content-Type", "application/json")
        self.finish(json_encode({
            "body": result,
            "error": error
        }))
Ejemplo n.º 22
0
    def post(self, project_id):
        """
        Handle API HTTP requests.
        """
        timer = None
        if self.application.collector:
            timer = self.application.collector.get_timer('api_time')

        if not self.request.body:
            raise tornado.web.HTTPError(400, log_message="empty request")

        if self.request.headers.get("Content-Type",
                                    "").startswith("application/json"):
            # handle JSON requests if corresponding Content-Type specified
            try:
                request_data = json_decode(self.request.body)
            except ValueError:
                raise tornado.web.HTTPError(400, log_message="malformed json")
            if not isinstance(request_data, dict):
                raise tornado.web.HTTPError(400, log_message="object expected")
            sign = request_data.get("sign")
            encoded_data = request_data.get("data")
        else:
            # handle application/x-www-form-urlencoded request
            sign = self.get_argument('sign', None)
            encoded_data = self.get_argument('data', None)

        if not sign:
            raise tornado.web.HTTPError(400, log_message="no data sign")

        if not encoded_data:
            raise tornado.web.HTTPError(400, log_message="no data")

        is_owner_request = False

        if project_id == self.application.OWNER_API_PROJECT_ID:
            # API request aims to be from superuser
            is_owner_request = True

        if is_owner_request:
            # use api secret key from configuration to check sign
            secret = self.application.config.get("api_secret")
            if not secret:
                raise tornado.web.HTTPError(
                    501, log_message="no api_secret in configuration file")
            project = None
        else:
            project, error = yield self.application.structure.get_project_by_id(
                project_id)
            if error:
                raise tornado.web.HTTPError(500, log_message=str(error))
            if not project:
                raise tornado.web.HTTPError(404,
                                            log_message="project not found")

            # use project secret key to validate sign
            secret = project['secret_key']

        is_valid = auth.check_sign(secret, project_id, encoded_data, sign)

        if not is_valid:
            raise tornado.web.HTTPError(401, log_message="unauthorized")

        try:
            data = json_decode(encoded_data)
        except Exception as err:
            logger.debug(err)
            raise tornado.web.HTTPError(400, log_message="malformed data")

        multi_response, error = yield self.application.process_api_data(
            project, data, is_owner_request)
        if error:
            raise tornado.web.HTTPError(400, log_message=error)

        if self.application.collector:
            self.application.collector.incr('api')
            timer.stop()

        self.json_response(multi_response.as_message())
Ejemplo n.º 23
0
    def post(self, project_id):
        """
        Handle API HTTP requests.
        """
        timer = None
        if self.application.collector:
            timer = self.application.collector.get_timer('api_time')

        if not self.request.body:
            raise tornado.web.HTTPError(400, log_message="empty request")

        if self.request.headers.get("Content-Type", "").startswith("application/json"):
            # handle JSON requests if corresponding Content-Type specified
            try:
                request_data = json_decode(self.request.body)
            except ValueError:
                raise tornado.web.HTTPError(400, log_message="malformed json")
            if not isinstance(request_data, dict):
                raise tornado.web.HTTPError(400, log_message="object expected")
            sign = request_data.get("sign")
            encoded_data = request_data.get("data")
        else:
            # handle application/x-www-form-urlencoded request
            sign = self.get_argument('sign', None)
            encoded_data = self.get_argument('data', None)

        if not sign:
            raise tornado.web.HTTPError(400, log_message="no data sign")

        if not encoded_data:
            raise tornado.web.HTTPError(400, log_message="no data")

        is_owner_request = False

        if project_id == self.application.OWNER_API_PROJECT_ID:
            # API request aims to be from superuser
            is_owner_request = True

        if is_owner_request:
            # use api secret key from configuration to check sign
            secret = self.application.config.get("api_secret")
            if not secret:
                raise tornado.web.HTTPError(501, log_message="no api_secret in configuration file")
            project = None
        else:
            project, error = yield self.application.structure.get_project_by_id(project_id)
            if error:
                raise tornado.web.HTTPError(500, log_message=str(error))
            if not project:
                raise tornado.web.HTTPError(404, log_message="project not found")

            # use project secret key to validate sign
            secret = project['secret_key']

        is_valid = auth.check_sign(
            secret, project_id, encoded_data, sign
        )

        if not is_valid:
            raise tornado.web.HTTPError(401, log_message="unauthorized")

        try:
            data = json_decode(encoded_data)
        except Exception as err:
            logger.debug(err)
            raise tornado.web.HTTPError(400, log_message="malformed data")

        multi_response, error = yield self.application.process_api_data(project, data, is_owner_request)
        if error:
            raise tornado.web.HTTPError(400, log_message=error)

        if self.application.collector:
            self.application.collector.incr('api')
            timer.stop()

        self.json_response(multi_response.as_message())
Ejemplo n.º 24
0
    def post(self, project_id):
        """
        Handle API HTTP requests.
        """
        if not self.request.body:
            raise tornado.web.HTTPError(400, log_message="empty request")

        if self.request.headers.get("Content-Type", "").startswith("application/json"):
            # handle JSON requests if corresponding Content-Type specified
            try:
                request_data = json_decode(self.request.body)
            except ValueError:
                raise tornado.web.HTTPError(400, log_message="malformed json")
            if not isinstance(request_data, dict):
                raise tornado.web.HTTPError(400, log_message="object expected")
            sign = request_data.get("sign")
            encoded_data = request_data.get("data")
        else:
            # handle application/x-www-form-urlencoded request
            sign = self.get_argument('sign', None)
            encoded_data = self.get_argument('data', None)

        if not sign:
            raise tornado.web.HTTPError(400, log_message="no data sign")

        if not encoded_data:
            raise tornado.web.HTTPError(400, log_message="no data")

        is_owner_request = False

        if project_id == self.application.OWNER_API_PROJECT_ID:
            # API request aims to be from superuser
            is_owner_request = True

        if is_owner_request:
            # use api secret key from configuration to check sign
            secret = self.application.config.get("api_secret")
            if not secret:
                raise tornado.web.HTTPError(501, log_message="no api_secret in configuration file")
            project = None

        else:
            project, error = yield self.application.structure.get_project_by_id(project_id)
            if error:
                raise tornado.web.HTTPError(500, log_message=str(error))
            if not project:
                raise tornado.web.HTTPError(404, log_message="project not found")

            # use project secret key to validate sign
            secret = project['secret_key']

        is_valid = auth.check_sign(
            secret, project_id, encoded_data, sign
        )

        if not is_valid:
            raise tornado.web.HTTPError(401, log_message="unauthorized")

        data = auth.decode_data(encoded_data)
        if not data:
            raise tornado.web.HTTPError(400, log_message="malformed data")

        multi_response = MultiResponse()

        if isinstance(data, dict):
            # single object request
            response = yield self.process_object(data, project, is_owner_request)
            multi_response.add(response)
        elif isinstance(data, list):
            # multiple object request
            if len(data) > self.application.ADMIN_API_MESSAGE_LIMIT:
                raise tornado.web.HTTPError(
                    400,
                    log_message="admin API message limit exceeded (received {0} messages)".format(
                        len(data)
                    )
                )

            for obj in data:
                response = yield self.process_object(obj, project, is_owner_request)
                multi_response.add(response)
        else:
            raise tornado.web.HTTPError(400, log_message="data not a list or dictionary")

        if self.application.collector:
            self.application.collector.incr('api')

        self.json_response(multi_response.as_message())
Ejemplo n.º 25
0
    def handle_connect(self, params):
        """
        Authenticate client's connection, initialize required
        variables in case of successful authentication.
        """
        if self.application.collector:
            self.application.collector.incr('connect')
            self.application.collector.incr(self.sock.session.transport_name)

        if self.is_authenticated:
            raise Return((self.uid, None))

        token = params["token"]
        user = params["user"]
        project_id = params["project"]
        timestamp = params["timestamp"]
        user_info = params.get("info")

        project, error = yield self.application.get_project(project_id)
        if error:
            raise Return((None, error))

        secret_key = project['secret_key']

        error_msg = self.validate_token(token, secret_key, project_id, user, timestamp, user_info)
        if error_msg:
            raise Return((None, error_msg))

        if user_info is not None:
            try:
                user_info = json_decode(user_info)
            except Exception as err:
                logger.error("malformed JSON data in user_info")
                logger.error(err)
                user_info = None

        try:
            timestamp = int(timestamp)
        except ValueError:
            raise Return((None, "invalid timestamp"))

        self.user = user
        self.examined_at = timestamp

        if self.is_connection_must_be_checked(project, self.examined_at):
            # connection expired - this is a rare case when Centrifuge went offline
            # for a while or client turned on his computer from sleeping mode.

            # put this client into the queue of connections waiting for
            # permission to reconnect with expired credentials. To avoid waiting
            # client must reconnect with actual credentials i.e. reload browser
            # window.

            if project_id not in self.application.expired_reconnections:
                self.application.expired_reconnections[project_id] = []
            self.application.expired_reconnections[project_id].append(self)

            if project_id not in self.application.expired_connections:
                self.application.expired_connections[project_id] = {
                    "users": set(),
                    "checked_at": None
                }
            self.application.expired_connections[project_id]["users"].add(user)

            self.connect_queue = toro.Queue(maxsize=1)
            value = yield self.connect_queue.get()
            if not value:
                yield self.close_sock()
                raise Return((None, self.application.UNAUTHORIZED))
            else:
                self.connect_queue = None

        # Welcome to Centrifuge dear Connection!
        self.is_authenticated = True
        self.project_id = project_id
        self.token = token
        self.default_user_info = {
            'user_id': self.user,
            'client_id': self.uid,
            'default_info': user_info,
            'channel_info': None
        }
        self.channels = {}

        self.presence_ping_task = PeriodicCallback(
            self.send_presence_ping, self.application.engine.presence_ping_interval
        )
        self.presence_ping_task.start()

        self.application.add_connection(project_id, self.user, self.uid, self)

        raise Return((self.uid, None))
Ejemplo n.º 26
0
    def handle_connect(self, params):
        """
        Authenticate client's connection, initialize required
        variables in case of successful authentication.
        """
        if self.application.collector:
            self.application.collector.incr('connect')
            self.application.collector.incr(self.sock.session.transport_name)

        if self.is_authenticated:
            raise Return((self.uid, None))

        project_id = params["project"]
        user = params["user"]
        info = params.get("info", "{}")

        if not self.application.INSECURE:
            token = params["token"]
            timestamp = params["timestamp"]
        else:
            token = timestamp = None

        project, error = yield self.application.get_project(project_id)
        if error:
            raise Return((None, error))

        secret_key = project['secret_key']

        if not self.application.INSECURE:
            error_msg = self.validate_token(
                token, secret_key, project_id, user, timestamp, info
            )
            if error_msg:
                raise Return((None, error_msg))

        if info:
            try:
                info = json_decode(info)
            except Exception as err:
                logger.error("malformed JSON data in user_info")
                logger.error(err)
                info = {}
        else:
            info = {}

        if not self.application.INSECURE:
            try:
                timestamp = int(timestamp)
            except ValueError:
                raise Return((None, "invalid timestamp"))
        else:
            # we are not interested in timestamp in case of insecure mode so just
            # set it to current timestamp
            timestamp = int(time.time())

        self.user = user
        self.project_id = project_id
        self.timestamp = timestamp

        time_to_expire = None
        if not self.application.INSECURE and project.get('connection_check', False):
            now = time.time()
            time_to_expire = self.timestamp + project.get("connection_lifetime", 3600) - now
            if time_to_expire <= 0:
                raise Return(({"client": None, "expired": True, "ttl": project.get("connection_lifetime", 3600)}, None))

        # Welcome to Centrifuge dear Connection!
        self.is_authenticated = True
        self.default_info = {
            'user_id': self.user,
            'client_id': self.uid,
            'default_info': info,
            'channel_info': None
        }

        self.channels = {}
        self.presence_ping_task = PeriodicCallback(
            self.send_presence_ping, self.application.engine.presence_ping_interval
        )
        self.presence_ping_task.start()
        self.application.add_connection(project_id, self.user, self.uid, self)

        if time_to_expire:
            self.expire_timeout = IOLoop.current().add_timeout(
                time.time() + project.get("connection_lifetime", 3600), self.expire
            )

        body = {
            "client": self.uid,
            "expired": False,
            "ttl": project.get("connection_lifetime", 3600) if project.get("connection_check", False) else None
        }
        raise Return((body, None))
Ejemplo n.º 27
0
    def handle_connect(self, params):
        """
        Authenticate client's connection, initialize required
        variables in case of successful authentication.
        """
        if self.application.collector:
            self.application.collector.incr('connect')
            self.application.collector.incr(self.sock.session.transport_name)

        if self.is_authenticated:
            raise Return((self.uid, None))

        project_name = params["project"]
        user = params["user"]
        info = params.get("info", "")

        if not self.application.INSECURE:
            token = params["token"]
            timestamp = params["timestamp"]
        else:
            token = timestamp = None

        project = self.application.get_project(project_name)
        if not project:
            raise Return((None, self.application.PROJECT_NOT_FOUND))

        secret = project['secret']

        if not self.application.INSECURE:
            error_msg = self.validate_token(
                token, secret, project_name, user, timestamp, info
            )
            if error_msg:
                raise Return((None, error_msg))

        if info:
            try:
                info = json_decode(info)
            except Exception as err:
                logger.error("malformed JSON data in user_info")
                logger.error(err)
                info = {}
        else:
            info = {}

        if not self.application.INSECURE:
            try:
                timestamp = int(timestamp)
            except ValueError:
                raise Return((None, "invalid timestamp"))
        else:
            # we are not interested in timestamp in case of insecure mode so just
            # set it to current timestamp
            timestamp = int(time.time())

        self.user = user
        self.project_name = project_name
        self.timestamp = timestamp

        time_to_expire = None
        if not self.application.INSECURE and project['connection_lifetime'] > 0:
            now = time.time()
            conn_lifetime = project["connection_lifetime"]
            time_to_expire = self.timestamp + conn_lifetime - now
            if time_to_expire <= 0:
                raise Return(({"client": None, "expired": True, "ttl": conn_lifetime}, None))

        # Welcome to Centrifuge dear Connection!
        self.is_authenticated = True
        self.default_info = {
            'user': self.user,
            'client': self.uid,
            'default_info': info,
            'channel_info': None
        }

        self.channels = {}
        self.presence_ping_task = PeriodicCallback(
            self.send_presence_ping, self.application.engine.presence_ping_interval
        )
        self.presence_ping_task.start()
        self.application.add_connection(project_name, self.user, self.uid, self)

        conn_lifetime = project["connection_lifetime"]

        if time_to_expire:
            self.expire_timeout = IOLoop.current().add_timeout(
                time.time() + conn_lifetime, self.expire
            )

        body = {
            "client": self.uid,
            "expired": False,
            "ttl": conn_lifetime if project["connection_lifetime"] > 0 else None
        }
        raise Return((body, None))
Ejemplo n.º 28
0
    def handle_connect(self, params):
        """
        Authenticate client's connection, initialize required
        variables in case of successful authentication.
        """
        if self.application.collector:
            self.application.collector.incr('connect')
            self.application.collector.incr(self.sock.session.transport_name)

        if self.is_authenticated:
            raise Return((self.uid, None))

        token = params["token"]
        user = params["user"]
        project_id = params["project"]
        timestamp = params["timestamp"]
        user_info = params.get("info")

        project, error = yield self.application.get_project(project_id)
        if error:
            raise Return((None, error))

        secret_key = project['secret_key']

        try:
            client_token = auth.get_client_token(secret_key,
                                                 project_id,
                                                 user,
                                                 timestamp,
                                                 user_info=user_info)
        except Exception as err:
            logger.error(err)
            raise Return((None, "invalid connection parameters"))

        if token != client_token:
            raise Return((None, "invalid token"))

        if user_info is not None:
            try:
                user_info = json_decode(user_info)
            except Exception as err:
                logger.error("malformed JSON data in user_info")
                logger.error(err)
                user_info = None

        try:
            timestamp = int(timestamp)
        except ValueError:
            raise Return((None, "invalid timestamp"))

        now = time.time()

        self.user = user
        self.examined_at = timestamp

        connection_check = project.get('connection_check', False)

        if connection_check and self.examined_at + project.get(
                "connection_lifetime", 24 * 365 * 3600) < now:
            # connection expired - this is a rare case when Centrifuge went offline
            # for a while or client turned on his computer from sleeping mode.

            # put this client into the queue of connections waiting for
            # permission to reconnect with expired credentials. To avoid waiting
            # client must reconnect with actual credentials i.e. reload browser
            # window.

            if project_id not in self.application.expired_reconnections:
                self.application.expired_reconnections[project_id] = []
            self.application.expired_reconnections[project_id].append(self)

            if project_id not in self.application.expired_connections:
                self.application.expired_connections[project_id] = {
                    "users": set(),
                    "checked_at": None
                }
            self.application.expired_connections[project_id]["users"].add(user)

            self.connect_queue = toro.Queue(maxsize=1)
            value = yield self.connect_queue.get()
            if not value:
                yield self.close_sock()
                raise Return((None, self.application.UNAUTHORIZED))
            else:
                self.connect_queue = None

        # Welcome to Centrifuge dear Connection!
        self.is_authenticated = True
        self.project_id = project_id
        self.token = token
        self.default_user_info = {
            'user_id': self.user,
            'client_id': self.uid,
            'default_info': user_info,
            'channel_info': None
        }
        self.channels = {}

        self.presence_ping_task = PeriodicCallback(
            self.send_presence_ping,
            self.application.engine.presence_ping_interval)
        self.presence_ping_task.start()

        self.application.add_connection(project_id, self.user, self.uid, self)

        raise Return((self.uid, None))