From ecbfa279cc6f56df7a84d150c527923730986d36 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 3 Nov 2013 15:09:40 +0100 Subject: [PATCH] Added a new module to ease WWW-authentication a bit. --- wrw/auth.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 wrw/auth.py diff --git a/wrw/auth.py b/wrw/auth.py new file mode 100644 index 0000000..4ae292d --- /dev/null +++ b/wrw/auth.py @@ -0,0 +1,90 @@ +import binascii, hashlib, threading, time +import resp + +class unauthorized(resp.httperror): + def __init__(self, challenge, message=None, detail=None): + super(unauthorized, self).__init__(401, message, detail) + if isinstance(challenge, str): + challenge = [challenge] + self.challenge = challenge + + def handle(self, req): + for challenge in self.challenge: + req.ohead.add("WWW-Authenticate", challenge) + return super(unauthorized, self).handle(req) + +class forbidden(resp.httperror): + def __init__(self, message=None, detail=None): + super(forbidden, self).__init__(403, message, detail) + +def parsemech(req): + h = req.ihead.get("Authorization", None) + if h is None: + return None, None + p = h.find(" ") + if p < 0: + return None, None + return h[:p].strip().lower(), h[p + 1:].strip() + +def parsebasic(req): + mech, data = parsemech(req) + if mech != "basic": + return None, None + try: + raw = binascii.a2b_base64(data) + except binascii.Error: + return None, None + p = raw.find(":") + if p < 0: + return None, None + return raw[:p], raw[p + 1:] + +class basiccache(object): + cachetime = 300 + + def __init__(self, realm, authfn=None): + self._lock = threading.Lock() + self._cache = {} + self.realm = realm + if authfn is not None: + self.auth = authfn + + def _obscure(self, nm, pw): + dig = hashlib.sha256() + dig.update(self.realm) + dig.update(nm) + dig.update(pw) + return dig.digest() + + def check(self, req): + nm, pw = parsebasic(req) + if nm is None: + raise unauthorized("Basic Realm=\"%s\"" % self.realm) + pwh = self._obscure(nm, pw) + now = time.time() + with self._lock: + if (nm, pwh) in self._cache: + lock, atime, res, resob = self._cache[nm, pwh] + if now - atime < self.cachetime: + if res == "s": + return resob + elif res == "f": + raise resob + else: + lock = threading.Lock() + self._cache[nm, pwh] = (lock, now, None, None) + with lock: + try: + ret = self.auth(req, nm, pw) + except forbidden, exc: + with self._lock: + self._cache[nm, pwh] = (lock, now, "f", exc) + raise + if ret is None: + raise forbidden() + with self._lock: + self._cache[nm, pwh] = (lock, now, "s", ret) + return ret + + def auth(self, req, nm, pw): + raise Exception("authentication function neither supplied nor overridden") -- 2.11.0