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
|
#! /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())
|