Mercurial > hg > mercurial-source
comparison mercurial/url.py @ 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 | 59acb9c7d90f |
children | 89b71acdac9a |
comparison
equal
deleted
inserted
replaced
8846:b30775386d40 | 8847:7951f385fcb7 |
---|---|
107 if user and passwd: | 107 if user and passwd: |
108 self._writedebug(user, passwd) | 108 self._writedebug(user, passwd) |
109 return (user, passwd) | 109 return (user, passwd) |
110 | 110 |
111 if not user: | 111 if not user: |
112 user, passwd = self._readauthtoken(authuri) | 112 auth = self.readauthtoken(authuri) |
113 if auth: | |
114 user, passwd = auth.get('username'), auth.get('password') | |
113 if not user or not passwd: | 115 if not user or not passwd: |
114 if not self.ui.interactive(): | 116 if not self.ui.interactive(): |
115 raise util.Abort(_('http authorization required')) | 117 raise util.Abort(_('http authorization required')) |
116 | 118 |
117 self.ui.write(_("http authorization required\n")) | 119 self.ui.write(_("http authorization required\n")) |
130 | 132 |
131 def _writedebug(self, user, passwd): | 133 def _writedebug(self, user, passwd): |
132 msg = _('http auth: user %s, password %s\n') | 134 msg = _('http auth: user %s, password %s\n') |
133 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) | 135 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) |
134 | 136 |
135 def _readauthtoken(self, uri): | 137 def readauthtoken(self, uri): |
136 # Read configuration | 138 # Read configuration |
137 config = dict() | 139 config = dict() |
138 for key, val in self.ui.configitems('auth'): | 140 for key, val in self.ui.configitems('auth'): |
139 group, setting = key.split('.', 1) | 141 group, setting = key.split('.', 1) |
140 gdict = config.setdefault(group, dict()) | 142 gdict = config.setdefault(group, dict()) |
141 gdict[setting] = val | 143 gdict[setting] = val |
142 | 144 |
143 # Find the best match | 145 # Find the best match |
144 scheme, hostpath = uri.split('://', 1) | 146 scheme, hostpath = uri.split('://', 1) |
145 bestlen = 0 | 147 bestlen = 0 |
146 bestauth = None, None | 148 bestauth = None |
147 for auth in config.itervalues(): | 149 for auth in config.itervalues(): |
148 prefix = auth.get('prefix') | 150 prefix = auth.get('prefix') |
149 if not prefix: continue | 151 if not prefix: continue |
150 p = prefix.split('://', 1) | 152 p = prefix.split('://', 1) |
151 if len(p) > 1: | 153 if len(p) > 1: |
153 else: | 155 else: |
154 schemes = (auth.get('schemes') or 'https').split() | 156 schemes = (auth.get('schemes') or 'https').split() |
155 if (prefix == '*' or hostpath.startswith(prefix)) and \ | 157 if (prefix == '*' or hostpath.startswith(prefix)) and \ |
156 len(prefix) > bestlen and scheme in schemes: | 158 len(prefix) > bestlen and scheme in schemes: |
157 bestlen = len(prefix) | 159 bestlen = len(prefix) |
158 bestauth = auth.get('username'), auth.get('password') | 160 bestauth = auth |
159 return bestauth | 161 return bestauth |
160 | 162 |
161 class proxyhandler(urllib2.ProxyHandler): | 163 class proxyhandler(urllib2.ProxyHandler): |
162 def __init__(self, ui): | 164 def __init__(self, ui): |
163 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy') | 165 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy') |
409 response_class = keepalive.HTTPResponse | 411 response_class = keepalive.HTTPResponse |
410 # must be able to send big bundle as stream. | 412 # must be able to send big bundle as stream. |
411 send = _gen_sendfile(httplib.HTTPSConnection) | 413 send = _gen_sendfile(httplib.HTTPSConnection) |
412 | 414 |
413 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): | 415 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler): |
416 def __init__(self, ui): | |
417 keepalive.KeepAliveHandler.__init__(self) | |
418 urllib2.HTTPSHandler.__init__(self) | |
419 self.ui = ui | |
420 self.pwmgr = passwordmgr(self.ui) | |
421 | |
414 def https_open(self, req): | 422 def https_open(self, req): |
415 return self.do_open(httpsconnection, req) | 423 self.auth = self.pwmgr.readauthtoken(req.get_full_url()) |
424 return self.do_open(self._makeconnection, req) | |
425 | |
426 def _makeconnection(self, host, port=443, *args, **kwargs): | |
427 keyfile = None | |
428 certfile = None | |
429 | |
430 if args: # key_file | |
431 keyfile = args.pop(0) | |
432 if args: # cert_file | |
433 certfile = args.pop(0) | |
434 | |
435 # if the user has specified different key/cert files in | |
436 # hgrc, we prefer these | |
437 if self.auth and 'key' in self.auth and 'cert' in self.auth: | |
438 keyfile = self.auth['key'] | |
439 certfile = self.auth['cert'] | |
440 | |
441 return httpsconnection(host, port, keyfile, certfile, *args, **kwargs) | |
416 | 442 |
417 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if | 443 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if |
418 # it doesn't know about the auth type requested. This can happen if | 444 # it doesn't know about the auth type requested. This can happen if |
419 # somebody is using BasicAuth and types a bad password. | 445 # somebody is using BasicAuth and types a bad password. |
420 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler): | 446 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler): |
458 construct an opener suitable for urllib2 | 484 construct an opener suitable for urllib2 |
459 authinfo will be added to the password manager | 485 authinfo will be added to the password manager |
460 ''' | 486 ''' |
461 handlers = [httphandler()] | 487 handlers = [httphandler()] |
462 if has_https: | 488 if has_https: |
463 handlers.append(httpshandler()) | 489 handlers.append(httpshandler(ui)) |
464 | 490 |
465 handlers.append(proxyhandler(ui)) | 491 handlers.append(proxyhandler(ui)) |
466 | 492 |
467 passmgr = passwordmgr(ui) | 493 passmgr = passwordmgr(ui) |
468 if authinfo is not None: | 494 if authinfo is not None: |