Fix audit findings in queue fairness and official account lifecycle
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Admin account connection; password transient, session persisted as a secret."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import threading
|
||||
@@ -44,14 +45,32 @@ class Connection:
|
||||
self.reader, self.path, self.secrets = reader, path, secrets
|
||||
self.username = ""
|
||||
self.lock = threading.RLock()
|
||||
if path.exists():
|
||||
data = json.loads(path.read_text())
|
||||
reader.rpc = OfficialRPC(data["token"])
|
||||
reader._robot_id = data["robot_id"]
|
||||
self.username = data["username"]
|
||||
self.secrets.append(data["token"])
|
||||
try:
|
||||
if path.exists():
|
||||
data = json.loads(path.read_text())
|
||||
if (
|
||||
not isinstance(data, dict)
|
||||
or not isinstance(data.get("token"), str)
|
||||
or not isinstance(data.get("username"), str)
|
||||
or (
|
||||
data.get("robot_id") is not None
|
||||
and (type(data["robot_id"]) is not int or data["robot_id"] <= 0)
|
||||
)
|
||||
or "robot_id" not in data
|
||||
):
|
||||
raise ValueError()
|
||||
reader.rpc = OfficialRPC(data["token"])
|
||||
reader._robot_id = data["robot_id"]
|
||||
self.username = data["username"]
|
||||
self.secrets.append(data["token"])
|
||||
except (OSError, ValueError, KeyError, TypeError):
|
||||
reader.rpc.disconnect()
|
||||
reader._robot_id = None
|
||||
logging.getLogger("frame-flow").warning(
|
||||
"Official connection configuration unavailable; reconnect required"
|
||||
)
|
||||
|
||||
def _save(self):
|
||||
def _save(self, token, username, robot_id):
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", dir=self.path.parent, delete=False
|
||||
@@ -60,9 +79,9 @@ class Connection:
|
||||
try:
|
||||
json.dump(
|
||||
{
|
||||
"token": self.reader.rpc._token,
|
||||
"username": self.username,
|
||||
"robot_id": self.reader._robot_id,
|
||||
"token": token,
|
||||
"username": username,
|
||||
"robot_id": robot_id,
|
||||
},
|
||||
f,
|
||||
)
|
||||
@@ -73,6 +92,31 @@ class Connection:
|
||||
finally:
|
||||
temp.unlink(missing_ok=True)
|
||||
|
||||
def _commit(self, rpc, username, robot_id):
|
||||
self._save(rpc._token, username, robot_id)
|
||||
self.reader.rpc = rpc
|
||||
self.reader._robot_id = robot_id
|
||||
self.reader._cached = None
|
||||
self.username = username
|
||||
if rpc._token and rpc._token not in self.secrets:
|
||||
self.secrets.append(rpc._token)
|
||||
|
||||
def _query(self, call):
|
||||
try:
|
||||
return call()
|
||||
except FlowError as error:
|
||||
if error.code == E.OFFICIAL_AUTH_REQUIRED:
|
||||
self.reader.rpc.disconnect()
|
||||
self.reader._cached = None
|
||||
self.reader._robot_id = None
|
||||
try:
|
||||
self._save("", self.username, None)
|
||||
except OSError:
|
||||
logging.getLogger("frame-flow").warning(
|
||||
"Expired official connection could not be persisted"
|
||||
)
|
||||
raise
|
||||
|
||||
def _devices(self, rpc):
|
||||
if self.reader.mock:
|
||||
return [
|
||||
@@ -85,7 +129,9 @@ class Connection:
|
||||
)
|
||||
]
|
||||
try:
|
||||
data = rpc.devices()["data"]
|
||||
data = (
|
||||
self._query(rpc.devices) if rpc is self.reader.rpc else rpc.devices()
|
||||
)["data"]
|
||||
return [
|
||||
OfficialDevice(
|
||||
id=row["id"],
|
||||
@@ -119,12 +165,7 @@ class Connection:
|
||||
if not self.reader.mock:
|
||||
rpc.login(username, password)
|
||||
devices = self._devices(rpc)
|
||||
self.reader.rpc = rpc
|
||||
self.reader._robot_id = None
|
||||
self.reader._cached = None
|
||||
self.username = username
|
||||
self.secrets.append(rpc._token)
|
||||
self._save()
|
||||
self._commit(rpc, username, None)
|
||||
return ConnectionState(
|
||||
authorized=True,
|
||||
username=username,
|
||||
@@ -143,20 +184,14 @@ class Connection:
|
||||
raise FlowError(
|
||||
E.DEVICE_NOT_FOUND, "Official device not in this account", 404
|
||||
)
|
||||
self.reader._robot_id = robot_id
|
||||
self.reader._cached = None
|
||||
self._save()
|
||||
self._commit(self.reader.rpc, self.username, robot_id)
|
||||
return self.state()
|
||||
|
||||
def disconnect(self):
|
||||
with self.lock:
|
||||
self.reader.rpc.disconnect()
|
||||
self.reader._robot_id = None
|
||||
self.reader._cached = None
|
||||
self.username = ""
|
||||
self._save()
|
||||
self._commit(OfficialRPC(), "", None)
|
||||
return self.state()
|
||||
|
||||
def widgets(self):
|
||||
with self.lock:
|
||||
return self.reader.read()
|
||||
return self._query(self.reader.read)
|
||||
|
||||
Reference in New Issue
Block a user