Example #1
0
    def get_subscriber(self, name=None):
        """
        GET SUBSCRIBER BY id, OR BY QUEUE name
        """
        with self.db.transaction() as t:
            result = t.query(
                SQL(
                    f"""
                    SELECT
                        MIN(s.id) as id
                    FROM
                        {SUBSCRIBER} AS s
                    LEFT JOIN 
                        {QUEUE} as q on q.id = s.queue
                    WHERE
                        q.name = {quote_value(name)}
                    GROUP BY 
                        s.queue
                    """
                )
            )
            if not result:
                Log.error("not expected")

            queue = self.get_or_create_queue(name)
            sub_info = t.query(
                sql_query(
                    {"from": SUBSCRIBER, "where": {"eq": {"id": first_row(result).id}}}
                )
            )

            return Subscription(queue=queue, kwargs=first_row(sub_info))
Example #2
0
    def _load_from_database(self):
        # FIND ALL TABLES
        result = self.db.query(
            sql_query({
                "from": "sqlite_master",
                "where": {
                    "eq": {
                        "type": "table"
                    }
                },
                "orderby": "name"
            }))
        tables = wrap([{k: d
                        for k, d in zip(result.header, row)}
                       for row in result.data])
        last_nested_path = ["."]
        for table in tables:
            if table.name.startswith("__"):
                continue
            base_table, nested_path = tail_field(table.name)

            # FIND COMMON NESTED PATH SUFFIX
            if nested_path == ".":
                last_nested_path = []
            else:
                for i, p in enumerate(last_nested_path):
                    if startswith_field(nested_path, p):
                        last_nested_path = last_nested_path[i:]
                        break
                else:
                    last_nested_path = []

            full_nested_path = [nested_path] + last_nested_path
            self._snowflakes[literal_field(base_table)] += [full_nested_path]

            # LOAD THE COLUMNS
            details = self.db.about(table.name)

            for cid, name, dtype, notnull, dfft_value, pk in details:
                if name.startswith("__"):
                    continue
                cname, ctype = untyped_column(name)
                self.add(
                    Column(name=cname,
                           jx_type=coalesce(sql_type_to_json_type.get(ctype),
                                            IS_NULL),
                           nested_path=full_nested_path,
                           es_type=dtype,
                           es_column=name,
                           es_index=table.name,
                           last_updated=Date.now()))
            last_nested_path = full_nested_path
Example #3
0
    def get_or_create_queue(self, name, block_size_mb=8, kwargs=None):
        for q in self.queues:
            if q.name == name:
                return q

        # VERIFY QUEUE EXISTS
        with self.db.transaction() as t:
            result = t.query(
                sql_query({"from": QUEUE, "where": {"eq": {"name": name}}})
            )

            if not result.data:
                id = self.next_id()
                t.execute(
                    sql_insert(
                        table=QUEUE,
                        records={
                            "id": id,
                            "name": name,
                            "next_serial": 1,
                            "block_size_mb": block_size_mb,
                            "block_start": 1,
                            "block_end": 1,
                        },
                    )
                )
                t.execute(
                    sql_insert(
                        table=SUBSCRIBER,
                        records={
                            "id": id,
                            "queue": id,
                            "confirm_delay_seconds": 60,
                            "look_ahead_serial": 1000,
                            "last_confirmed_serial": 0,
                            "next_emit_serial": 1,
                            "last_emit_timestamp": Date.now()
                        },
                    )
                )

                output = Queue(id=id, broker=self, kwargs=kwargs)
            else:
                kwargs = first_row(result)
                output = Queue(broker=self, kwargs=kwargs)

        self.queues.append(output)
        return output
Example #4
0
    def device_status(self, path=None):
        """
        AUTOMATION CAN CALL THIS ENDPOINT TO FIND OUT THE LOGIN STATUS
        RESPOND WITH {"ok":true} WHEN USER HAS LOGGED IN, AND user IS
        ASSOCIATED WITH SESSION
        """
        now = Date.now().unix
        session_id = request.cookies.get(self.device.register.session.name)
        if not session_id:
            return Response('{"try_again":false, "status":"no session id"}',
                            status=401)
        device_session = self.session_manager.get_session(session_id)

        request_body = request.get_data()
        signed = json2value(request_body.decode("utf8"))
        command = rsa_crypto.verify(signed, device_session.public_key)

        time_sent = parse(command.timestamp).unix
        if not (now - LEEWAY <= time_sent < now + LEEWAY):
            return Response(
                '{"try_again":false, "status":"timestamp is not recent"}',
                status=401)
        if device_session.expires < now:
            return Response(
                '{"try_again":false, "status":"session is too old"}',
                status=401)
        if device_session.user:
            device_session.public_key = None
            return Response('{"try_again":false, "status":"verified"}',
                            status=200)

        state_info = self.device.db.query(
            sql_query({
                "select": "session_id",
                "from": self.device.table,
                "where": {
                    "eq": {
                        "state": device_session.state
                    }
                },
            }))
        if not state_info.data:
            return Response(
                '{"try_again":false, "status":"State has been lost"}',
                status=401)

        return Response('{"try_again":true, "status":"still waiting"}',
                        status=200)
Example #5
0
    def pop_text(self):
        with self.queue.broker.db.transaction() as t:
            # CHECK IF SOME MESSAGES CAN BE RESENT
            result = t.query(
                SQL(f"""
                    SELECT
                        m.serial,
                        m.content
                    FROM
                        {UNCONFIRMED} AS u
                    LEFT JOIN
                        {MESSAGES} AS m ON m.serial=u.serial
                    WHERE
                        m.queue = {quote_value(self.queue.id)} AND
                        u.subscriber = {quote_value(self.id)} AND 
                        u.deliver_time <= {quote_value(Date.now().unix - self.confirm_delay_seconds)}
                    ORDER BY
                        u.deliver_time
                    LIMIT
                        1
                    """))

            if result.data:
                record = first_row(result)
                # RECORD IT WAS SENT AGAIN
                now = Date.now()
                t.execute(
                    sql_update(
                        UNCONFIRMED,
                        {
                            "set": {
                                "deliver_time": now
                            },
                            "where": {
                                "eq": {
                                    "subscriber": self.id,
                                    "serial": record.serial
                                }
                            },
                        },
                    ))
                t.execute(
                    sql_update(SUBSCRIBER,
                               {"set": {
                                   "last_emit_timestamp": now
                               }}))
                return record.serial, record.content

            # IS THERE A NEVER-SENT MESSAGE?
            serial = self._next_serial(t)
            if not serial:
                return 0, None

            result = t.query(
                sql_query({
                    "select": "content",
                    "from": MESSAGES,
                    "where": {
                        "eq": {
                            "queue": self.queue.id,
                            "serial": serial
                        }
                    },
                }))

            if not result.data:
                result = t.query(
                    SQL(f"""
                        SELECT 
                            serial,
                            path
                        FROM 
                            {BLOCKS}
                        WHERE
                            queue = {quote_value(self.queue.id)} AND
                            serial <= {quote_value(serial)}
                        ORDER BY
                            serial DESC
                        LIMIT 1
                        """))

                if not result.data:
                    Log.error("not expected")

                row = first_row(result)
                self.queue.load(path=row.path, start=row.serial)

                # RETRY
                result = t.query(
                    sql_query({
                        "select": "content",
                        "from": MESSAGES,
                        "where": {
                            "eq": {
                                "queue": self.queue.id,
                                "serial": serial
                            }
                        },
                    }))

                if not result.data:
                    Log.error("not expected")

            content = first_row(result).content

            # RECORD IT WAS SENT
            now = Date.now()
            t.execute(
                sql_insert(
                    UNCONFIRMED,
                    {
                        "subscriber": self.id,
                        "serial": serial,
                        "deliver_time": now
                    },
                ))
            t.execute(
                sql_update(SUBSCRIBER, {"set": {
                    "last_emit_timestamp": now
                }}))

            return serial, content
Example #6
0
    def device_callback(self, path=None):
        # HANDLE BROWESR RETURN FROM AUTH0 LOGIN
        error = request.args.get("error")
        if error:
            Log.error("You did it wrong")
        session_id = request.cookies.get(self.device.login.session.name)
        if not session_id:
            Log.error("You did it wrong")
        login_session = self.session_manager.get_session(session_id)

        code = request.args.get("code")
        state = request.args.get("state")

        result = self.device.db.query(
            sql_query({
                "from": "device",
                "select": "session_id",
                "where": {
                    "eq": {
                        "state": state
                    }
                },
            }))
        if not result.data:
            Log.error("expecting valid state")
        device_session_id = result.data[0][0]

        # GO BACK TO AUTH0 TO GET TOKENS
        token_request = {
            "client_id": self.device.auth0.client_id,
            "redirect_uri": self.device.auth0.redirect_uri,
            "code_verifier": login_session.code_verifier,
            "code": code,
            "grant_type": "authorization_code",
        }
        DEBUG and Log.note("Send token request to Auth0:\n {{request}}",
                           request=token_request)
        auth_response = requests.request(
            "POST",
            str(URL("https://" + self.device.auth0.domain,
                    path="oauth/token")),
            headers={
                "Accept": mimetype.JSON,
                "Content-Type": mimetype.JSON,
                # "Referer": str(URL(self.device.auth0.redirect_uri, query={"code": code, "state": state})),
            },
            data=value2json(token_request),
        )

        try:
            auth_result = wrap(auth_response.json())
        except Exception as e:
            Log.error("not json {{value}}",
                      value=auth_response.content,
                      cause=e)

        # VERIFY TOKENS, ADD USER TO DEVICE'S SESSION
        user_details = self.verify_opaque_token(auth_result.access_token)
        self.session_manager.update_session(
            device_session_id,
            {"user": self.permissions.get_or_create_user(user_details)},
        )

        # REMOVE DEVICE SETUP STATE
        with self.device.db.transaction() as t:
            t.execute(
                ConcatSQL(
                    SQL_DELETE,
                    SQL_FROM,
                    quote_column(self.device.table),
                    SQL_WHERE,
                    sql_eq(state=state),
                ))
        Log.note("login complete")
        return Response("Login complete. You may close this page", status=200)