diff --git a/bumper.py b/bumper.py
index a6ba2ac..11aba3b 100644
--- a/bumper.py
+++ b/bumper.py
@@ -30,12 +30,21 @@ def main():
xmpp_address = (listen_host, 5223)
mqtt_address = (listen_host, 8883)
- xmpp_server = bumper.XMPPServer(xmpp_address)
- mqtt_server = bumper.MQTTServer(mqtt_address,bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var,remove_clients=bumper.bumper_removeclients_var)
+ xmpp_server = bumper.XMPPServer(xmpp_address, bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var)
+ mqtt_server = bumper.MQTTServer(mqtt_address, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var)
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var)
- conf_server = bumper.ConfServer(conf_address_443, usessl=True, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var, remove_clients=bumper.bumper_removeclients_var,helperbot=mqtt_helperbot)
- conf_server_2 = bumper.ConfServer(conf_address_8007, usessl=False, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var, helperbot=mqtt_helperbot,remove_clients=bumper.bumper_removeclients_var)
+ conf_server = bumper.ConfServer(conf_address_443, usessl=True,bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var,helperbot=mqtt_helperbot)
+ conf_server_2 = bumper.ConfServer(conf_address_8007, usessl=False,bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var, helperbot=mqtt_helperbot)
+ #add user
+ users = bumper.bumper_users_var.get()
+ user1 = bumper.BumperUser('user1')
+ user1.add_device('user_phone')
+ user1.add_bot('bot_id')
+ users.append(user1)
+ bumper.bumper_users_var.set(users)
+
+
# start xmpp server on port 5223 (sync)
xmpp_server.run(run_async=True) #Start in new thread
@@ -58,17 +67,19 @@ def main():
time.sleep(0.25)
# WIP: Remove clients that have disconnected
- remove_clients = bumper.bumper_removeclients_var.get()
- if len(remove_clients) > 0:
- for uid in remove_clients:
- if uid != "":
- xmpp_server.remove_client_byuid(uid) #Remove clients from xmpp server
- remove_clients.remove(uid)
+ # remove_clients = bumper.bumper_removeclients_var.get()
+ # if len(remove_clients) > 0:
+ # for uid in remove_clients:
+ # if uid != "":
+ # xmpp_server.remove_client_byuid(uid) #Remove clients from xmpp server
+ # remove_clients.remove(uid)
- bumper.bumper_removeclients_var.set(remove_clients)
+ # bumper.bumper_removeclients_var.set(remove_clients)
- except KeyboardInterrupt:
+ except KeyboardInterrupt:
+ bumper.bumperlog.info("Bumper Exiting - Keyboard Interrupt")
print("Bumper Exiting")
+ exit(1)
if __name__ == "__main__":
main()
\ No newline at end of file
diff --git a/bumper/__init__.py b/bumper/__init__.py
index e38cf5b..b39671c 100644
--- a/bumper/__init__.py
+++ b/bumper/__init__.py
@@ -8,10 +8,12 @@ import asyncio
import contextvars
import time
import logging
+from base64 import b64decode, b64encode
+bumper_users_var = contextvars.ContextVar('bumper_users', default=[])
bumper_clients_var = contextvars.ContextVar('bumper_clients', default=[])
bumper_bots_var = contextvars.ContextVar('bumper_bots', default=[])
-bumper_removeclients_var = contextvars.ContextVar('bumper_removeclients', default=[])
+
ca_cert = './certs/CA/cacert.pem'
server_cert = './certs/cert.pem'
server_key = './certs/key.pem'
@@ -35,6 +37,47 @@ def get_milli_time(timetoconvert):
return int(round(timetoconvert * 1000))
+class BumperUser(object):
+ def __init__(self,userid=""):
+ self.userid = userid
+ self.devices = []
+ self.tokens = []
+ self.authcodes = []
+ self.bots = []
+
+ def add_device(self, devid):
+ if not devid in self.devices:
+ self.devices.append(devid)
+
+ def remove_device(self, devid):
+ if devid in self.devices:
+ self.devices.remove(devid)
+
+
+ def add_token(self, token):
+ if not token in self.tokens:
+ self.tokens.append(token)
+
+ def revoke_token(self, token):
+ if token in self.tokens:
+ self.tokens.remove(token)
+
+ def add_authcode(self, authcode):
+ if not authcode in self.authcodes:
+ self.authcodes.append(authcode)
+
+ def revoke_authcode(self, authcode):
+ if authcode in self.authcodes:
+ self.authcodes.remove(authcode)
+
+ def add_bot(self, botdid):
+ if not botdid in self.bots:
+ self.bots.append(botdid)
+
+ def remove_bot(self, botdid):
+ if botdid in self.bots:
+ self.bots.remove(botdid)
+
class VacBotDevice(object):
def __init__(self,did="", vac_bot_device_class="",resource="" , name="", nick="", company="eco-ng"):
self.vac_bot_device_class = vac_bot_device_class
@@ -48,12 +91,11 @@ class VacBotDevice(object):
return {"class": self.vac_bot_device_class, "company": self.company,
"did": self.did, "name": self.name, "nick": self.nick, "resource": self.resource}
-class VacBotUser(object):
+class VacBotClient(object):
def __init__(self,userid="",realm="",token=""):
self.userid = userid
self.realm = realm
self.resource = token
-
def asdict(self):
return {"userid": self.userid,"realm": self.realm,"resource": self.resource}
diff --git a/bumper/confserver.py b/bumper/confserver.py
index 501ca1f..ebebe5c 100644
--- a/bumper/confserver.py
+++ b/bumper/confserver.py
@@ -10,6 +10,7 @@ from datetime import datetime, timedelta
import asyncio
import contextvars
from aiohttp import web
+import uuid
class aiohttp_filter(logging.Filter):
@@ -32,10 +33,10 @@ class ConfServer():
bumper_clients = contextvars.ContextVar
bumper_bots = contextvars.ContextVar
- def __init__(self, address, usessl=False, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar, remove_clients=contextvars.ContextVar,helperbot=None):
+ def __init__(self, address, usessl=False, bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar, helperbot=None):
+ self.bumper_users = bumper_users
self.bumper_bots = bumper_bots
- self.bumper_clients = bumper_clients
- self.remove_clients = remove_clients
+ self.bumper_clients = bumper_clients
self.helperbot = helperbot
self.usessl = usessl
self.address = address
@@ -82,7 +83,7 @@ class ConfServer():
app.add_routes([
web.get('', self.handle_base),
web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login', self.handle_login),
- # web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin', self.handle_checkLogin),
+ web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin', self.handle_login),
web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout', self.handle_logout),
web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode', self.handle_getAuthCode),
web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement', self.handle_checkAgreement),
@@ -96,6 +97,9 @@ class ConfServer():
web.post('/lookup.do', self.handle_lookup),
])
+ #Direct register from app:
+ #/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister
+
runner = web.AppRunner(app)
@@ -130,61 +134,65 @@ class ConfServer():
except Exception as e:
confserverlog.exception('{}'.format(e))
- async def handle_login(self, request):
+ async def handle_login(self, request):
+
try:
- #Could implement basic auth if you wanted, or just accept anything
- countrycode = request.match_info.get('country', "us")
+ user_devid = request.match_info.get('devid', "")
+ countrycode = request.match_info.get('country', "us")
+ if not user_devid == "": #Performing basic "auth" using devid, super insecure
+ users = bumper.bumper_users_var.get()
+ for user in users:
+ if user_devid in user.devices:
+ tmpaccesstoken = ''
+ if 'checkLogin' in request.path:
+ if request.query['accessToken'] in user.tokens and request.query['uid'] == "fuid_{}".format(user.userid):
+ tmpaccesstoken = request.query['accessToken']
+ else:
+ if tmpaccesstoken == '':
+ tmpaccesstoken = uuid.uuid4().hex
+ user.add_token(tmpaccesstoken)
+
+ body = {
+ "code": "0000",
+ "data": {
+ "accessToken": tmpaccesstoken, #Random chars 32 length
+ "country": countrycode,
+ "email": "null@null.com",
+ "uid": "fuid_{}".format(user.userid),
+ "username": "fusername_{}".format(user.userid),
+ },
+ "msg": "操作成功",
+ "time": bumper.get_milli_time(time.time())
+ }
+ bumper.bumper_users_var.set(users)
+ return web.json_response(body)
+
body = {
- "code": "0000",
- "data": {
- "accessToken": "tempaccesstoken", #Random chars 32 length
- "country": countrycode,
- "email": "null@null.com",
- "uid": "fuid_{}".format(''.join(random.sample(string.ascii_letters,6))), #Date(14)_RandomChars(32)
- "username": "fusername_{}".format(''.join(random.sample(string.ascii_letters,6))) #Random chars 8
- },
- "msg": "操作成功",
+ "code": "1005",
+ "data": None,
+ "msg": "当前密码错误",
"time": bumper.get_milli_time(time.time())
- }
-
+ }
+
return web.json_response(body)
+
except Exception as e:
confserverlog.exception('{}'.format(e))
- async def handle_checkLogin(self, request):
- try:
- # The app seems to remember it's last uid and accessToken
- # If these don't match, it fails
- countrycode = request.match_info.get('country', "us")
- body = {
- "code": "0000",
- "data": {
- "accessToken": "tempaccesstoken", #Random chars 32 length
- "country": countrycode,
- "email": "null@null.com",
- "uid": "fuid_{}".format(''.join(random.sample(string.ascii_letters,6))), #Date(14)_RandomChars(32)
- "username": "fusername_{}".format(''.join(random.sample(string.ascii_letters,6))) #Random chars 8
- },
- "msg": "操作成功",
- "time": bumper.get_milli_time(time.time())
- }
-
- return web.json_response(body)
-
- except Exception as e:
- confserverlog.exception('{}'.format(e))
async def handle_logout(self, request):
- try:
- uid = request.query['uid']
- body = {"code": "0000","data": None,"msg": "操作成功", "time": bumper.get_milli_time(time.time())}
- # QUERY String
- # 'uid=fuid_CUOVIn&accessToken=tempaccesstoken&requestId=e584de79f9cca854df6fb3352c6893b6&authTimespan=1550168440575&authTimeZone=GMT-5&authAppkey=eJUWrzRv34qFSaYk&authSign=14e38f95c7316e111c5815cf15f0f972'
- if not uid == "":
- remove_clients = self.remove_clients.get()
- remove_clients.append(request.query['uid'])
- self.remove_clients.set(remove_clients)
+ try:
+ user_devid = request.match_info.get('devid', "")
+ if not user_devid == "":
+ users = bumper.bumper_users_var.get()
+ for user in users:
+ if user_devid in user.devices:
+ if request.query['uid'] == "fuid_{}".format(user.userid) and request.query['accessToken'] in user.tokens:
+ user.revoke_token(request.query['accessToken'])
+ bumper.bumper_users_var.set(users)
+
+ body = {"code": "0000","data": None,"msg": "操作成功", "time": bumper.get_milli_time(time.time())}
return web.json_response(body)
@@ -193,19 +201,37 @@ class ConfServer():
async def handle_getAuthCode(self, request):
try:
- countrycode = request.match_info.get('country', "us")
- body = {
- "code": "0000",
- "data": {
- "authCode": "{}_tempauthcode".format(countrycode), #countrycode_randomchars(32)
- "ecovacsUid": "fuid_{}".format(''.join(random.sample(string.ascii_letters,6))) #Date(14)_RandomChars(32)
- },
- "msg": "操作成功",
- "time": bumper.get_milli_time(time.time())
- }
-
- return web.json_response(body)
+
+ user_devid = request.match_info.get('devid', "")
+ if not user_devid == "":
+ users = bumper.bumper_users_var.get()
+ for user in users:
+ if user_devid in user.devices and request.query['accessToken'] in user.tokens:
+ countrycode = request.match_info.get('country', "us")
+ tmpauthcode = "{}_{}".format(countrycode,uuid.uuid4().hex)
+ user.add_authcode(tmpauthcode)
+
+ body = {
+ "code": "0000",
+ "data": {
+ "authCode": tmpauthcode,
+ "ecovacsUid": request.query['uid']
+ },
+ "msg": "操作成功",
+ "time": bumper.get_milli_time(time.time())
+ }
+ bumper.bumper_users_var.set(users)
+ return web.json_response(body)
+ body = {
+ "code": "1005",
+ "data": None,
+ "msg": "当前密码错误",
+ "time": bumper.get_milli_time(time.time())
+ }
+
+ return web.json_response(body)
+
except Exception as e:
confserverlog.exception('{}'.format(e))
@@ -271,7 +297,6 @@ class ConfServer():
async def handle_getProductIotMap(self, request):
try:
- #json_body = json.loads(await request.text())
body = {"code":0,"data":[{"classid":"dl8fht","product":{"_id":"5acb0fa87c295c0001876ecf","name":"DEEBOT 600 Series","icon":"5acc32067c295c0001876eea","UILogicId":"dl8fht","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea"}},{"classid":"02uwxm","product":{"_id":"5ae1481e7ccd1a0001e1f69e","name":"DEEBOT OZMO Slim10 Series","icon":"5b1dddc48bc45700014035a1","UILogicId":"02uwxm","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1"}},{"classid":"y79a7u","product":{"_id":"5b04c0227ccd1a0001e1f6a8","name":"DEEBOT OZMO 900","icon":"5b04c0217ccd1a0001e1f6a7","UILogicId":"y79a7u","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7"}},{"classid":"jr3pqa","product":{"_id":"5b43077b8bc457000140363e","name":"DEEBOT 711","icon":"5b5ac4cc8d5a56000111e769","UILogicId":"jr3pqa","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769"}},{"classid":"uv242z","product":{"_id":"5b5149b4ac0b87000148c128","name":"DEEBOT 710","icon":"5b5ac4e45f21100001882bb9","UILogicId":"uv242z","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9"}},{"classid":"ls1ok3","product":{"_id":"5b6561060506b100015c8868","name":"DEEBOT 900 Series","icon":"5ba4a2cb6c2f120001c32839","UILogicId":"ls1ok3","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839"}}]}
return web.json_response(body)
@@ -297,13 +322,18 @@ class ConfServer():
elif service == 'EcoUpdate':
body = {"result":"ok","ip":"47.88.66.164","port":8005}
elif todo == 'loginByItToken':
- body = {
- "resource": postbody["resource"],
- "result": "ok",
- "todo": "result",
- "token": postbody["token"], #RandomChar(32)
- "userId": postbody["userId"] #RandomChar(16)
- }
+
+ users = bumper.bumper_users_var.get()
+ for user in users:
+ if postbody['userId'] == "fuid_{}".format(user.userid) and postbody['token'] in user.authcodes:
+ body = {
+ "resource": postbody["resource"],
+ "result": "ok",
+ "todo": "result",
+ "token": postbody["token"],
+ "userId": postbody["userId"]
+ }
+
elif todo == 'GetDeviceList':
active_bots = self.bumper_bots.get()
body = {
diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py
index 39e610b..e7ee992 100644
--- a/bumper/mqttserver.py
+++ b/bumper/mqttserver.py
@@ -174,12 +174,11 @@ class MQTTServer():
except Exception as e:
mqttserverlog.exception('{}'.format(e))
- def __init__(self, address, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar,remove_clients=contextvars.ContextVar):
+ def __init__(self, address, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar):
try:
self.bumper_bots = bumper_bots
self.bumper_clients = bumper_clients
- self.remove_clients = remove_clients
self.mqttserverthread = None
self.address = address
@@ -216,7 +215,6 @@ class MQTTServer():
'clients':{
'connected_bots': self.bumper_bots,
'connected_clients': self.bumper_clients,
- 'remove_clients': self.remove_clients
},
}
@@ -285,7 +283,7 @@ class BumperMQTTServer_Plugin:
self.clients['connected_bots'].set(connected_bots)
else:
tmpuserdetail = str(didsplit[1]).split("/")
- newuser = bumper.VacBotUser()
+ newuser = bumper.VacBotClient()
newuser.userid = didsplit[0]
newuser.realm = tmpuserdetail[0]
newuser.resource = tmpuserdetail[1]
@@ -314,7 +312,7 @@ class BumperMQTTServer_Plugin:
#mqttserverlog.debug('%s disconnected' % client_id)
connected_bots = self.clients['connected_bots'].get()
connected_clients = self.clients['connected_clients'].get()
- remove_clients = self.clients['remove_clients'].get()
+ #remove_clients = self.clients['remove_clients'].get()
didsplit = str(client_id).split("@")
#If the did is in the list, remove it
for bot in connected_bots:
diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py
index e0a3228..4c1f800 100644
--- a/bumper/xmppserver.py
+++ b/bumper/xmppserver.py
@@ -4,6 +4,7 @@ from threading import Thread
import sys, socket, threading, re, time, logging, uuid, xml.etree.ElementTree as ET
import base64
import ssl
+import contextvars
import bumper
xmppserverlog = logging.getLogger("xmppserver")
@@ -15,9 +16,12 @@ class XMPPServer():
clients = []
exit_flag = False
- def __init__(self, address):
+ def __init__(self, address, bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar):
# Initialize bot server
self.address = address
+ self.bumper_users = bumper_users
+ self.bumper_bots = bumper_bots
+ self.bumper_clients = bumper_clients
def run(self, run_async=False):
@@ -86,9 +90,7 @@ class XMPPServer():
self.disconnect()
xmppserverlog.info('disconnecting')
- self.socket.close()
-
-
+ self.socket.close()
def disconnect(self):
try:
@@ -339,8 +341,8 @@ class Client(threading.Thread):
elif 'resource' in aitem.tag:
self.clientresource = aitem.text
-
- if True:
+
+ if self.check_authcode(self.uid, password):
#Client authenticated, move to next state
self._set_state('INIT')
@@ -364,6 +366,13 @@ class Client(threading.Thread):
except Exception as e:
xmppserverlog.exception('{}'.format(e))
+ def check_authcode(self, uid, authcode):
+ users = bumper.bumper_users_var.get()
+ for user in users:
+ if uid == "fuid_{}".format(user.userid) and authcode in user.authcodes:
+ return True
+
+ return False
def _handle_sasl_auth(self, data):
try:
@@ -375,18 +384,18 @@ class Client(threading.Thread):
resource = saslauth[1]
self.clientresource = resource
authcode = saslauth[2]
-
- if authcode == "us_tempauthcode": #Handle auth
+
+ if self.check_authcode(self.uid, authcode):
#Send response
self.send('') #Success
#Client authenticated, move to next state
self._set_state('INIT')
-
+
else:
#Failed to authenticate
self.send('') #Fail
-
+
except ET.ParseError as e:
if "no element found" in e.msg:
xmppserverlog.debug('xml parse error - {} - {} - this is common with ecovac protocol'.format(data.decode('utf-8'), e))