diff options
Diffstat (limited to 'src/wsgi.py')
-rwxr-xr-x | src/wsgi.py | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/wsgi.py b/src/wsgi.py new file mode 100755 index 0000000..1eda9de --- /dev/null +++ b/src/wsgi.py @@ -0,0 +1,120 @@ +#! /usr/bin/env python3 + +import sys +from wsgiref.simple_server import make_server +import falcon +import json +from db import DictDB +import time +from base64 import b64decode + +class CollectorResource(): + def __init__(self, db): + self._db = db + + def parse_error(data): + return "I want valid JSON but got this:\n{}\n".format(data) + + def user_authn(self, auth_header, authfun): + if not auth_header: + return None # Fail. + BAlit, b64 = auth_header.split() + if BAlit != "Basic": + return None # Fail + userbytes, pwbytes = b64decode(b64).split(b':') + try: + user = userbytes.decode('ascii') + except: + return None # Fail + if authfun(user, pwbytes): + return user # Success. + return None # Fail. + + +class EPGet(CollectorResource): + def on_get(self, req, resp): + resp.status = falcon.HTTP_200 + resp.content_type = falcon.MEDIA_JSON + out = [] + + userid = self.user_authn(req.auth, lambda user,_pw: user is not None) + if not userid: + resp.status = falcon.HTTP_401 + resp.text = 'Invalid user or password\n' + return + + out = [{time.ctime(key): dict} for (key, dict) in self._db.search('domain', dict_val=userid)] + + resp.text = json.dumps(out) + '\n' + + +class EPAdd(CollectorResource): + def on_post(self, req, resp): + resp.status = falcon.HTTP_200 + resp.content_type = falcon.MEDIA_TEXT + self._indata = [] + + if self.user_authn(req.auth, + lambda u,p: u == 'admin' and p == b'admin') is None: + resp.status = falcon.HTTP_401 + resp.text = 'Invalid user or password\n' + return + + # TODO: can we do json.load(req.bounded_stream, + # cls=customDecoder) where our decoder calls JSONDecoder after + # decoding UTF-8? + + # NOTE: Reading the whole body in one go instead of streaming + # it nicely. + rawin = req.bounded_stream.read() + try: + decodedin = rawin.decode('UTF-8') + except: + resp.status = falcon.HTTP_400 + resp.text = 'Need UTF-8\n' + return + + try: + keys = json.loads(decodedin, object_hook=self.handle_data) + except TypeError: + print('DEBUG: type error') + resp.status = falcon.HTTP_400 + resp.text = CollectorResource.parse_error(decodedin) + return + except json.decoder.JSONDecodeError: + print('DEBUG: json decode error') + resp.status = falcon.HTTP_400 + resp.text = CollectorResource.parse_error(decodedin) + return + + resp.text = '' + for key in keys: + resp.text += repr(key) + '\n' + + def handle_data(self, data): + return self._db.add(data) # return key + + +def init(url_res_map, addr = '', port = 8000): + app = falcon.App() + for url, res in url_res_map: + app.add_route(url, res) + + return make_server(addr, port, app) + + +def main(): + # Simple demo. + # Try adding some observations, basic auth admin:admin, and + # include {"domain": "foo.se"} in some of them. + # Try retreiving all observations for user 'foo.se' (basic auth + # foo.se:whatever). + + db = DictDB('wsgi_demo.db') + httpd = init([('/sc/v0/add', EPAdd(db)), + ('/sc/v0/get', EPGet(db))]) + print('Serving on port 8000...') + httpd.serve_forever() + +if __name__ == '__main__': + sys.exit(main()) |