summaryrefslogtreecommitdiff
path: root/src/wsgi.py
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2021-06-21 16:34:11 +0200
committerLinus Nordberg <linus@nordberg.se>2021-06-21 16:34:11 +0200
commit475a215ef62140d5656ba794cc29e67226587bbc (patch)
tree450b5c0f7d0f7df1f49fda1b5d29f8034cb4c0fb /src/wsgi.py
initial commit
Diffstat (limited to 'src/wsgi.py')
-rwxr-xr-xsrc/wsgi.py120
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())