summaryrefslogtreecommitdiff
path: root/src/wsgi.py
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2021-09-15 15:29:10 +0200
committerLinus Nordberg <linus@nordberg.se>2021-09-15 15:29:10 +0200
commit838d36e7cab2f11322d4c3c211407a73ebc712b9 (patch)
treecfbb667b8232221b1d00cdbb1ab5428b4647ef8f /src/wsgi.py
parent6cccfe8cb27fc58f373c069e53f1c8773427d173 (diff)
add simple authentication based on a local yaml file
This allows for mapping username/password pairs to sets of organisations with 'r' or 'rw' permissions. To be replaced with an external service providing a JWT in an HTTP header.
Diffstat (limited to 'src/wsgi.py')
-rwxr-xr-xsrc/wsgi.py63
1 files changed, 41 insertions, 22 deletions
diff --git a/src/wsgi.py b/src/wsgi.py
index aed3513..98efc6f 100755
--- a/src/wsgi.py
+++ b/src/wsgi.py
@@ -8,27 +8,29 @@ from db import DictDB
import time
from base64 import b64decode
+import authn
+
class CollectorResource():
- def __init__(self, db):
+ def __init__(self, db, users):
self._db = db
+ self._users = users
def parse_error(data):
return "I want valid JSON but got this:\n{}\n".format(data)
- def user_authn(self, auth_header, authfun):
+ def user_auth(self, auth_header, authfun):
if not auth_header:
- return None # Fail.
+ return None, None # Fail.
BAlit, b64 = auth_header.split()
if BAlit != "Basic":
- return None # Fail
+ return None, None # Fail
userbytes, pwbytes = b64decode(b64).split(b':')
try:
- user = userbytes.decode('ascii')
+ user = userbytes.decode('utf-8')
+ pw = pwbytes.decode('utf-8')
except:
- return None # Fail
- if authfun(user, pwbytes):
- return user # Success.
- return None # Fail.
+ return None, None # Fail
+ return authfun(user, pw)
class EPGet(CollectorResource):
@@ -37,13 +39,14 @@ class EPGet(CollectorResource):
resp.content_type = falcon.MEDIA_JSON
out = []
- userid = self.user_authn(req.auth, lambda user,_pw: user is not None)
- if not userid:
+ orgs = self.user_auth(req.auth, self._users.read_perms)
+ if not orgs:
resp.status = falcon.HTTP_401
- resp.text = 'Invalid user or password\n'
+ resp.text = 'Invalid username or password\n'
return
- out = [{time.ctime(key): dict} for (key, dict) in self._db.search('domain', dict_val=userid)]
+ for org in orgs:
+ out += [{time.ctime(key): dict} for (key, dict) in self._db.search('domain', dict_val=org)]
resp.text = json.dumps(out) + '\n'
@@ -54,12 +57,15 @@ class EPAdd(CollectorResource):
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:
+ orgs = self.user_auth(req.auth, self._users.write_perms)
+ if not orgs:
resp.status = falcon.HTTP_401
resp.text = 'Invalid user or password\n'
return
+ # NOTE: Allowing writing to _any_ org!
+ # TODO: Allow only input where input.domain in orgs == True.
+
# TODO: can we do json.load(req.bounded_stream,
# cls=customDecoder) where our decoder calls JSONDecoder after
# decoding UTF-8?
@@ -100,15 +106,28 @@ def init(url_res_map, addr = '', port = 8000):
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).
+ # Simple demo. Run it from the demo directory where a sample user
+ # database can be found:
+ #
+ # $ cd demo && ../src/wsgi.py
+ # Serving on port 8000...
+ #
+ # 1. Try adding some observations, basic auth user:pw from
+ # wsgi_demo_users.yaml, including {"domain": "sunet.se"} in at
+ # least one of them:
+ #
+ # $ echo '[{"ip": "192.168.0.1", "port": 80, "domain": "sunet.se"}]' | curl -s -u user3:pw3 --data-binary @- http://localhost:8000/sc/v0/add
+ #
+ # 2. Try retreiving all observations for a user with read access
+ # to 'sunet.se':
+ #
+ # $ curl -s -u user1:pw1 http://localhost:8000/sc/v0/get | json_pp -json_opt utf8,pretty
db = DictDB('wsgi_demo.db')
- httpd = init([('/sc/v0/add', EPAdd(db)),
- ('/sc/v0/get', EPGet(db))])
+ users = authn.UserDB('wsgi_demo_users.yaml')
+
+ httpd = init([('/sc/v0/add', EPAdd(db, users)),
+ ('/sc/v0/get', EPGet(db, users))])
print('Serving on port 8000...')
httpd.serve_forever()