add new endpoints for app v2 #116
7 changed files with 161 additions and 6 deletions
|
|
@ -57,6 +57,7 @@ bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
|
||||||
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
|
bumper_debug = strtobool(os.environ.get("BUMPER_DEBUG")) or False
|
||||||
use_auth = False
|
use_auth = False
|
||||||
token_validity_seconds = 3600 # 1 hour
|
token_validity_seconds = 3600 # 1 hour
|
||||||
|
oauth_validity_days = 15
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
mqtt_server = None
|
mqtt_server = None
|
||||||
|
|
|
||||||
|
|
@ -62,13 +62,12 @@ class ConfServer:
|
||||||
|
|
||||||
self.app.add_routes(
|
self.app.add_routes(
|
||||||
[
|
[
|
||||||
|
web.get("", self.handle_base, name="base"),
|
||||||
web.get("", self.handle_base, name="base"),
|
|
||||||
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
web.get("/bot/remove/{did}", self.handle_RemoveBot, name='remove-bot'),
|
||||||
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
web.get("/client/remove/{resource}", self.handle_RemoveClient, name='remove-client'),
|
||||||
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
web.get("/restart_{service}", self.handle_RestartService, name='restart-service'),
|
||||||
web.post("/lookup.do", self.handle_lookup),
|
web.post("/lookup.do", self.handle_lookup),
|
||||||
|
web.post("/newauth.do", self.handle_newauth),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -507,6 +506,26 @@ class ConfServer:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
confserverlog.exception("{}".format(e))
|
confserverlog.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_newauth(self, request):
|
||||||
|
# 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":
|
||||||
|
postbody = await request.post()
|
||||||
|
else:
|
||||||
|
postbody = json.loads(await request.text())
|
||||||
|
|
||||||
|
confserverlog.debug(postbody)
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"authCode": postbody["itToken"],
|
||||||
|
"result": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
confserverlog.exception("{}".format(e))
|
||||||
|
|
||||||
async def disconnect(self):
|
async def disconnect(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
37
bumper/db.py
37
bumper/db.py
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import bumper
|
import bumper
|
||||||
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts
|
from bumper.models import VacBotClient, VacBotDevice, BumperUser, EcoVacsHomeProducts, OAuth
|
||||||
from tinydb import TinyDB, Query
|
from tinydb import TinyDB, Query
|
||||||
from tinydb.storages import MemoryStorage
|
from tinydb.storages import MemoryStorage
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
@ -32,6 +32,7 @@ def db_get():
|
||||||
db.table("clients", cache_size=0)
|
db.table("clients", cache_size=0)
|
||||||
db.table("bots", cache_size=0)
|
db.table("bots", cache_size=0)
|
||||||
db.table("tokens", cache_size=0)
|
db.table("tokens", cache_size=0)
|
||||||
|
db.table("oauth", cache_size=0)
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
|
@ -202,6 +203,40 @@ def user_revoke_authcode(userid, token, authcode):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def user_revoke_expired_oauths(userid):
|
||||||
|
opendb = db_get()
|
||||||
|
with opendb:
|
||||||
|
table = opendb.table("oauth")
|
||||||
|
search = table.search(Query().userid == userid)
|
||||||
|
for i in search:
|
||||||
|
oauth = OAuth(**i)
|
||||||
|
if datetime.now() >= datetime.fromisoformat(oauth.expire_at):
|
||||||
|
bumperlog.debug(
|
||||||
|
"Removing oauth {} due to expiration".format(oauth.access_token)
|
||||||
|
)
|
||||||
|
table.remove(doc_ids=[i.doc_id])
|
||||||
|
|
||||||
|
|
||||||
|
def user_add_oauth(userid) -> OAuth:
|
||||||
|
user_revoke_expired_oauths(userid)
|
||||||
|
opendb = db_get()
|
||||||
|
with opendb:
|
||||||
|
table = opendb.table("oauth")
|
||||||
|
entry = table.get(Query().userid == userid)
|
||||||
|
if entry:
|
||||||
|
return OAuth(**entry)
|
||||||
|
else:
|
||||||
|
oauth = OAuth.create_new(userid)
|
||||||
|
bumperlog.debug("Adding oauth {} for userid {}".format(oauth.access_token, userid))
|
||||||
|
table.insert(oauth.toDB())
|
||||||
|
return oauth
|
||||||
|
|
||||||
|
|
||||||
|
def token_by_authcode(authcode):
|
||||||
|
tokens = db_get().table("tokens")
|
||||||
|
return tokens.get(Query().authcode == authcode)
|
||||||
|
|
||||||
|
|
||||||
def get_disconnected_xmpp_clients():
|
def get_disconnected_xmpp_clients():
|
||||||
clients = db_get().table("clients")
|
clients = db_get().table("clients")
|
||||||
Client = Query()
|
Client = Query()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import json
|
import json
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import bumper
|
||||||
|
|
||||||
|
|
||||||
class VacBotDevice(object):
|
class VacBotDevice(object):
|
||||||
|
|
@ -83,6 +87,34 @@ class EcoVacsHome_Login(EcoVacs_Login):
|
||||||
ucUid = ""
|
ucUid = ""
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth:
|
||||||
|
access_token = ""
|
||||||
|
expire_at = ""
|
||||||
|
refresh_token = ""
|
||||||
|
userId = ""
|
||||||
|
|
||||||
|
def __init__(self, **entries):
|
||||||
|
self.__dict__.update(entries)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_new(cls, userId: str):
|
||||||
|
oauth = OAuth()
|
||||||
|
oauth.userId = userId
|
||||||
|
oauth.access_token = uuid.uuid4().hex
|
||||||
|
oauth.expire_at = "{}".format(datetime.utcnow() + timedelta(days=bumper.oauth_validity_days))
|
||||||
|
oauth.refresh_token = uuid.uuid4().hex
|
||||||
|
return oauth
|
||||||
|
|
||||||
|
def toDB(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
def toResponse(self):
|
||||||
|
data = self.__dict__
|
||||||
|
data["expire_at"] = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time(
|
||||||
|
datetime.fromisoformat(self.expire_at).timestamp())
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
# EcoVacs Home Product IOT Map - 2020-01-05
|
# EcoVacs Home Product IOT Map - 2020-01-05
|
||||||
# https://portal-ww.ecouser.net/api/pim/product/getProductIotMap
|
# https://portal-ww.ecouser.net/api/pim/product/getProductIotMap
|
||||||
EcoVacsHomeProducts = [
|
EcoVacsHomeProducts = [
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
||||||
self.sub_api = "portal_api"
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
self.routes = [
|
self.routes = [
|
||||||
|
|
||||||
web.route("*", "/appsvr/app.do", self.handle_appsvr_app, name="portal_api_appsvr_app"),
|
web.route("*", "/appsvr/app.do", self.handle_appsvr_app, name="portal_api_appsvr_app"),
|
||||||
web.route("*", "/appsvr/service/list", self.handle_appsvr_service_list, name="portal_api_appsvr_service_list"),
|
web.route("*", "/appsvr/service/list", self.handle_appsvr_service_list, name="portal_api_appsvr_service_list"),
|
||||||
|
web.route("*", "/appsvr/oauth_callback", self.handle_appsvr_oauth_callback, name="portal_api_appsvr_oauth_callback"),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||||
|
|
@ -180,4 +179,20 @@ class portal_api_appsvr(plugins.ConfServerApp):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception("{}".format(e))
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
async def handle_appsvr_oauth_callback(self, request):
|
||||||
|
try:
|
||||||
|
token = bumper.token_by_authcode(request.query["code"])
|
||||||
|
oauth = bumper.user_add_oauth(token["userid"])
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": oauth.toResponse(),
|
||||||
|
"ret": "ok",
|
||||||
|
"todo": "result"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
plugin = portal_api_appsvr()
|
plugin = portal_api_appsvr()
|
||||||
|
|
|
||||||
39
bumper/plugins/bumper_confserver_portal_rapp.py
Normal file
39
bumper/plugins/bumper_confserver_portal_rapp.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from aiohttp import web
|
||||||
|
import logging
|
||||||
|
from bumper.models import *
|
||||||
|
from bumper import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class api_rapp(plugins.ConfServerApp):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.name = "api_rapp"
|
||||||
|
self.plugin_type = "sub_api"
|
||||||
|
self.sub_api = "portal_api"
|
||||||
|
|
||||||
|
self.routes = [
|
||||||
|
web.route("*", "/rapp/sds/user/data/map/get", self.handle_map_get, name="api_rapp"),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
|
||||||
|
|
||||||
|
async def handle_map_get(self, request):
|
||||||
|
try:
|
||||||
|
body = {
|
||||||
|
"code": 0,
|
||||||
|
"data": {
|
||||||
|
"data": {
|
||||||
|
"name": "My Home"
|
||||||
|
},
|
||||||
|
"tag": None
|
||||||
|
},
|
||||||
|
"message": "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
return web.json_response(body)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("{}".format(e))
|
||||||
|
|
||||||
|
plugin = api_rapp()
|
||||||
14
tests/test_models.py
Normal file
14
tests/test_models.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from bumper import OAuth
|
||||||
|
|
||||||
|
|
||||||
|
def test_oauth():
|
||||||
|
userId = "test"
|
||||||
|
o_auth = OAuth.create_new(userId)
|
||||||
|
assert o_auth is not None
|
||||||
|
assert o_auth.userId == userId
|
||||||
|
assert o_auth.access_token is not None
|
||||||
|
assert o_auth.expire_at is not None
|
||||||
|
assert o_auth.refresh_token is not None
|
||||||
|
|
||||||
|
data = o_auth.toResponse()
|
||||||
|
assert data is not None
|
||||||
Loading…
Add table
Add a link
Reference in a new issue