[PATCH 0 of 1] Patch to fix a small "bug" and improve support for custom auth extensions.
Sune Foldager
cryo at cyanite.org
Mon Feb 9 06:52:49 CST 2009
Dirkjan wrote:
>> So why do this? Well, I have an extension (actually a patch right now) which wraps check_perms and does some
>> custom authorization. For that I need to send back 401 challenges. With this patch it's as simple as raising
>> an ErrorResponse with the correct WWW-Authenticate header :-).
> I'm curious to hear a little more about your authn/authz needs, as I'm
> thinking about abstracting hgweb's model in that department a bit.
I'll describe how our current authn/authz system works. It involves patching hgweb_mod (to hack check_perms, although it could probably just be wrapped instead) and url.py.
Authentication is done by a separate program, which obtains sends username and password to a webservice and obtains a token (a string). It writes it into a file in my homedir called .hgauth containing lines like "<hostname> = <username>:<password>", which in this case will be "<our repository host> = edlauth:<the token>", where 'edlauth' is a just a magic user name. This file is then [the Windows version of] chmod 600'ed.
url.py is patched to look for the .hgauth file if no password is given on the command line, look for a matching host, and use that username/password pair for HTTP auth. On the server, check_perms is patched/wrapped to check the token (via a local webservice), and send back 401 errors or challenges as appropriate.
I'd like to have the .hgauth patch included in some form upstream, but I don't know how people feel about it. I have included the current version of the patch below, although I am not claiming that it's suitable as a final-form or anything. Notice that I also patch url.py to include the auth information the first time around, instead of waiting for a challenge; urllib2 can't do that, so I set the header myself. It seems a waste of network time to wait for a challenge when the user provided auth information; it must be assumed that it will be needed! :-).
Comments and critique welcome :-).
--
Sune.
---- PATCH for url.py ------------
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -7,7 +7,7 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import urllib, urllib2, urlparse, httplib, os, re
+import urllib, urllib2, urlparse, httplib, os, re, base64
from i18n import _
import keepalive, util
@@ -246,6 +246,22 @@
return
raise
+def readauthtoken(url):
+ try:
+ url = url[url.index('://')+3:]
+ lines = file(os.path.expanduser('~/.hgauth')).readlines()
+ for line in lines:
+ if not line or line[0] == '#':
+ continue
+ prefix, token = line.split('=')
+ prefix = prefix.strip()
+ if prefix == '*' or url.startswith(prefix):
+ user, passwd = token.strip().split(':')
+ return user, passwd
+ return None, None
+ except:
+ return None, None
+
def getauthinfo(path):
scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
if not urlpath:
@@ -261,6 +277,8 @@
# urllib cannot handle URLs with embedded user or passwd
url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
urlpath, query, frag))
+ if not user:
+ user, passwd = readauthtoken(url)
if user:
netloc = host
if port:
@@ -296,6 +314,11 @@
# 1.0 here is the _protocol_ version
opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
+
+ # pre-emptively send auth header to reduce roundtrips
+ if authinfo:
+ opener.addheaders.append(('Authorization', 'Basic ' +
+ base64.b64encode(user + ':' + passwd)))
return opener
scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
More information about the Mercurial-devel
mailing list