Mercurial > hg
changeset 8847:7951f385fcb7
url: support client certificate files over HTTPS (issue643)
This extends the httpshandler with the means to utilise the auth
section to provide it with a PEM encoded certificate key file and
certificate chain file. This works also with sites that both require
client certificate authentication and basic or digest password
authentication, although the latter situation may require the user to
enter the PEM password multiple times.
author | Henrik Stuart <hg@hstuart.dk> |
---|---|
date | Sat, 20 Jun 2009 10:58:57 +0200 |
parents | b30775386d40 |
children | 89b71acdac9a |
files | doc/hgrc.5.txt mercurial/url.py |
diffstat | 2 files changed, 46 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/hgrc.5.txt Sun Jun 07 20:31:38 2009 +0200 +++ b/doc/hgrc.5.txt Sat Jun 20 10:58:57 2009 +0200 @@ -142,6 +142,11 @@ foo.password = bar foo.schemes = http https + bar.prefix = secure.example.org + bar.key = path/to/file.key + bar.cert = path/to/file.cert + bar.schemes = https + Supported arguments: prefix;; @@ -152,10 +157,17 @@ against the URI with its scheme stripped as well, and the schemes argument, q.v., is then subsequently consulted. username;; - Username to authenticate with. + Optional. Username to authenticate with. If not given, and the + remote site requires basic or digest authentication, the user + will be prompted for it. password;; - Optional. Password to authenticate with. If not given the user + Optional. Password to authenticate with. If not given, and the + remote site requires basic or digest authentication, the user will be prompted for it. + key;; + Optional. PEM encoded client certificate key file. + cert;; + Optional. PEM encoded client certificate chain file. schemes;; Optional. Space separated list of URI schemes to use this authentication entry with. Only used if the prefix doesn't include
--- a/mercurial/url.py Sun Jun 07 20:31:38 2009 +0200 +++ b/mercurial/url.py Sat Jun 20 10:58:57 2009 +0200 @@ -109,7 +109,9 @@ return (user, passwd) if not user: - user, passwd = self._readauthtoken(authuri) + auth = self.readauthtoken(authuri) + if auth: + user, passwd = auth.get('username'), auth.get('password') if not user or not passwd: if not self.ui.interactive(): raise util.Abort(_('http authorization required')) @@ -132,7 +134,7 @@ msg = _('http auth: user %s, password %s\n') self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) - def _readauthtoken(self, uri): + def readauthtoken(self, uri): # Read configuration config = dict() for key, val in self.ui.configitems('auth'): @@ -143,7 +145,7 @@ # Find the best match scheme, hostpath = uri.split('://', 1) bestlen = 0 - bestauth = None, None + bestauth = None for auth in config.itervalues(): prefix = auth.get('prefix') if not prefix: continue @@ -155,7 +157,7 @@ if (prefix == '*' or hostpath.startswith(prefix)) and \ len(prefix) > bestlen and scheme in schemes: bestlen = len(prefix) - bestauth = auth.get('username'), auth.get('password') + bestauth = auth return bestauth class proxyhandler(urllib2.ProxyHandler): @@ -411,8 +413,32 @@ send = _gen_sendfile(httplib.HTTPSConnection) class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): + def __init__(self, ui): + keepalive.KeepAliveHandler.__init__(self) + urllib2.HTTPSHandler.__init__(self) + self.ui = ui + self.pwmgr = passwordmgr(self.ui) + def https_open(self, req): - return self.do_open(httpsconnection, req) + self.auth = self.pwmgr.readauthtoken(req.get_full_url()) + return self.do_open(self._makeconnection, req) + + def _makeconnection(self, host, port=443, *args, **kwargs): + keyfile = None + certfile = None + + if args: # key_file + keyfile = args.pop(0) + if args: # cert_file + certfile = args.pop(0) + + # if the user has specified different key/cert files in + # hgrc, we prefer these + if self.auth and 'key' in self.auth and 'cert' in self.auth: + keyfile = self.auth['key'] + certfile = self.auth['cert'] + + return httpsconnection(host, port, keyfile, certfile, *args, **kwargs) # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if # it doesn't know about the auth type requested. This can happen if @@ -460,7 +486,7 @@ ''' handlers = [httphandler()] if has_https: - handlers.append(httpshandler()) + handlers.append(httpshandler(ui)) handlers.append(proxyhandler(ui))