start refactoring confserver

This commit is contained in:
Robert Resch 2022-03-06 00:03:05 +01:00
parent ba3f28165f
commit 4dce3c262c
32 changed files with 627 additions and 840 deletions

View file

@ -6,7 +6,7 @@ import socket
import sys
from typing import Optional
from bumper.confserver import ConfServer
from bumper.confserver import ConfServer, WebServerBinding
from bumper.db import *
from bumper.models import *
from bumper.mqttserver import MQTTHelperBot, MQTTServer
@ -53,11 +53,10 @@ token_validity_seconds = 3600 # 1 hour
oauth_validity_days = 15
db = None
mqtt_server: Optional[MQTTServer] = None
mqtt_helperbot: Optional[MQTTHelperBot] = None
conf_server: Optional[ConfServer] = None
conf_server_2: Optional[ConfServer] = None
xmpp_server: Optional[XMPPServer] = None
mqtt_server: MQTTServer
mqtt_helperbot: MQTTHelperBot
conf_server: ConfServer
xmpp_server: XMPPServer
# Plugins
sys.path.append(os.path.join(bumper_dir, "bumper", "plugins"))
@ -75,9 +74,11 @@ bumperlog = get_logger("bumper")
logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger
mqtt_listen_port = 8883
conf1_listen_port = 443
conf2_listen_port = 8007
xmpp_listen_port = 5223
conf_server_bindings = [
WebServerBinding(bumper_listen, 8443, True),
WebServerBinding(bumper_listen, 8007, False),
]
async def start():
@ -117,9 +118,7 @@ async def start():
global mqtt_helperbot
mqtt_helperbot = MQTTHelperBot(bumper_listen, mqtt_listen_port)
global conf_server
conf_server = ConfServer((bumper_listen, conf1_listen_port), usessl=True)
global conf_server_2
conf_server_2 = ConfServer((bumper_listen, conf2_listen_port), usessl=False)
conf_server = ConfServer(conf_server_bindings)
global xmpp_server
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
@ -138,17 +137,7 @@ async def start():
await asyncio.sleep(0.1)
# Start web servers
conf_server.confserver_app()
asyncio.create_task(
conf_server.start_site(
conf_server.app, address=bumper_listen, port=conf1_listen_port, usessl=True
)
)
asyncio.create_task(
conf_server.start_site(
conf_server.app, address=bumper_listen, port=conf2_listen_port, usessl=False
)
)
await conf_server.start()
# Start maintenance
while not shutting_down:
@ -165,8 +154,7 @@ async def shutdown():
try:
bumperlog.info("Shutting down")
await conf_server.stop_server()
await conf_server_2.stop_server()
await conf_server.shutdown()
if mqtt_server.state == "started":
await mqtt_server.shutdown()
elif mqtt_server.state == "starting":

View file

@ -1,25 +1,36 @@
#!/usr/bin/env python3
"""Web server module."""
import asyncio
import dataclasses
import inspect
import json
import logging
import os
import ssl
from typing import Union
import aiohttp_jinja2
import jinja2
from aiohttp import web
from aiohttp.typedefs import Handler
from aiohttp.web_exceptions import (
HTTPInternalServerError,
HTTPNoContent,
HTTPBadRequest,
)
from aiohttp.web_request import Request
from aiohttp.web_response import Response, StreamResponse
from bumper import plugins
from bumper.models import *
import bumper
from .plugins import ConfServerApp
from .util import get_logger
class aiohttp_filter(logging.Filter):
def filter(self, record):
if (
record.name == "aiohttp.access" and record.levelno == 20
): # Filters aiohttp.access log to switch it from INFO to DEBUG
class _aiohttp_filter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
if record.name == "aiohttp.access" and record.levelno == 20:
# Filters aiohttp.access log to switch it from INFO to DEBUG
record.levelno = 10
record.levelname = "DEBUG"
@ -30,53 +41,64 @@ class aiohttp_filter(logging.Filter):
confserverlog = get_logger("confserver")
logging.getLogger("aiohttp.access").addFilter(
aiohttp_filter()
) # Add logging filter above to aiohttp.access
# Add logging filter above to aiohttp.access
logging.getLogger("aiohttp.access").addFilter(_aiohttp_filter())
@dataclasses.dataclass(frozen=True)
class WebServerBinding:
"""Web server binding."""
host: str
port: int
use_ssl: bool
class ConfServer:
def __init__(self, address, usessl=False):
self.usessl = usessl
self.address = address
self.app = None
self.site = None
self.runner = None
self.runners = []
self.excludelogging = ["base", "remove-bot", "remove-client", "restart-service"]
"""Web server."""
def get_milli_time(self, timetoconvert):
return int(round(timetoconvert * 1000))
_EXCLUDE_FROM_LOGGING = ["base", "remove-bot", "remove-client", "restart-service"]
def confserver_app(self):
self.app = web.Application(
def __init__(self, bindings: Union[list[WebServerBinding], WebServerBinding]):
self._runners: list[web.AppRunner] = []
if isinstance(bindings, WebServerBinding):
bindings = [bindings]
self._bindings = bindings
self._app = web.Application(
middlewares=[
self.log_all_requests,
self._log_all_requests,
],
)
aiohttp_jinja2.setup(
self.app,
self._app,
loader=jinja2.FileSystemLoader(
os.path.join(bumper.bumper_dir, "bumper", "web", "templates")
),
)
self._add_routes()
self._app.freeze() # no modification allowed anymore
self.app.add_routes(
def _add_routes(self) -> None:
self._app.add_routes(
[
web.get("", self.handle_base, name="base"),
web.get("/bot/remove/{did}", self.handle_RemoveBot, name="remove-bot"),
web.get("", self._handle_base, name="base"),
web.get(
"/bot/remove/{did}", self._handle_remove_bot, name="remove-bot"
),
web.get(
"/client/remove/{resource}",
self.handle_RemoveClient,
self._handle_remove_client,
name="remove-client",
),
web.get(
"/restart_{service}",
self.handle_RestartService,
self._handle_restart_service,
name="restart-service",
),
web.post("/lookup.do", self.handle_lookup),
web.post("/newauth.do", self.handle_newauth),
web.post("/lookup.do", self._handle_lookup),
web.post("/newauth.do", self._handle_newauth),
]
)
@ -94,11 +116,17 @@ class ConfServer:
}
# Load plugins
for plug in bumper.discovered_plugins:
if isinstance(
bumper.discovered_plugins[plug].plugin, bumper.plugins.ConfServerApp
):
plugin = bumper.discovered_plugins[plug].plugin
for module in bumper.discovered_plugins.values():
plugins = [
m[1]
for m in inspect.getmembers(module, inspect.isclass)
if m[1].__module__ == module.__name__
]
for plugin_type in plugins:
if not issubclass(plugin_type, ConfServerApp):
continue
plugin = plugin_type()
if plugin.plugin_type == "sub_api": # app or sub_api
if plugin.sub_api in apis:
if plugin.routes:
@ -108,73 +136,51 @@ class ConfServer:
elif plugin.plugin_type == "app":
if plugin.path_prefix and plugin.app:
logging.debug(f"Adding confserver plugin ({plugin.name})")
self.app.add_subapp(plugin.path_prefix, plugin.app)
self._app.add_subapp(plugin.path_prefix, plugin.app)
for api in apis:
self.app.add_subapp(apis[api]["prefix"], apis[api]["app"])
self._app.add_subapp(apis[api]["prefix"], apis[api]["app"])
# for resource in self.app.router.resources():
# print(resource)
async def start_site(self, app, address="localhost", port=8080, usessl=False):
async def start(self) -> None:
"""Start server."""
try:
runner = web.AppRunner(app)
self.runners.append(runner)
confserverlog.info("Starting ConfServer")
for binding in self._bindings:
runner = web.AppRunner(self._app)
self._runners.append(runner)
await runner.setup()
if usessl:
ssl_ctx = None
if binding.use_ssl:
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
site = web.TCPSite(
runner,
host=address,
port=port,
host=binding.host,
port=binding.port,
ssl_context=ssl_ctx,
)
else:
site = web.TCPSite(runner, host=address, port=port)
await site.start()
except Exception as e:
confserverlog.exception(f"{e}")
raise e
async def start_server(self):
async def shutdown(self) -> None:
"""Shutdown server."""
try:
confserverlog.info(
f"Starting ConfServer at {self.address[0]}:{self.address[1]}"
)
self.runner = web.AppRunner(self.app)
await self.runner.setup()
confserverlog.info("Shutting down")
for runner in self._runners:
await runner.shutdown()
if self.usessl:
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
self.site = web.TCPSite(
self.runner,
host=self.address[0],
port=self.address[1],
ssl_context=ssl_ctx,
)
else:
self.site = web.TCPSite(
self.runner, host=self.address[0], port=self.address[1]
)
await self.site.start()
except Exception as e:
confserverlog.exception(f"{e}")
raise e
async def stop_server(self):
try:
await self.runner.shutdown()
self._runners.clear()
await self._app.shutdown()
except Exception as e:
confserverlog.exception(f"{e}")
async def handle_base(self, request):
async def _handle_base(self, request: Request) -> Response:
try:
bots = bumper.db_get().table("bots").all()
clients = bumper.db_get().table("clients").all()
@ -196,20 +202,21 @@ class ConfServer:
"sessions": {
"count": len(mq_sessions),
"clients": mq_sessions,
}
},
},
"xmpp_server": bumper.xmpp_server,
}
resp = aiohttp_jinja2.render_template("home.jinja2", request, context=all)
return resp
return aiohttp_jinja2.render_template("home.jinja2", request, context=all)
except Exception as e:
confserverlog.exception(f"{e}")
@web.middleware
async def log_all_requests(self, request, handler):
raise HTTPInternalServerError
if request._match_info.route.name not in self.excludelogging:
@web.middleware
async def _log_all_requests(
self, request: Request, handler: Handler
) -> StreamResponse:
if request._match_info.route.name not in self._EXCLUDE_FROM_LOGGING:
to_log = {
"request": {
"route_name": f"{request.match_info.route.name}",
@ -230,8 +237,8 @@ class ConfServer:
try:
postbody = json.loads(await request.text())
except Exception as e:
confserverlog.error(f"Request body not json: {e} - {e.doc}")
postbody = e.doc
confserverlog.error(f"Request body not json: {e}")
raise HTTPBadRequest(reason="Body was not json")
else:
postbody = await request.post()
@ -242,12 +249,15 @@ class ConfServer:
if response is None:
confserverlog.warning("Response was null!")
confserverlog.warning(json.dumps(to_log))
return response
raise HTTPNoContent
to_log["response"] = {
"status": f"{response.status}",
}
if not "application/octet-stream" in response.content_type:
if (
"application/octet-stream" not in response.content_type
and isinstance(response, Response)
):
to_log["response"]["body"] = f"{json.loads(response.body)}"
confserverlog.debug(json.dumps(to_log))
@ -262,16 +272,16 @@ class ConfServer:
except Exception as e:
confserverlog.exception(f"{e}")
confserverlog.error(json.dumps(to_log))
return e
raise e
else:
return await handler(request)
async def restart_Helper(self):
async def _restart_helper_bot(self) -> None:
await bumper.mqtt_helperbot.disconnect()
asyncio.create_task(bumper.mqtt_helperbot.start())
async def restart_MQTT(self):
async def _restart_mqtt_server(self) -> None:
loop = asyncio.get_event_loop()
if bumper.mqtt_server.state not in ["stopped", "not_started"]:
@ -287,35 +297,31 @@ class ConfServer:
loop.call_later(1.5, lambda: asyncio.create_task(bumper.mqtt_server.start()))
async def restart_XMPP(self):
bumper.xmpp_server.disconnect()
await bumper.xmpp_server.start_async_server()
async def handle_RestartService(self, request):
async def _handle_restart_service(self, request: Request) -> Response:
try:
service = request.match_info.get("service", "")
if service == "Helperbot":
await self.restart_Helper()
await self._restart_helper_bot()
return web.json_response({"status": "complete"})
elif service == "MQTTServer":
asyncio.create_task(self.restart_MQTT())
if service == "MQTTServer":
asyncio.create_task(self._restart_mqtt_server())
aloop = asyncio.get_event_loop()
aloop.call_later(
5, lambda: asyncio.create_task(self.restart_Helper())
5, lambda: asyncio.create_task(self._restart_helper_bot())
) # In 5 seconds restart Helperbot
return web.json_response({"status": "complete"})
elif service == "XMPPServer":
await self.restart_XMPP()
if service == "XMPPServer":
bumper.xmpp_server.disconnect()
await bumper.xmpp_server.start_async_server()
return web.json_response({"status": "complete"})
else:
return web.json_response({"status": "invalid service"})
return web.json_response({"status": "invalid service"})
except Exception as e:
confserverlog.exception(f"{e}")
pass
raise
async def handle_RemoveBot(self, request):
async def _handle_remove_bot(self, request: Request) -> Response:
try:
did = request.match_info.get("did", "")
bumper.bot_remove(did)
@ -326,9 +332,10 @@ class ConfServer:
except Exception as e:
confserverlog.exception(f"{e}")
pass
async def handle_RemoveClient(self, request):
raise HTTPInternalServerError
async def _handle_remove_client(self, request: Request) -> Response:
try:
resource = request.match_info.get("resource", "")
bumper.client_remove(resource)
@ -339,93 +346,20 @@ class ConfServer:
except Exception as e:
confserverlog.exception(f"{e}")
pass
async def handle_login(self, request):
raise HTTPInternalServerError
async def _handle_lookup(self, request: Request) -> Response:
try:
user_devid = request.match_info.get("devid", "")
countrycode = request.match_info.get("country", "us")
apptype = request.match_info.get("apptype", "")
confserverlog.info(f"client with devid {user_devid} attempting login")
if bumper.use_auth:
if (
not user_devid == ""
): # Performing basic "auth" using devid, super insecure
user = bumper.user_by_deviceid(user_devid)
if "checkLogin" in request.path:
self.check_token(
apptype, countrycode, user, request.query["accessToken"]
)
else:
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(
user["userid"]
)
login_details.mobile = None
else:
login_details = EcoVacs_Login()
# Deactivate old tokens and authcodes
bumper.user_revoke_expired_tokens(user["userid"])
login_details.accessToken = self.generate_token(user)
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": API_ERRORS[RETURN_API_SUCCESS],
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
body = {
"code": bumper.ERR_USER_NOT_ACTIVATED,
"data": None,
"msg": "当前密码错误",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
else:
return web.json_response(
self._auth_any(user_devid, apptype, countrycode, request)
)
except Exception as e:
confserverlog.exception(f"{e}")
async def handle_lookup(self, request):
try:
body = {}
postbody = {}
if request.content_type == "application/x-www-form-urlencoded":
postbody = await request.post()
body = await request.post()
else:
postbody = json.loads(await request.text())
body = json.loads(await request.text())
confserverlog.debug(postbody)
confserverlog.debug(body)
todo = postbody["todo"]
if todo == "FindBest":
service = postbody["service"]
if body["todo"] == "FindBest":
service = body["service"]
if service == "EcoMsgNew":
srvip = bumper.bumper_announce_ip
srvport = 5223
@ -434,13 +368,10 @@ class ConfServer:
srvip, srvport
)
)
msgserver = {"ip": srvip, "port": srvport, "result": "ok"}
msgserver = json.dumps(msgserver)
msgserver = msgserver.replace(
" ", ""
) # bot seems to be very picky about having no spaces, only way was with text
return web.json_response(text=msgserver)
server = json.dumps({"ip": srvip, "port": srvport, "result": "ok"})
# bot seems to be very picky about having no spaces, only way was with text
server = server.replace(" ", "")
return web.json_response(text=server)
elif service == "EcoUpdate":
srvip = "47.88.66.164" # EcoVacs Server
@ -450,14 +381,18 @@ class ConfServer:
srvip, srvport
)
)
body = {"result": "ok", "ip": srvip, "port": srvport}
return web.json_response(
{"result": "ok", "ip": srvip, "port": srvport}
)
return web.json_response(body)
return web.json_response({})
except Exception as e:
confserverlog.exception(f"{e}")
async def handle_newauth(self, request):
raise HTTPInternalServerError
async def _handle_newauth(self, request: Request) -> Response:
# Bumper is only returning the submitted token. No reason yet to create another new token
try:
if request.content_type == "application/x-www-form-urlencoded":
@ -474,401 +409,4 @@ class ConfServer:
except Exception as e:
confserverlog.exception(f"{e}")
async def disconnect(self):
try:
confserverlog.info("shutting down")
await self.app.shutdown()
except Exception as e:
confserverlog.exception(f"{e}")
class ConfServer_GeneralFunctions:
def __init__(self):
pass
def get_milli_time(self, timetoconvert):
return int(round(timetoconvert * 1000))
class ConfServer_AuthHandler:
def __init__(self):
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
pass
def generate_token(self, user):
try:
tmpaccesstoken = uuid.uuid4().hex
bumper.user_add_token(user["userid"], tmpaccesstoken)
return tmpaccesstoken
except Exception as e:
confserverlog.exception(f"{e}")
def generate_authcode(self, user, countrycode, token):
try:
tmpauthcode = f"{countrycode}_{uuid.uuid4().hex}"
bumper.user_add_authcode(user["userid"], token, tmpauthcode)
return tmpauthcode
except Exception as e:
confserverlog.exception(f"{e}")
async def login(self, request):
try:
user_devid = request.match_info.get("devid", "")
countrycode = request.match_info.get("country", "us")
apptype = request.match_info.get("apptype", "")
confserverlog.info(f"client with devid {user_devid} attempting login")
if bumper.use_auth:
if (
not user_devid == ""
): # Performing basic "auth" using devid, super insecure
user = bumper.user_by_deviceid(user_devid)
if "checkLogin" in request.path:
self.check_token(
apptype, countrycode, user, request.query["accessToken"]
)
else:
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(
user["userid"]
)
login_details.mobile = None
else:
login_details = EcoVacs_Login()
# Deactivate old tokens and authcodes
bumper.user_revoke_expired_tokens(user["userid"])
login_details.accessToken = self.generate_token(user)
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(
user["userid"]
)
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": API_ERRORS[RETURN_API_SUCCESS],
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": self.get_milli_time(
datetime.utcnow().timestamp()
),
}
return web.json_response(body)
body = {
"code": bumper.ERR_USER_NOT_ACTIVATED,
"data": None,
"msg": "当前密码错误",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
else:
return web.json_response(
self._auth_any(user_devid, apptype, countrycode, request)
)
except Exception as e:
confserverlog.exception(f"{e}")
async def get_AuthCode(self, request):
try:
apptype = request.match_info.get("apptype", "")
user_devid = request.match_info.get("devid", "") # Ecovacs
if user_devid == "":
user_devid = request.query["deviceId"] # Ecovacs Home
if not user_devid == "":
user = bumper.user_by_deviceid(user_devid)
token = ""
if user:
if "accessToken" in request.query:
token = bumper.user_get_token(
user["userid"], request.query["accessToken"]
)
if token:
authcode = ""
if not "authcode" in token:
authcode = self.generate_authcode(
user,
request.match_info.get("country", "us"),
request.query["accessToken"],
)
else:
authcode = token["authcode"]
if "global" in apptype:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"authCode": authcode,
"ecovacsUid": request.query["uid"],
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(
datetime.utcnow().timestamp()
),
}
else:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"authCode": authcode,
"ecovacsUid": request.query["uid"],
},
"msg": "操作成功",
"time": self.get_milli_time(
datetime.utcnow().timestamp()
),
}
return web.json_response(body)
body = {
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
confserverlog.exception(f"{e}")
def check_token(self, apptype, countrycode, user, token):
try:
if bumper.check_token(user["userid"], token):
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(user["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = token
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
else:
body = {
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
confserverlog.exception(f"{e}")
def _auth_any(self, devid, apptype, country, request):
try:
user_devid = devid
countrycode = country
user = bumper.user_by_deviceid(user_devid)
bots = bumper.db_get().table("bots").all()
if user: # Default to user 0
tmpuser = user
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
login_details.loginName = "fusername_{}".format(
tmpuser["userid"]
)
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = self.generate_token(tmpuser)
login_details.uid = "fuid_{}".format(tmpuser["userid"])
login_details.username = "fusername_{}".format(tmpuser["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
bumper.user_add_device(tmpuser["userid"], user_devid)
else:
bumper.user_add("tmpuser") # Add a new user
tmpuser = bumper.user_get("tmpuser")
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
login_details.loginName = "fusername_{}".format(
tmpuser["userid"]
)
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = self.generate_token(tmpuser)
login_details.uid = "fuid_{}".format(tmpuser["userid"])
login_details.username = "fusername_{}".format(tmpuser["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
bumper.user_add_device(tmpuser["userid"], user_devid)
for bot in bots: # Add all bots to the user
if "did" in bot:
bumper.user_add_bot(tmpuser["userid"], bot["did"])
else:
confserverlog.error(f"No DID for bot: {bot}")
if (
"checkLogin" in request.path
): # If request was to check a token do so
checkToken = self.check_token(
apptype, countrycode, tmpuser, request.query["accessToken"]
)
isGood = json.loads(checkToken.text)
if isGood["code"] == "0000":
return isGood
# Deactivate old tokens and authcodes
bumper.user_revoke_expired_tokens(tmpuser["userid"])
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return body
except Exception as e:
confserverlog.exception(f"{e}")
def getUserAccountInfo(self, request):
try:
user_devid = request.match_info.get("devid", "")
countrycode = request.match_info.get("country", "us")
apptype = request.match_info.get("apptype", "")
user = bumper.user_by_deviceid(user_devid)
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(user["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"email": login_details.email,
"hasMobile": "N",
"hasPassword": "Y",
"uid": login_details.uid,
"userName": login_details.username,
"obfuscatedMobile": None,
"mobile": None,
"loginName": login_details.loginName,
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
# Example body
# {
# "code": "0000",
# "data": {
# "email": "user@gmail.com",
# "hasMobile": "N",
# "hasPassword": "Y",
# "headIco": "",
# "loginName": "user@gmail.com",
# "mobile": null,
# "mobileAreaNo": null,
# "nickname": "",
# "obfuscatedMobile": null,
# "thirdLoginInfoList": [
# {
# "accountType": "WeChat",
# "hasBind": "N"
# }
# ],
# "uid": "20180719212155_*****",
# "userName": "EAY*****"
# },
# "msg": "操作成功",
# "success": true,
# "time": 1578203898343
# }
return web.json_response(body)
except Exception as e:
confserverlog.exception(f"{e}")
async def logout(self, request):
try:
user_devid = request.match_info.get("devid", "")
if not user_devid == "":
user = bumper.user_by_deviceid(user_devid)
if user:
if bumper.check_token(
user["userid"], request.query["accessToken"]
):
# Deactivate old tokens and authcodes
bumper.user_revoke_token(
user["userid"], request.query["accessToken"]
)
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
confserverlog.exception(f"{e}")
raise HTTPInternalServerError

View file

@ -31,7 +31,7 @@ def os_db_path(): # createdir=True):
return os.path.join(bumper.data_dir, "bumper.db")
def db_get():
def db_get() -> TinyDB:
# Will create the database if it doesn't exist
db = TinyDB(db_file())
@ -340,7 +340,7 @@ def bot_add(sn: str, did: str, devclass: str, resource: str, company: str) -> No
bot_full_upsert(newbot.asdict())
def bot_remove(did):
def bot_remove(did: str):
bots = db_get().table("bots")
bot = bot_get(did)
if bot:
@ -392,7 +392,7 @@ def client_add(userid: str, realm: str, resource: str) -> None:
client_full_upsert(newclient.asdict())
def client_remove(resource):
def client_remove(resource: str):
clients = db_get().table("clients")
client = client_get(resource)
if client:

View file

@ -8,6 +8,7 @@ from typing import Any
from amqtt.session import Session
import bumper
from bumper.util import convert_to_millis
class VacBotDevice:
@ -116,9 +117,7 @@ class OAuth:
def toResponse(self):
data = self.__dict__
data[
"expire_at"
] = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time(
data["expire_at"] = convert_to_millis(
datetime.fromisoformat(self.expire_at).timestamp()
)
return data

View file

@ -1,13 +1,14 @@
#!/usr/bin/env python3
import json
import logging
from aiohttp import web
from bumper import plugins
from bumper.models import *
import bumper
from bumper.plugins import ConfServerApp
class portal_api_appsvr(plugins.ConfServerApp):
class portal_api_appsvr(ConfServerApp):
def __init__(self):
self.name = "portal_api_appsvr"
self.plugin_type = "sub_api"
@ -34,10 +35,6 @@ class portal_api_appsvr(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_appsvr_app(self, request):
if not request.method == "GET": # Skip GET for now
try:
@ -211,6 +208,3 @@ class portal_api_appsvr(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = portal_api_appsvr()

View file

@ -27,10 +27,6 @@ class portal_api_dim(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_dim_devmanager(self, request): # Used in EcoVacs Home App
try:
json_body = json.loads(await request.text())
@ -76,6 +72,3 @@ class portal_api_dim(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = portal_api_dim()

View file

@ -22,10 +22,6 @@ class portal_api_ecms(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_ad_res(self, request):
try:
body = {"code": 0, "data": [], "message": "success", "success": True}
@ -34,6 +30,3 @@ class portal_api_ecms(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = portal_api_ecms()

View file

@ -27,10 +27,6 @@ class portal_api_iot(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_devmanager_botcommand(self, request):
try:
json_body = json.loads(await request.text())
@ -81,6 +77,3 @@ class portal_api_iot(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = portal_api_iot()

View file

@ -20,10 +20,6 @@ class portal_api_lg(plugins.ConfServerApp):
web.route("*", "/lg/log.do", self.handle_lg_log, name="portal_api_lg_log"),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_lg_log(self, request): # EcoVacs Home
randomid = "".join(random.sample(string.ascii_letters, 6))
@ -101,6 +97,3 @@ class portal_api_lg(plugins.ConfServerApp):
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
return web.json_response(body)
plugin = portal_api_lg()

View file

@ -37,10 +37,6 @@ class portal_api_neng(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_neng_hasUnreadMessage(self, request): # EcoVacs Home
try:
body = {"code": 0, "data": {"hasUnRead": True}}
@ -107,6 +103,3 @@ class portal_api_neng(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = portal_api_neng()

View file

@ -53,10 +53,6 @@ class portal_api_pim(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_getProductIotMap(self, request):
try:
body = {
@ -130,8 +126,6 @@ class portal_api_pim(plugins.ConfServerApp):
logging.exception(f"{e}")
plugin = portal_api_pim()
confignetAllResponse = {
"code": 0,
"data": [

View file

@ -19,10 +19,6 @@ class api_rapp(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_map_get(self, request):
try:
body = {
@ -35,6 +31,3 @@ class api_rapp(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = api_rapp()

View file

@ -25,10 +25,6 @@ class portal_api_users(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_usersapi(self, request):
if not request.method == "GET": # Skip GET for now
try:
@ -120,6 +116,3 @@ class portal_api_users(plugins.ConfServerApp):
# Return fail for GET
body = {"result": "fail", "todo": "result"}
return web.json_response(body)
plugin = portal_api_users()

View file

@ -26,10 +26,6 @@ class upload_global(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_upload_global_file(self, request):
try:
fileID = request.match_info.get("id", "")
@ -42,6 +38,3 @@ class upload_global(plugins.ConfServerApp):
except Exception as e:
logging.exception(f"{e}")
plugin = upload_global()

View file

@ -8,6 +8,7 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.rest import auth_util
class v1_global_auth(plugins.ConfServerApp):
@ -16,19 +17,11 @@ class v1_global_auth(plugins.ConfServerApp):
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route(
"*",
"/global/auth/getAuthCode",
authhandler.get_AuthCode,
auth_util.get_authcode,
name="v1_global_auth_getAuthCode",
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
plugin = v1_global_auth()

View file

@ -8,6 +8,7 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.util import get_current_time_as_millis
class v1_private_ad(plugins.ConfServerApp):
@ -31,10 +32,6 @@ class v1_private_ad(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_getAdByPositionType(self, request): # EcoVacs Home
try:
body = {
@ -42,7 +39,7 @@ class v1_private_ad(plugins.ConfServerApp):
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -57,13 +54,10 @@ class v1_private_ad(plugins.ConfServerApp):
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_ad()

View file

@ -8,6 +8,7 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.util import get_current_time_as_millis
class v1_private_campaign(plugins.ConfServerApp):
@ -25,13 +26,9 @@ class v1_private_campaign(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_homePageAlert(self, request):
try:
nextAlert = self.get_milli_time(
nextAlert = convert_to_millis(
(datetime.now() + timedelta(hours=12)).timestamp()
)
@ -43,16 +40,13 @@ class v1_private_campaign(plugins.ConfServerApp):
"hasCampaign": "N",
"imageUrl": None,
"nextAlertTime": nextAlert,
"serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
"serverTime": get_current_time_as_millis(),
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_campaign()

View file

@ -5,6 +5,7 @@ from aiohttp import web
from bumper import plugins
from bumper.models import *
from bumper.util import get_current_time_as_millis
class v1_private_common(plugins.ConfServerApp):
@ -64,10 +65,6 @@ class v1_private_common(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_checkVersion(self, request):
try:
body = {
@ -82,7 +79,7 @@ class v1_private_common(plugins.ConfServerApp):
"v": None,
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -107,7 +104,7 @@ class v1_private_common(plugins.ConfServerApp):
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -122,7 +119,7 @@ class v1_private_common(plugins.ConfServerApp):
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -144,7 +141,7 @@ class v1_private_common(plugins.ConfServerApp):
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -163,7 +160,7 @@ class v1_private_common(plugins.ConfServerApp):
"data": data,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -178,7 +175,7 @@ class v1_private_common(plugins.ConfServerApp):
"data": AREA_LIST,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -210,7 +207,7 @@ class v1_private_common(plugins.ConfServerApp):
],
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -220,7 +217,7 @@ class v1_private_common(plugins.ConfServerApp):
async def handle_getTimestamp(self, request): # EcoVacs Home
try:
time = self.get_milli_time(datetime.utcnow().timestamp())
time = get_current_time_as_millis()
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {"timestamp": time},
@ -235,8 +232,6 @@ class v1_private_common(plugins.ConfServerApp):
logging.exception(f"{e}")
plugin = v1_private_common()
AREA_LIST = {
"currentVersion": 231,
"areaList": [

View file

@ -8,6 +8,7 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.util import get_current_time_as_millis
class v1_private_message(plugins.ConfServerApp):
@ -31,10 +32,6 @@ class v1_private_message(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_hasUnreadMessage(self, request): # EcoVacs Home
try:
body = {
@ -42,7 +39,7 @@ class v1_private_message(plugins.ConfServerApp):
"data": "N",
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -57,13 +54,10 @@ class v1_private_message(plugins.ConfServerApp):
"data": {"hasNextPage": 0, "items": []},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_message()

View file

@ -8,6 +8,7 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.util import get_current_time_as_millis
class v1_private_shop(plugins.ConfServerApp):
@ -25,10 +26,6 @@ class v1_private_shop(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_getCnWapShopConfig(self, request): # EcoVacs Home
try:
body = {
@ -41,13 +38,10 @@ class v1_private_shop(plugins.ConfServerApp):
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_shop()

View file

@ -8,6 +8,8 @@ from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.rest import auth_util
from bumper.util import get_current_time_as_millis
class v1_private_user(plugins.ConfServerApp):
@ -17,30 +19,29 @@ class v1_private_user(plugins.ConfServerApp):
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login",
authhandler.login,
auth_util.login,
name="v1_user_login",
),
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin",
authhandler.login,
auth_util.login,
name="v1_user_checkLogin",
),
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode",
authhandler.get_AuthCode,
auth_util.get_authcode,
name="v1_user_getAuthCode",
),
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout",
authhandler.logout,
auth_util.logout,
name="v1_user_logout",
),
web.route(
@ -58,7 +59,7 @@ class v1_private_user(plugins.ConfServerApp):
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getUserAccountInfo",
authhandler.getUserAccountInfo,
auth_util.get_user_account_info,
name="v1_user_getUserAccountInfo",
),
web.route(
@ -91,10 +92,6 @@ class v1_private_user(plugins.ConfServerApp):
# /registerByEmail
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_checkAgreement(self, request):
try:
apptype = request.match_info.get("apptype", "")
@ -119,14 +116,14 @@ class v1_private_user(plugins.ConfServerApp):
],
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
else:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": [],
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -195,7 +192,7 @@ class v1_private_user(plugins.ConfServerApp):
],
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -210,7 +207,7 @@ class v1_private_user(plugins.ConfServerApp):
"data": {"isNeedReLogin": "N"},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
@ -225,13 +222,10 @@ class v1_private_user(plugins.ConfServerApp):
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_user()

View file

@ -1,11 +1,11 @@
#!/usr/bin/env python3
import logging
from datetime import datetime
from aiohttp import web
import bumper
from bumper import plugins
from bumper.util import get_current_time_as_millis
class v1_private_userSetting(plugins.ConfServerApp):
@ -24,10 +24,6 @@ class v1_private_userSetting(plugins.ConfServerApp):
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
async def handle_getSuggestionSetting(self, request):
try:
@ -54,13 +50,10 @@ class v1_private_userSetting(plugins.ConfServerApp):
],
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
logging.exception(f"{e}")
plugin = v1_private_userSetting()

View file

@ -1,13 +1,8 @@
#!/usr/bin/env python3
import asyncio
import logging
from datetime import datetime, timedelta
from aiohttp import web
import bumper
from bumper import plugins
from bumper.models import *
from bumper.rest import auth_util
class v2_private_user(plugins.ConfServerApp):
@ -17,19 +12,11 @@ class v2_private_user(plugins.ConfServerApp):
self.plugin_type = "sub_api"
self.sub_api = "api_v2"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route(
"*",
"/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin",
authhandler.login,
auth_util.login,
name="v2_user_checkLogin",
),
]
self.get_milli_time = (
bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
)
plugin = v2_private_user()

0
bumper/rest/__init__.py Normal file
View file

384
bumper/rest/auth_util.py Normal file
View file

@ -0,0 +1,384 @@
"""Auth util module."""
import json
import uuid
from aiohttp import web
import bumper
from bumper import (
get_logger,
EcoVacsHome_Login,
EcoVacs_Login,
API_ERRORS,
RETURN_API_SUCCESS,
)
from bumper.util import get_current_time_as_millis
_logger = get_logger("confserver")
def generate_token(user):
try:
tmpaccesstoken = uuid.uuid4().hex
bumper.user_add_token(user["userid"], tmpaccesstoken)
return tmpaccesstoken
except Exception as e:
_logger.exception(f"{e}")
def generate_authcode(user, countrycode, token):
try:
tmpauthcode = f"{countrycode}_{uuid.uuid4().hex}"
bumper.user_add_authcode(user["userid"], token, tmpauthcode)
return tmpauthcode
except Exception as e:
_logger.exception(f"{e}")
async def login(request):
try:
user_devid = request.match_info.get("devid", "")
countrycode = request.match_info.get("country", "us")
apptype = request.match_info.get("apptype", "")
_logger.info(f"client with devid {user_devid} attempting login")
if bumper.use_auth:
if (
not user_devid == ""
): # Performing basic "auth" using devid, super insecure
user = bumper.user_by_deviceid(user_devid)
if "checkLogin" in request.path:
check_token(
apptype, countrycode, user, request.query["accessToken"]
)
else:
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(user["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
# Deactivate old tokens and authcodes
bumper.user_revoke_expired_tokens(user["userid"])
login_details.accessToken = generate_token(user)
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": API_ERRORS[RETURN_API_SUCCESS],
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
body = {
"code": bumper.ERR_USER_NOT_ACTIVATED,
"data": None,
"msg": "当前密码错误",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
else:
return web.json_response(
_auth_any(user_devid, apptype, countrycode, request)
)
except Exception as e:
_logger.exception(f"{e}")
async def get_authcode(request):
try:
apptype = request.match_info.get("apptype", "")
user_devid = request.match_info.get("devid", "") # Ecovacs
if user_devid == "":
user_devid = request.query["deviceId"] # Ecovacs Home
if not user_devid == "":
user = bumper.user_by_deviceid(user_devid)
token = ""
if user:
if "accessToken" in request.query:
token = bumper.user_get_token(
user["userid"], request.query["accessToken"]
)
if token:
authcode = ""
if not "authcode" in token:
authcode = generate_authcode(
user,
request.match_info.get("country", "us"),
request.query["accessToken"],
)
else:
authcode = token["authcode"]
if "global" in apptype:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"authCode": authcode,
"ecovacsUid": request.query["uid"],
},
"msg": "操作成功",
"success": True,
"time": get_current_time_as_millis(),
}
else:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"authCode": authcode,
"ecovacsUid": request.query["uid"],
},
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
body = {
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
_logger.exception(f"{e}")
def check_token(apptype, countrycode, user, token):
try:
if bumper.check_token(user["userid"], token):
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(user["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = token
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
else:
body = {
"code": bumper.ERR_TOKEN_INVALID,
"data": None,
"msg": "当前密码错误",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
_logger.exception(f"{e}")
def _auth_any(devid, apptype, country, request):
try:
user_devid = devid
countrycode = country
user = bumper.user_by_deviceid(user_devid)
bots = bumper.db_get().table("bots").all()
if user: # Default to user 0
tmpuser = user
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
login_details.loginName = "fusername_{}".format(tmpuser["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = generate_token(tmpuser)
login_details.uid = "fuid_{}".format(tmpuser["userid"])
login_details.username = "fusername_{}".format(tmpuser["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
bumper.user_add_device(tmpuser["userid"], user_devid)
else:
bumper.user_add("tmpuser") # Add a new user
tmpuser = bumper.user_get("tmpuser")
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(tmpuser["userid"])
login_details.loginName = "fusername_{}".format(tmpuser["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.accessToken = generate_token(tmpuser)
login_details.uid = "fuid_{}".format(tmpuser["userid"])
login_details.username = "fusername_{}".format(tmpuser["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
bumper.user_add_device(tmpuser["userid"], user_devid)
for bot in bots: # Add all bots to the user
if "did" in bot:
bumper.user_add_bot(tmpuser["userid"], bot["did"])
else:
_logger.error(f"No DID for bot: {bot}")
if "checkLogin" in request.path: # If request was to check a token do so
checkToken = check_token(
apptype, countrycode, tmpuser, request.query["accessToken"]
)
isGood = json.loads(checkToken.text)
if isGood["code"] == "0000":
return isGood
# Deactivate old tokens and authcodes
bumper.user_revoke_expired_tokens(tmpuser["userid"])
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": json.loads(login_details.toJSON()),
# {
# "accessToken": self.generate_token(tmpuser), # Generate a token
# "country": countrycode,
# "email": "null@null.com",
# "uid": "fuid_{}".format(tmpuser["userid"]),
# "username": "fusername_{}".format(tmpuser["userid"]),
# },
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
return body
except Exception as e:
_logger.exception(f"{e}")
def get_user_account_info(request):
try:
user_devid = request.match_info.get("devid", "")
countrycode = request.match_info.get("country", "us")
apptype = request.match_info.get("apptype", "")
user = bumper.user_by_deviceid(user_devid)
if "global_" in apptype: # EcoVacs Home
login_details = EcoVacsHome_Login()
login_details.ucUid = "fuid_{}".format(user["userid"])
login_details.loginName = "fusername_{}".format(user["userid"])
login_details.mobile = None
else:
login_details = EcoVacs_Login()
login_details.uid = "fuid_{}".format(user["userid"])
login_details.username = "fusername_{}".format(user["userid"])
login_details.country = countrycode
login_details.email = "null@null.com"
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"email": login_details.email,
"hasMobile": "N",
"hasPassword": "Y",
"uid": login_details.uid,
"userName": login_details.username,
"obfuscatedMobile": None,
"mobile": None,
"loginName": login_details.loginName,
},
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
# Example body
# {
# "code": "0000",
# "data": {
# "email": "user@gmail.com",
# "hasMobile": "N",
# "hasPassword": "Y",
# "headIco": "",
# "loginName": "user@gmail.com",
# "mobile": null,
# "mobileAreaNo": null,
# "nickname": "",
# "obfuscatedMobile": null,
# "thirdLoginInfoList": [
# {
# "accountType": "WeChat",
# "hasBind": "N"
# }
# ],
# "uid": "20180719212155_*****",
# "userName": "EAY*****"
# },
# "msg": "操作成功",
# "success": true,
# "time": 1578203898343
# }
return web.json_response(body)
except Exception as e:
_logger.exception(f"{e}")
async def logout(request):
try:
user_devid = request.match_info.get("devid", "")
if not user_devid == "":
user = bumper.user_by_deviceid(user_devid)
if user:
if bumper.check_token(user["userid"], request.query["accessToken"]):
# Deactivate old tokens and authcodes
bumper.user_revoke_token(
user["userid"], request.query["accessToken"]
)
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"time": get_current_time_as_millis(),
}
return web.json_response(body)
except Exception as e:
_logger.exception(f"{e}")

View file

@ -1,8 +1,9 @@
import logging
import os
import sys
from datetime import datetime
from logging.handlers import RotatingFileHandler
from typing import MutableMapping
from typing import MutableMapping, Union
logformat = logging.Formatter(
"[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s"
@ -43,3 +44,12 @@ def get_logger(name: str, rotate: RotatingFileHandler = None) -> logging.Logger:
get_logger("amqtt.client", rotate)
return logger
def convert_to_millis(seconds: Union[int, float]) -> int:
"""Convert seconds to milliseconds."""
return int(round(seconds * 1000))
def get_current_time_as_millis() -> int:
return convert_to_millis(datetime.utcnow().timestamp())

View file

@ -26,7 +26,7 @@ class XMPPServer:
self.address = address
self.xmpp_protocol = lambda: XMPPServer_Protocol()
async def start_async_server(self):
async def start_async_server(self) -> None:
try:
xmppserverlog.info(
f"Starting XMPP Server at {self.address[0]}:{self.address[1]}"
@ -44,7 +44,7 @@ class XMPPServer:
xmppserverlog.exception(f"{e}")
raise e
def disconnect(self):
def disconnect(self) -> None:
xmppserverlog.debug("waiting for all clients to disconnect")
for client in self.clients:

View file

@ -1,2 +1,3 @@
HOST = "127.0.0.1"
MQTT_PORT = 8883
CONF_SERVER_PORT = 11111

View file

@ -4,7 +4,8 @@ import pytest
from amqtt.client import MQTTClient
import bumper
from tests import HOST, MQTT_PORT
from bumper import WebServerBinding
from tests import HOST, MQTT_PORT, CONF_SERVER_PORT
@pytest.fixture
@ -37,10 +38,8 @@ async def mqtt_client():
@pytest.fixture
async def conf_server_client(aiohttp_client):
confserver = bumper.ConfServer("127.0.0.1:11111", False)
confserver.confserver_app()
client = await aiohttp_client(confserver.app)
confserver = bumper.ConfServer(WebServerBinding(HOST, CONF_SERVER_PORT, False))
client = await aiohttp_client(confserver._app)
yield client

View file

@ -9,11 +9,12 @@ from aiohttp import web
from testfixtures import LogCapture
import bumper
from tests import HOST, MQTT_PORT
from bumper import WebServerBinding
from tests import HOST, MQTT_PORT, CONF_SERVER_PORT
def create_confserver():
return bumper.ConfServer("127.0.0.1:11111", False)
return bumper.ConfServer(WebServerBinding(HOST, CONF_SERVER_PORT, False))
def async_return(result):
@ -28,27 +29,13 @@ def remove_existing_db():
async def test_confserver_ssl():
conf_server = bumper.ConfServer((HOST, 11111), usessl=True)
conf_server.confserver_app()
await conf_server.start_server()
conf_server = bumper.ConfServer(WebServerBinding(HOST, CONF_SERVER_PORT, True))
await conf_server.start()
async def test_confserver_no_ssl():
conf_server = bumper.ConfServer((HOST, 11112), usessl=False)
conf_server.confserver_app()
await conf_server.start_server()
def test_get_milli_time():
cserv = create_confserver()
assert (
cserv.get_milli_time(
datetime.datetime(
2018, 1, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
).timestamp()
)
== 1514768400000
)
conf_server = bumper.ConfServer(WebServerBinding(HOST, 11112, False))
await conf_server.start()
@pytest.mark.usefixtures("mqtt_server")

View file

@ -26,15 +26,12 @@ async def test_start_stop():
b = bumper
b.db = "tests/tmp.db" # Set db location for testing
b.conf1_listen_address = "127.0.0.1"
b.conf1_listen_port = 444
asyncio.create_task(b.start())
await asyncio.sleep(0.1)
l.check_present(("bumper", "INFO", "Starting Bumper"))
l.clear()
asyncio.create_task(b.shutdown())
await asyncio.sleep(0.1)
await b.shutdown()
l.check_present(
("bumper", "INFO", "Shutting down"), ("bumper", "INFO", "Shutdown complete")
)

14
tests/test_util.py Normal file
View file

@ -0,0 +1,14 @@
import datetime
from bumper.util import convert_to_millis
def test_get_milli_time():
assert (
convert_to_millis(
datetime.datetime(
2018, 1, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
).timestamp()
)
== 1514768400000
)