From d232167dc3af61944eeefbda4dcc5c4365853295 Mon Sep 17 00:00:00 2001 From: aiju Date: Mon, 9 May 2011 18:53:34 +0000 Subject: [PATCH] added HTTP Basic to hgfactotum --- sys/lib/python/factotum.py | 53 +++++++++++++++++++++++++++--- sys/lib/python/hgext/hgfactotum.py | 28 ++++++++++++++-- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/sys/lib/python/factotum.py b/sys/lib/python/factotum.py index 4971459c0..0a8ebcd3a 100644 --- a/sys/lib/python/factotum.py +++ b/sys/lib/python/factotum.py @@ -17,11 +17,14 @@ class Factotum: msg = 'start' for k, v in args.iteritems(): msg += ' ' + k + '=\'' + v + '\'' - self.f.write(msg) - ret = self.f.read(4096) + while True: + self.f.write(msg) + ret = self.f.read(4096) + if ret[:7] != "needkey": break + self.needkey(ret[8:]) if ret == "ok": return if ret[:5] == "error": raise FactotumError(ret[6:]) - raise FactotumError("unexpected " + ret) + raise FactotumError("start: unexpected " + ret) def needkey(self, string): subprocess.call(['/bin/auth/factotum', '-g', string]) def read(self): @@ -34,7 +37,7 @@ class Factotum: if ret[:3] == "ok ": return ret[3:] if ret[:5] == "error": raise FactotumError(ret[6:]) if ret[:5] == "phase": raise PhaseError(ret[6:]) - raise FactotumError("unexpected " + ret) + raise FactotumError("read: unexpected " + ret) def write(self, data): while True: self.f.write('write ' + data) @@ -45,6 +48,48 @@ class Factotum: if ret[:3] == "toosmall ": return int(ret[4:]) if ret[:5] == "error": raise FactotumError(ret[6:]) if ret[:5] == "phase": raise PhaseError(ret[6:]) + raise FactotumError("write: unexpected " + ret) + def attr(self): + self.f.write('attr') + ret = self.f.read(4096) + if ret[:5] == "error": raise FactotumError(ret[6:]) + if ret[:3] == "ok ": + dict = {} + ret = ret[3:] + mode = 0 + key = "" + value = "" + while ret != "": + if mode == 0: + if ret[0] == '=': + if ret[1] == '\'': + mode = 2 + ret = ret[1:] + else: + mode = 1 + else: + key += ret[0] + elif mode == 1: + if ret[0] == ' ': + dict[key] = value + key = "" + value = "" + mode = 0 + else: + value += ret[0] + elif mode == 2: + if ret[0] == '\'': + ret = ret[1:] + dict[key] = value + key = "" + value = "" + mode = 0 + else: + value += ret[0] + if ret != "": ret = ret[1:] + if key != "": + dict[key] = value + return dict raise FactotumError("unexpected " + ret) def close(self): self.f.close() diff --git a/sys/lib/python/hgext/hgfactotum.py b/sys/lib/python/hgext/hgfactotum.py index 2dbd6ca5d..b850e1e78 100644 --- a/sys/lib/python/hgext/hgfactotum.py +++ b/sys/lib/python/hgext/hgfactotum.py @@ -3,6 +3,26 @@ import mercurial.url import urllib2 import factotum +import base64 + +class factotumbasic(urllib2.BaseHandler): + def __init__(self, passmgr=None): + self.f = factotum.Factotum() + def http_error_401(self, req, fp, code, msg, headers): + host = urllib2.urlparse.urlparse(req.get_full_url())[1] + authreq = headers.get('www-authenticate', None) + if authreq == None: return None + authreq = authreq.split(' ', 1) + if authreq[0].lower() != 'basic': return None + chal = urllib2.parse_keqv_list(urllib2.parse_http_list(authreq[1])) + realm = chal['realm'] + self.f.start(proto="pass", host=host, realm=realm, role="client") + pw = self.f.read() + user = self.f.attr()["user"] + val = 'Basic %s' % base64.b64encode(user + ':' + pw).strip() + if req.headers.get('Authorization', None) == val: return None + req.add_header('Authorization', val) + return self.parent.open(req) class factotumdigest(urllib2.BaseHandler): auth_header = 'Authorization' @@ -22,16 +42,18 @@ class factotumdigest(urllib2.BaseHandler): realm = chal['realm'] nonce = chal['nonce'] if self.retried >= 6: - self.f.delkey(proto="httpdigest", realm=realm) - self.f.start(proto="httpdigest", role="client", realm=realm) + self.f.delkey(proto="httpdigest", realm=realm, host=host) + self.f.start(proto="httpdigest", role="client", realm=realm, host=host) self.f.write(nonce + ' ' + req.get_method() + ' ' + req.get_selector()) resp = self.f.read() + user = self.f.attr()["user"] self.f.close() - val = 'Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s", algorithm=MD5' % ("aiju", realm, nonce, req.get_selector(), resp) + val = 'Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s", algorithm=MD5' % (user, realm, nonce, req.get_selector(), resp) if req.headers.get('Authorization', None) == val: return None req.add_unredirected_header('Authorization', val) result = self.parent.open(req) self.retried = 0 return result +urllib2.HTTPBasicAuthHandler = factotumbasic mercurial.url.httpdigestauthhandler = factotumdigest