fix flake8 findings

This commit is contained in:
Robert Resch 2022-08-25 11:50:11 +02:00
parent 23a84dda04
commit a0eb86a76f
16 changed files with 230 additions and 121 deletions

View file

@ -1,3 +1,4 @@
"""Init module."""
import asyncio
import logging
import os
@ -5,8 +6,8 @@ import socket
import sys
from bumper.db import (
bot_reset_connectionStatus,
client_reset_connectionStatus,
bot_reset_connection_status,
client_reset_connection_status,
revoke_expired_oauths,
revoke_expired_tokens,
)
@ -18,6 +19,7 @@ from bumper.xmppserver import XMPPServer
def strtobool(strbool: str | bool | None) -> bool:
"""Convert str to bool."""
if str(strbool).lower() in ["true", "1", "t", "y", "on", "yes"]:
return True
else:
@ -77,13 +79,14 @@ web_server_bindings = [
async def start() -> None:
"""Start bumper."""
# Reset xmpp/mqtt to false in database for bots and clients
bot_reset_connectionStatus()
client_reset_connectionStatus()
bot_reset_connection_status()
client_reset_connection_status()
try:
loop = asyncio.get_event_loop()
except:
except: # noqa: E722
loop = asyncio.new_event_loop()
if bumper_debug:
@ -150,22 +153,28 @@ async def start() -> None:
async def maintenance() -> None:
"""Run maintenance."""
revoke_expired_tokens()
revoke_expired_oauths()
async def shutdown() -> None:
"""Shutdown bumper."""
try:
bumperlog.info("Shutting down")
global shutting_down
shutting_down = True
global mqtt_helperbot
await mqtt_helperbot.disconnect()
global web_server
await web_server.shutdown()
global mqtt_server
while mqtt_server.state == "starting":
await asyncio.sleep(0.1)
if mqtt_server.state == "started":
await mqtt_server.shutdown()
global xmpp_server
if xmpp_server.server:
if xmpp_server.server.is_serving:
xmpp_server.server.close()
@ -177,6 +186,7 @@ async def shutdown() -> None:
def main(argv: None | list[str] = None) -> None:
"""Start everything."""
import argparse
global bumper_debug

View file

@ -1,3 +1,4 @@
"""Database module."""
import os
from datetime import datetime, timedelta
from typing import Any
@ -13,17 +14,17 @@ from .util import get_logger
_LOGGER = get_logger("db")
def db_file() -> str:
return os.environ.get("DB_FILE") or os_db_path()
def _db_file() -> str:
return os.environ.get("DB_FILE") or _os_db_path()
def os_db_path() -> str: # createdir=True):
def _os_db_path() -> str: # createdir=True):
return os.path.join(bumper.data_dir, "bumper.db")
def db_get() -> TinyDB:
def _db_get() -> TinyDB:
# Will create the database if it doesn't exist
db = TinyDB(db_file())
db = TinyDB(_db_file())
# Will create the tables if they don't exist
db.table("users", cache_size=0)
@ -36,29 +37,32 @@ def db_get() -> TinyDB:
def user_add(userid: str) -> None:
"""Add user."""
newuser = BumperUser()
newuser.userid = userid
user = user_get(userid)
if not user:
_LOGGER.info(f"Adding new user with userid: {newuser.userid}")
user_full_upsert(newuser.asdict())
_user_full_upsert(newuser.asdict())
def user_get(userid: str) -> None | Document:
users = db_get().table("users")
"""Get user."""
users = _db_get().table("users")
User = Query()
return users.get(User.userid == userid)
def user_by_deviceid(deviceid: str) -> None | Document:
users = db_get().table("users")
def user_by_device_id(deviceid: str) -> None | Document:
"""Get user by device id."""
users = _db_get().table("users")
User = Query()
return users.get(User.devices.any([deviceid]))
def user_full_upsert(user: dict[str, Any]) -> None:
opendb = db_get()
def _user_full_upsert(user: dict[str, Any]) -> None:
opendb = _db_get()
with opendb:
users = opendb.table("users")
User = Query()
@ -66,21 +70,23 @@ def user_full_upsert(user: dict[str, Any]) -> None:
def user_add_device(userid: str, devid: str) -> None:
opendb = db_get()
"""Add device to user."""
opendb = _db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
if user:
userdevices = list(user["devices"])
if not devid in userdevices:
if devid not in userdevices:
userdevices.append(devid)
users.upsert({"devices": userdevices}, User.userid == userid)
def user_remove_device(userid: str, devid: str) -> None:
opendb = db_get()
"""Remove device from user."""
opendb = _db_get()
with opendb:
users = opendb.table("users")
User = Query()
@ -94,21 +100,23 @@ def user_remove_device(userid: str, devid: str) -> None:
def user_add_bot(userid: str, did: str) -> None:
opendb = db_get()
"""Add bot to user."""
opendb = _db_get()
with opendb:
users = opendb.table("users")
User = Query()
user = users.get(User.userid == userid)
if user:
userbots = list(user["bots"])
if not did in userbots:
if did not in userbots:
userbots.append(did)
users.upsert({"bots": userbots}, User.userid == userid)
def user_remove_bot(userid: str, did: str) -> None:
opendb = db_get()
"""Remove bot from user."""
opendb = _db_get()
with opendb:
users = opendb.table("users")
User = Query()
@ -122,17 +130,20 @@ def user_remove_bot(userid: str, did: str) -> None:
def user_get_tokens(userid: str) -> list[Document]:
tokens = db_get().table("tokens")
"""Get all tokens by given user."""
tokens = _db_get().table("tokens")
return tokens.search(Query().userid == userid)
def user_get_token(userid: str, token: str) -> Document | None:
tokens = db_get().table("tokens")
"""Get token by user."""
tokens = _db_get().table("tokens")
return tokens.get((Query().userid == userid) & (Query().token == token))
def user_add_token(userid: str, token: str) -> None:
opendb = db_get()
"""Ass token for given user."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
@ -151,7 +162,8 @@ def user_add_token(userid: str, token: str) -> None:
def user_revoke_all_tokens(userid: str) -> None:
opendb = db_get()
"""Revoke all tokens for given user."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tsearch = tokens.search(Query().userid == userid)
@ -160,7 +172,8 @@ def user_revoke_all_tokens(userid: str) -> None:
def user_revoke_expired_tokens(userid: str) -> None:
opendb = db_get()
"""Revoke expired user tokens."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tsearch = tokens.search(Query().userid == userid)
@ -171,7 +184,8 @@ def user_revoke_expired_tokens(userid: str) -> None:
def user_revoke_token(userid: str, token: str) -> None:
opendb = db_get()
"""Revoke user token."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
@ -180,7 +194,8 @@ def user_revoke_token(userid: str, token: str) -> None:
def user_add_authcode(userid: str, token: str, authcode: str) -> None:
opendb = db_get()
"""Add user authcode."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
@ -192,7 +207,8 @@ def user_add_authcode(userid: str, token: str, authcode: str) -> None:
def user_revoke_authcode(userid: str, token: str) -> None:
opendb = db_get()
"""Revoke user authcode."""
opendb = _db_get()
with opendb:
tokens = opendb.table("tokens")
tmptoken = tokens.get((Query().userid == userid) & (Query().token == token))
@ -204,7 +220,8 @@ def user_revoke_authcode(userid: str, token: str) -> None:
def revoke_expired_oauths() -> None:
opendb = db_get()
"""Revoke expired oauths."""
opendb = _db_get()
with opendb:
table = opendb.table("oauth")
entries = table.all()
@ -217,7 +234,8 @@ def revoke_expired_oauths() -> None:
def user_revoke_expired_oauths(userid: str) -> None:
opendb = db_get()
"""Revoke expired oauths by user."""
opendb = _db_get()
with opendb:
table = opendb.table("oauth")
search = table.search(Query().userid == userid)
@ -229,8 +247,9 @@ def user_revoke_expired_oauths(userid: str) -> None:
def user_add_oauth(userid: str) -> OAuth:
"""Add oauth for user."""
user_revoke_expired_oauths(userid)
opendb = db_get()
opendb = _db_get()
with opendb:
table = opendb.table("oauth")
entry = table.get(Query().userid == userid)
@ -244,19 +263,22 @@ def user_add_oauth(userid: str) -> OAuth:
def token_by_authcode(authcode: str) -> Document | None:
tokens = db_get().table("tokens")
"""Get token by authcode."""
tokens = _db_get().table("tokens")
return tokens.get(Query().authcode == authcode)
def get_disconnected_xmpp_clients() -> list[Document]:
clients = db_get().table("clients")
Client = Query()
return clients.search(Client.xmpp_connection == False)
"""Get disconnected XMPP clients."""
clients = _db_get().table("clients")
client = Query()
return clients.search(client.xmpp_connection == False) # noqa: E712
def check_authcode(uid: str, authcode: str) -> bool:
"""Check authcode."""
_LOGGER.debug(f"Checking for authcode: {authcode}")
tokens = db_get().table("tokens")
tokens = _db_get().table("tokens")
tmpauth = tokens.get(
(Query().authcode == authcode)
& ( # Match authcode
@ -270,9 +292,10 @@ def check_authcode(uid: str, authcode: str) -> bool:
return False
def loginByItToken(authcode: str) -> dict[str, str]:
def login_by_it_token(authcode: str) -> dict[str, str]:
"""Login by token."""
_LOGGER.debug(f"Checking for authcode: {authcode}")
tokens = db_get().table("tokens")
tokens = _db_get().table("tokens")
tmpauth = tokens.get(
Query().authcode
== authcode
@ -288,8 +311,9 @@ def loginByItToken(authcode: str) -> dict[str, str]:
def check_token(uid: str, token: str) -> bool:
"""Check token."""
_LOGGER.debug(f"Checking for token: {token}")
tokens = db_get().table("tokens")
tokens = _db_get().table("tokens")
tmpauth = tokens.get(
(Query().token == token)
& ( # Match token
@ -304,122 +328,137 @@ def check_token(uid: str, token: str) -> bool:
def revoke_expired_tokens() -> None:
tokens = db_get().table("tokens").all()
"""Revoke expired tokens."""
tokens = _db_get().table("tokens").all()
for i in tokens:
if datetime.now() >= datetime.fromisoformat(i["expiration"]):
_LOGGER.debug("Removing token {} due to expiration".format(i["token"]))
db_get().table("tokens").remove(doc_ids=[i.doc_id])
_db_get().table("tokens").remove(doc_ids=[i.doc_id])
def bot_add(sn: str, did: str, devclass: str, resource: str, company: str) -> None:
newbot = VacBotDevice()
newbot.did = did
newbot.name = sn
newbot.vac_bot_device_class = devclass
newbot.resource = resource
newbot.company = company
def bot_add(sn: str, did: str, dev_class: str, resource: str, company: str) -> None:
"""Add bot."""
new_bot = VacBotDevice()
new_bot.did = did
new_bot.name = sn
new_bot.vac_bot_device_class = dev_class
new_bot.resource = resource
new_bot.company = company
bot = bot_get(did)
if not bot: # Not existing bot in database
if (
not devclass == "" or "@" not in sn or "tmp" not in sn
not dev_class == "" or "@" not in sn or "tmp" not in sn
): # try to prevent bad additions to the bot list
_LOGGER.info(f"Adding new bot with SN: {newbot.name} DID: {newbot.did}")
bot_full_upsert(newbot.asdict())
_LOGGER.info(f"Adding new bot with SN: {new_bot.name} DID: {new_bot.did}")
bot_full_upsert(new_bot.asdict())
def bot_remove(did: str) -> None:
bots = db_get().table("bots")
"""Remove bot."""
bots = _db_get().table("bots")
bot = bot_get(did)
if bot:
bots.remove(doc_ids=[bot.doc_id])
def bot_get(did: str) -> Document | None:
bots = db_get().table("bots")
Bot = Query()
return bots.get(Bot.did == did)
"""Get bot."""
bots = _db_get().table("bots")
bot = Query()
return bots.get(bot.did == did)
def bot_full_upsert(vacbot: dict[str, Any]) -> None:
bots = db_get().table("bots")
Bot = Query()
"""Upsert bot."""
bots = _db_get().table("bots")
bot = Query()
if "did" in vacbot:
bots.upsert(vacbot, Bot.did == vacbot["did"])
bots.upsert(vacbot, bot.did == vacbot["did"])
else:
_LOGGER.error(f"No DID in vacbot: {vacbot}")
def bot_set_nick(did: str, nick: str) -> None:
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"nick": nick}, Bot.did == did)
"""Bot set nickname."""
bots = _db_get().table("bots")
bot = Query()
bots.upsert({"nick": nick}, bot.did == did)
def bot_set_mqtt(did: str, mqtt: bool) -> None:
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"mqtt_connection": mqtt}, Bot.did == did)
"""Bot ste MQTT status."""
bots = _db_get().table("bots")
bot = Query()
bots.upsert({"mqtt_connection": mqtt}, bot.did == did)
def bot_set_xmpp(did: str, xmpp: bool) -> None:
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"xmpp_connection": xmpp}, Bot.did == did)
"""Bot set XMPP status."""
bots = _db_get().table("bots")
bot = Query()
bots.upsert({"xmpp_connection": xmpp}, bot.did == did)
def client_add(userid: str, realm: str, resource: str) -> None:
newclient = VacBotClient()
newclient.userid = userid
newclient.realm = realm
newclient.resource = resource
"""Add client."""
new_client = VacBotClient()
new_client.userid = userid
new_client.realm = realm
new_client.resource = resource
client = client_get(resource)
if not client:
_LOGGER.info(f"Adding new client with resource {newclient.resource}")
client_full_upsert(newclient.asdict())
_LOGGER.info(f"Adding new client with resource {new_client.resource}")
_client_full_upsert(new_client.asdict())
def client_remove(resource: str) -> None:
clients = db_get().table("clients")
"""Remove client."""
clients = _db_get().table("clients")
client = client_get(resource)
if client:
clients.remove(doc_ids=[client.doc_id])
def client_get(resource: str) -> Document | None:
clients = db_get().table("clients")
Client = Query()
return clients.get(Client.resource == resource)
"""Get client by resource."""
clients = _db_get().table("clients")
client = Query()
return clients.get(client.resource == resource)
def client_full_upsert(client: dict[str, Any]) -> None:
clients = db_get().table("clients")
Client = Query()
clients.upsert(client, Client.resource == client["resource"])
def _client_full_upsert(client: dict[str, Any]) -> None:
clients = _db_get().table("clients")
client_query = Query()
clients.upsert(client, client_query.resource == client["resource"])
def client_set_mqtt(resource: str, mqtt: bool) -> None:
clients = db_get().table("clients")
Client = Query()
clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource)
"""Client set MQTT status."""
clients = _db_get().table("clients")
client = Query()
clients.upsert({"mqtt_connection": mqtt}, client.resource == resource)
def client_set_xmpp(resource: str, xmpp: bool) -> None:
clients = db_get().table("clients")
Client = Query()
clients.upsert({"xmpp_connection": xmpp}, Client.resource == resource)
"""Client set XMPP status."""
clients = _db_get().table("clients")
client = Query()
clients.upsert({"xmpp_connection": xmpp}, client.resource == resource)
def bot_reset_connectionStatus() -> None:
bots = db_get().table("bots")
def bot_reset_connection_status() -> None:
"""Reset all bot connection status."""
bots = _db_get().table("bots")
for bot in bots:
bot_set_mqtt(bot["did"], False)
bot_set_xmpp(bot["did"], False)
def client_reset_connectionStatus() -> None:
clients = db_get().table("clients")
def client_reset_connection_status() -> None:
"""Reset all client connection status."""
clients = _db_get().table("clients")
for client in clients:
client_set_mqtt(client["resource"], False)
client_set_xmpp(client["resource"], False)

View file

@ -1,11 +1,14 @@
"""Dns module."""
from aiohttp import AsyncResolver
def get_resolver_with_public_nameserver() -> AsyncResolver:
"""Get resolver."""
# requires aiodns
return AsyncResolver(nameservers=["1.1.1.1", "8.8.8.8"])
async def resolve(host: str) -> str:
"""Resolve host."""
hosts = await get_resolver_with_public_nameserver().resolve(host)
return hosts[0]["host"] # type:ignore[no-any-return]

View file

@ -1,3 +1,4 @@
"""Models module."""
import json
import uuid
from datetime import datetime, timedelta
@ -8,6 +9,8 @@ from bumper.util import convert_to_millis
class VacBotDevice:
"""Vacuum device."""
def __init__(
self,
did: str = "",
@ -27,6 +30,7 @@ class VacBotDevice:
self.xmpp_connection = False
def asdict(self) -> dict[str, str | bool]:
"""Convert to dict."""
return {
"class": self.vac_bot_device_class,
"company": self.company,
@ -40,16 +44,21 @@ class VacBotDevice:
class BumperUser:
"""Bumper user."""
def __init__(self, userid: str = ""):
self.userid = userid
self.devices: list[str] = []
self.bots: list[str] = []
def asdict(self) -> dict[str, Any]:
"""Convert to dict."""
return {"userid": self.userid, "devices": self.devices, "bots": self.bots}
class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home
class GlobalVacBotDevice(VacBotDevice):
"""Global vacuum device."""
UILogicId = ""
ota = True
updateInfo = {"changeLog": "", "needUpdate": False}
@ -58,6 +67,8 @@ class GlobalVacBotDevice(VacBotDevice): # EcoVacs Home
class VacBotClient:
"""Vacuum client."""
def __init__(self, userid: str = "", realm: str = "", token: str = ""):
self.userid = userid
self.realm = realm
@ -66,6 +77,7 @@ class VacBotClient:
self.xmpp_connection = False
def asdict(self) -> dict[str, Any]:
"""Convert to dict."""
return {
"userid": self.userid,
"realm": self.realm,
@ -76,6 +88,8 @@ class VacBotClient:
class EcoVacs_Login:
"""Ecovacs login."""
accessToken = ""
country = ""
email = ""
@ -83,16 +97,21 @@ class EcoVacs_Login:
username = ""
def toJSON(self) -> str:
"""Convert to json."""
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False)
class EcoVacsHome_Login(EcoVacs_Login):
"""Ecovacs home login."""
loginName = ""
mobile: str | None = ""
ucUid = ""
class OAuth:
"""Oauth."""
access_token = ""
expire_at = ""
refresh_token = ""
@ -103,6 +122,7 @@ class OAuth:
@classmethod
def create_new(cls, userId: str) -> "OAuth":
"""Create new."""
oauth = OAuth()
oauth.userId = userId
oauth.access_token = uuid.uuid4().hex
@ -113,9 +133,11 @@ class OAuth:
return oauth
def toDB(self) -> dict:
"""Convert for db."""
return self.__dict__
def toResponse(self) -> dict:
"""Convert to response."""
data = self.__dict__
data["expire_at"] = convert_to_millis(
datetime.fromisoformat(self.expire_at).timestamp()

View file

@ -154,6 +154,7 @@ class HelperBot:
self._commands.pop(request_id, None)
def publish(self, topic: str, data: bytes) -> None:
"""Publish message."""
self._client.publish(topic, data)
async def disconnect(self) -> None:

View file

@ -1,6 +1,5 @@
"""Mqtt proxy module."""
import asyncio
import re
import ssl
import typing
from collections.abc import MutableMapping
@ -52,6 +51,7 @@ class ProxyClient:
self._port = port
async def connect(self, username: str, password: str) -> None:
"""Connect."""
try:
await self._client.connect(
f"mqtts://{username}:{password}@{self._host}:{self._port}"
@ -96,12 +96,15 @@ class ProxyClient:
)
async def subscribe(self, topic: str, qos: QOS_0 | QOS_1 | QOS_2 = QOS_0) -> None:
"""Subscribe to topic."""
await self._client.subscribe([(topic, qos)])
async def disconnect(self) -> None:
"""Disconnect."""
await self._client.disconnect()
async def publish(self, topic: str, message: bytes, qos: int | None = None) -> None:
"""Publish message."""
await self._client.publish(topic, message, qos)

View file

@ -253,6 +253,8 @@ class BumperMQTTServerPlugin:
async def on_broker_client_subscribed(
self, client_id: str, topic: str, qos: QOS_0 | QOS_1 | QOS_2
) -> None:
"""Is called when a client subscribes on the broker."""
if bumper.bumper_proxy_mqtt:
# if proxy mode, also subscribe on ecovacs server
if client_id in self._proxy_clients:

View file

@ -1,3 +1,5 @@
"""Util module."""
import logging
import os
import sys
@ -14,6 +16,7 @@ log_to_stdout = os.environ.get("LOG_TO_STDOUT")
def get_logger(name: str, rotate: RotatingFileHandler | None = None) -> logging.Logger:
"""Get logger."""
found_logger = __loggers.get(name)
if found_logger:
return found_logger
@ -53,4 +56,5 @@ def convert_to_millis(seconds: int | float) -> int:
def get_current_time_as_millis() -> int:
"""Get current time in millis."""
return convert_to_millis(datetime.utcnow().timestamp())

View file

@ -11,13 +11,13 @@ from aiohttp.web_response import Response
from bumper import db, use_auth
from bumper.db import (
db_get,
_db_get,
user_add,
user_add_authcode,
user_add_bot,
user_add_device,
user_add_token,
user_by_deviceid,
user_by_device_id,
user_get,
user_get_token,
user_revoke_expired_tokens,
@ -60,7 +60,7 @@ async def login(request: Request) -> Response:
if (
not user_devid == ""
): # Performing basic "auth" using devid, super insecure
user = user_by_deviceid(user_devid)
user = user_by_device_id(user_devid)
if user:
if "checkLogin" in request.path:
_check_token(
@ -132,7 +132,7 @@ async def get_authcode(request: Request) -> Response:
user_devid = request.query["deviceId"] # Ecovacs Home
if user_devid:
user = user_by_deviceid(user_devid)
user = user_by_device_id(user_devid)
if user:
if "accessToken" in request.query:
token = user_get_token(user["userid"], request.query["accessToken"])
@ -224,8 +224,8 @@ def _auth_any(
try:
user_devid = devid
countrycode = country
user = user_by_deviceid(user_devid)
bots = db_get().table("bots").all()
user = user_by_device_id(user_devid)
bots = _db_get().table("bots").all()
login_details: EcoVacs_Login | EcoVacsHome_Login
if user: # Default to user 0

View file

@ -14,7 +14,10 @@ _LOGGER = get_logger("webserver_requests")
class CustomEncoder(json.JSONEncoder):
"""Custom json encoder, which supports set."""
def default(self, obj: Any) -> Any:
"""Convert objects, which are not supported by the default JSONEncoder."""
if isinstance(obj, set):
return list(obj)
return json.JSONEncoder.default(self, obj)
@ -30,6 +33,7 @@ _EXCLUDE_FROM_LOGGING = [
@web.middleware
async def log_all_requests(request: Request, handler: Handler) -> StreamResponse:
"""Middleware to log all requests."""
if (
not request.match_info.route.resource
) or request.match_info.route.resource.canonical in _EXCLUDE_FROM_LOGGING:

View file

@ -13,7 +13,7 @@ from aiohttp.web_routedef import AbstractRouteDef
from amqtt.session import Session
import bumper
from bumper.db import db_get, token_by_authcode, user_add_oauth
from bumper.db import _db_get, token_by_authcode, user_add_oauth
from .. import WebserverPlugin
from .pim import get_product_iot_map
@ -73,7 +73,7 @@ async def _handle_appsvr_app(request: Request) -> Response:
todo = postbody["todo"]
if todo == "GetGlobalDeviceList":
bots = db_get().table("bots").all()
bots = _db_get().table("bots").all()
devices = []
for bot in bots:
if bot["class"] != "":

View file

@ -10,7 +10,13 @@ from aiohttp.web_response import Response
from aiohttp.web_routedef import AbstractRouteDef
from bumper import bumper_announce_ip
from bumper.db import bot_remove, bot_set_nick, check_authcode, db_get, loginByItToken
from bumper.db import (
_db_get,
bot_remove,
bot_set_nick,
check_authcode,
login_by_it_token,
)
from .. import WebserverPlugin
@ -81,7 +87,7 @@ async def _handle_usersapi(request: Request) -> Response:
"userId": postbody["userId"],
}
else: # EcoVacs Home LoginByITToken
login_token = loginByItToken(postbody["token"])
login_token = login_by_it_token(postbody["token"])
if login_token:
body = {
"resource": postbody["resource"],
@ -95,7 +101,7 @@ async def _handle_usersapi(request: Request) -> Response:
elif todo == "GetDeviceList":
body = {
"devices": db_get().table("bots").all(),
"devices": _db_get().table("bots").all(),
"result": "ok",
"todo": "result",
}

View file

@ -8,7 +8,7 @@ from aiohttp.web_request import Request
from aiohttp.web_response import Response
from aiohttp.web_routedef import AbstractRouteDef
from bumper.db import check_token, user_by_deviceid, user_revoke_token
from bumper.db import check_token, user_by_device_id, user_revoke_token
from bumper.web import auth_util
from ... import WebserverPlugin, get_success_response
@ -84,7 +84,7 @@ async def _logout(request: Request) -> Response:
try:
user_device_id = request.match_info.get("devid", None)
if user_device_id:
user = user_by_deviceid(user_device_id)
user = user_by_device_id(user_device_id)
if user:
if check_token(user["userid"], request.query["accessToken"]):
# Deactivate old tokens and authcodes
@ -101,7 +101,7 @@ async def _logout(request: Request) -> Response:
async def _get_user_account_info(request: Request) -> Response:
try:
user_devid = request.match_info.get("devid", "")
user = user_by_deviceid(user_devid)
user = user_by_device_id(user_devid)
if user:
username = f"fusername_{user['userid']}"
return get_success_response(

View file

@ -15,7 +15,7 @@ from aiohttp.web_request import Request
from aiohttp.web_response import Response
import bumper
from bumper.db import bot_get, bot_remove, client_get, client_remove, db_get
from bumper.db import _db_get, bot_get, bot_remove, client_get, client_remove
from bumper.dns import get_resolver_with_public_nameserver
from bumper.util import get_logger
from bumper.web.middlewares import log_all_requests
@ -148,8 +148,8 @@ class WebServer:
async def _handle_base(self, request: Request) -> Response:
try:
bots = db_get().table("bots").all()
clients = db_get().table("clients").all()
bots = _db_get().table("bots").all()
clients = _db_get().table("clients").all()
mq_sessions = []
for (session, _) in bumper.mqtt_server.broker._sessions.values():
mq_sessions.append(

View file

@ -1,3 +1,4 @@
"""XMPP module."""
import asyncio
import base64
import re
@ -23,6 +24,8 @@ boterrorlog = bumper.get_logger("boterror")
class XMPPServer:
"""XMPP server."""
server_id = "ecouser.net"
clients: list["XMPPAsyncClient"] = []
exit_flag = False
@ -35,6 +38,7 @@ class XMPPServer:
self.xmpp_protocol = lambda: XMPPServer_Protocol()
async def start_async_server(self) -> None:
"""Start server."""
try:
xmppserverlog.info(f"Starting XMPP Server at {self._host}:{self._port}")
@ -51,7 +55,7 @@ class XMPPServer:
raise e
def disconnect(self) -> None:
"""Disconnect."""
xmppserverlog.debug("waiting for all clients to disconnect")
for client in self.clients:
client._disconnect()
@ -62,11 +66,14 @@ class XMPPServer:
class XMPPServer_Protocol(asyncio.Protocol):
"""XMPP server protocol."""
client_id = None
exit_flag = False
_client: Optional["XMPPAsyncClient"] = None
def connection_made(self, transport: transports.BaseTransport) -> None:
"""Establish connection."""
if self._client: # Existing client... upgrading to TLS
xmppserverlog.debug(f"Upgraded connection for {self._client.address}")
self._client.transport = transport
@ -78,6 +85,7 @@ class XMPPServer_Protocol(asyncio.Protocol):
xmppserverlog.debug(f"New Connection from {client.address}")
def connection_lost(self, exc: Exception | None) -> None:
"""Lost connection."""
if self._client:
XMPPServer.clients.remove(self._client)
self._client.set_state("DISCONNECT")
@ -90,11 +98,14 @@ class XMPPServer_Protocol(asyncio.Protocol):
)
def data_received(self, data: bytes) -> None:
"""Parse received data."""
if self._client:
self._client.parse_data(data)
class XMPPAsyncClient:
"""XMPP client."""
IDLE = 0
CONNECT = 1
INIT = 2
@ -120,6 +131,7 @@ class XMPPAsyncClient:
xmppserverlog.debug(f"new client with ip {self.address}")
def send(self, command: str) -> None:
"""Send command."""
try:
if self.log_sent_message:
xmppserverlog.debug(
@ -158,6 +170,7 @@ class XMPPAsyncClient:
return tag
def set_state(self, state: str) -> None:
"""Set state."""
try:
new_state = getattr(XMPPAsyncClient, state)
if self.state > new_state:
@ -240,7 +253,7 @@ class XMPPAsyncClient:
and client.state == client.READY
):
ctl_to = xml.get("to")
if not "from" in xml.attrib:
if "from" not in xml.attrib:
xml.attrib["from"] = f"{self.bumper_jid}"
rxmlstring = ET.tostring(xml).decode("utf-8")
# clean up string to remove namespaces added by ET
@ -270,7 +283,7 @@ class XMPPAsyncClient:
else:
pingfrom = self.bumper_jid
if not "from" in xml.attrib:
if "from" not in xml.attrib:
xml.attrib["from"] = f"{pingfrom}"
pingstring = ET.tostring(xml).decode("utf-8")
# clean up string to remove namespaces added by ET
@ -292,6 +305,7 @@ class XMPPAsyncClient:
xmppserverlog.exception(f"{e}")
async def schedule_ping(self, time: int) -> None:
"""Schedule ping."""
if not self.state == 5: # disconnected
pingstring = "<iq from='{}' to='{}' id='s2c1' type='get'><ping xmlns='urn:xmpp:ping'/></iq>".format(
XMPPServer.server_id, self.bumper_jid
@ -303,7 +317,7 @@ class XMPPAsyncClient:
def _handle_result(self, xml: ET.Element, data: str) -> None:
try:
ctl_to = xml.get("to")
if not "from" in xml.attrib:
if "from" not in xml.attrib:
xml.attrib["from"] = f"{self.bumper_jid}"
if "errno" in data:
xmppserverlog.error(f"Error from bot - {data}")
@ -383,7 +397,7 @@ class XMPPAsyncClient:
client.bumper_jid != self.bumper_jid
and client.state == client.READY
):
if not "@" in ctl_to: # No user@, send to all clients?
if "@" not in ctl_to: # No user@, send to all clients?
# TODO: Revisit later, this may be wrong
client.send(rxmlstring)
@ -684,6 +698,7 @@ class XMPPAsyncClient:
self.send(f'<presence to="{self.bumper_jid}"> dummy </presence>')
def parse_data(self, data: bytes) -> None:
"""Parse data."""
if data.decode("utf-8").startswith(
"<?xml"
@ -770,7 +785,7 @@ class XMPPAsyncClient:
elif "not well-formed (invalid token)" in e.msg:
# If a lone </stream:stream> - client is signalling end of session/disconnect
if not "</stream:stream>" in newdata:
if "</stream:stream>" not in newdata:
xmppserverlog.error(f"xml parse error - {newdata} - {e}")
else:
self.send("</stream:stream>") # Close stream
@ -781,7 +796,7 @@ class XMPPAsyncClient:
xmppserverlog.debug(f"Handling connect data - {newdata}")
self._handle_connect(newdata.encode("utf-8"))
else:
if not "</stream:stream>" in newdata:
if "</stream:stream>" not in newdata:
xmppserverlog.error(f"xml parse error - {newdata} - {e}")
else:
self.send("</stream:stream>") # Close stream

View file

@ -11,7 +11,7 @@ def test_db_path():
env = os.environ.copy()
env.pop("DB_FILE")
with mock.patch.dict(os.environ, env, clear=True):
assert db.db_file() == os.path.join(data_dir, "bumper.db")
assert db._db_file() == os.path.join(data_dir, "bumper.db")
def test_user_db():
@ -24,7 +24,7 @@ def test_user_db():
db.user_add_device("testuser", "dev_1234") # Add device to testuser
assert (
db.user_by_deviceid("dev_1234")["userid"] == "testuser"
db.user_by_device_id("dev_1234")["userid"] == "testuser"
) # Test that testuser was found by deviceid
db.user_remove_device("testuser", "dev_1234") # Remove device from testuser