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))
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
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
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)
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
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)