-
Notifications
You must be signed in to change notification settings - Fork 0
/
__init__.py
146 lines (127 loc) · 5.87 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# -*- coding: utf-8 -*-
#
# Copyright 2017 dpa-infocom GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import hashlib
import json
import logging
import uuid
import os.path
from aiohttp import web
logger = logging.getLogger(__name__)
_tokens = {}
async def auth_middleware(app, handler):
async def middleware_handler(request):
try:
# check auth header
if not request.path in ["/", "/api/v1/session"] and not request.path.startswith("/dashboard"):
token = request.cookies.get("lb-db") or request.headers.get("X-Auth-Token")
if token not in _tokens.values():
return web.json_response({"error": "Invalid token."}, status=401)
# return response
response = await handler(request)
return response
except web.HTTPException as ex:
raise
return middleware_handler
def error_overrides(overrides):
async def middleware(app, handler):
async def middleware_handler(request):
try:
response = await handler(request)
return response
except web.HTTPException as ex:
override = overrides.get(ex.status)
if override:
return await override(request, ex)
return middleware_handler
return middleware
class WebApi(object):
def __init__(self, *, config, controller, loop):
self.config = config
self.loop = loop
self.static_path = os.path.join(os.path.dirname(__file__), "static")
self.control_etag = None
# start server
logger.info("Starting API server ...")
middlewares = [error_overrides({405: self.handle_405}), auth_middleware]
self.app = web.Application(loop=loop, middlewares=middlewares)
self.app["controller"] = controller
self.app.router.add_get("/", self.index_handler)
self.app.router.add_static("/dashboard", self.static_path, show_index=True)
self.app.router.add_get("/api/v1/controldata", self.control_get)
self.app.router.add_put("/api/v1/controldata", self.control_put)
self.app.router.add_post("/api/v1/session", self.login, expect_handler=web.Request.json)
self.handler = self.app.make_handler()
f = self.loop.create_server(self.handler, self.config["host"], self.config["port"])
self.srv = loop.run_until_complete(f) if not loop.is_running() else None
logger.info("... API server started up")
def _get_etag(self, data):
hash_md5 = hashlib.md5()
hash_md5.update(json.dumps(data).encode("utf-8"))
return hash_md5.hexdigest()
async def login(self, request):
try:
assert self.config["auth"]["user"]
assert self.config["auth"]["password"]
except AssertionError:
logger.error("HTTP Auth credentials are missing!")
return web.json_response({"error": "Auth credentials are missing."}, status=400)
params = await request.post()
user = params.get('username', None)
if (user == self.config["auth"]["user"] and
params.get('password', None) == self.config["auth"]["password"]):
# User is in our database, remember their login details
_tokens[user] = str(uuid.uuid4())
response = web.json_response({"token": _tokens[user]})
response.set_cookie("lb-db", _tokens[user])
return response
return web.json_response({"error": "Unauthorized"}, status=401)
async def handle_405(self, request, response):
return web.json_response({"error": "Method Not Allowed"}, status=405)
async def index_handler(self, request):
static_path = os.path.join(self.static_path, "index.html")
return web.FileResponse(static_path)
async def control_get(self, request):
control_doc = await self.app["controller"].load_control_doc()
return web.json_response(control_doc, headers={"Etag": self._get_etag(control_doc)})
async def control_put(self, request):
try:
# check etag first
control_doc = await self.app["controller"].load_control_doc()
if self._get_etag(control_doc) != request.headers.get("If-Match"):
return web.json_response({"error": "Precondition Failed."}, status=412)
# handle data
await request.post()
if request.has_body:
uploaded_doc = await request.json()
res = await self.app["controller"].save_control_data(uploaded_doc)
if res:
return web.json_response({"ok": "true"})
else:
return web.json_response({"error": "Controldata was not saved."}, status=400)
else:
return web.json_response({"error": "No request body was found."}, status=400)
except Exception as exc:
logger.error("Error handling PUT controldata")
logger.exception(exc)
return web.json_response({"error": "Internal Server Error"}, status=500)
def shutdown(self):
logger.debug("Shutting down web API!")
if self.srv:
self.srv.close()
self.loop.run_until_complete(self.srv.wait_closed())
self.loop.run_until_complete(self.app.shutdown())
self.loop.run_until_complete(self.handler.shutdown(60.0))
self.loop.run_until_complete(self.app.cleanup())