Mercurial > hg-stable
changeset 8333:89c80c3dc584
allow http authentication information to be specified in the configuration
author | Sune Foldager <cryo@cyanite.org> |
---|---|
date | Mon, 04 May 2009 20:26:27 +0200 |
parents | 3e544c074459 |
children | 0695288e8c37 |
files | doc/hgrc.5.txt mercurial/url.py tests/test-hgweb-auth.py tests/test-hgweb-auth.py.out |
diffstat | 4 files changed, 283 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/hgrc.5.txt Fri May 08 18:30:44 2009 +0200 +++ b/doc/hgrc.5.txt Mon May 04 20:26:27 2009 +0200 @@ -100,6 +100,45 @@ Mercurial "hgrc" file, the purpose of each section, its possible keys, and their possible values. +[[auth]] +auth: + Authentication credentials for HTTP authentication. + Each line has the following format: + + <name>.<argument> = <value> + + where <name> is used to group arguments into authentication entries. + Example: + + foo.prefix = hg.intevation.org/mercurial + foo.username = foo + foo.password = bar + foo.schemes = http https + + Supported arguments: + + prefix;; + Either '*' or a URI prefix with or without the scheme part. The + authentication entry with the longest matching prefix is used + (where '*' matches everything and counts as a match of length 1). + If the prefix doesn't include a scheme, the match is performed against + the URI with its scheme stripped as well, and the schemes argument, + q.v., is then subsequently consulted. + username;; + Username to authenticate with. + password;; + Optional. Password to authenticate with. If not given the user will be + prompted for it. + schemes;; + Optional. Space separated list of URI schemes to use this authentication + entry with. Only used if the prefix doesn't include a scheme. Supported + schemes are http and https. They will match static-http and static-https + respectively, as well. + Default: https. + + If no suitable authentication entry is found, the user is prompted for + credentials as usual if required by the remote. + [[decode]] decode/encode:: Filters for transforming files on checkout/checkin. This would
--- a/mercurial/url.py Fri May 08 18:30:44 2009 +0200 +++ b/mercurial/url.py Mon May 04 20:26:27 2009 +0200 @@ -105,24 +105,58 @@ self, realm, authuri) user, passwd = authinfo if user and passwd: + self._writedebug(user, passwd) return (user, passwd) - if not self.ui.interactive(): - raise util.Abort(_('http authorization required')) + user, passwd = self._readauthtoken(authuri) + if not user or not passwd: + if not self.ui.interactive(): + raise util.Abort(_('http authorization required')) - self.ui.write(_("http authorization required\n")) - self.ui.status(_("realm: %s\n") % realm) - if user: - self.ui.status(_("user: %s\n") % user) - else: - user = self.ui.prompt(_("user:"), default=None) + self.ui.write(_("http authorization required\n")) + self.ui.status(_("realm: %s\n") % realm) + if user: + self.ui.status(_("user: %s\n") % user) + else: + user = self.ui.prompt(_("user:"), default=None) - if not passwd: - passwd = self.ui.getpass() + if not passwd: + passwd = self.ui.getpass() self.add_password(realm, authuri, user, passwd) + self._writedebug(user, passwd) return (user, passwd) + def _writedebug(self, user, passwd): + msg = _('http auth: user %s, password %s\n') + self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) + + def _readauthtoken(self, uri): + # Read configuration + config = dict() + for key, val in self.ui.configitems('auth'): + group, setting = key.split('.', 1) + gdict = config.setdefault(group, dict()) + gdict[setting] = val + + # Find the best match + scheme, hostpath = uri.split('://', 1) + bestlen = 0 + bestauth = None, None + for auth in config.itervalues(): + prefix = auth.get('prefix') + if not prefix: continue + p = prefix.split('://', 1) + if len(p) > 1: + schemes, prefix = [p[0]], p[1] + else: + schemes = (auth.get('schemes') or 'https').split() + if (prefix == '*' or hostpath.startswith(prefix)) and \ + len(prefix) > bestlen and scheme in schemes: + bestlen = len(prefix) + bestauth = auth.get('username'), auth.get('password') + return bestauth + class proxyhandler(urllib2.ProxyHandler): def __init__(self, ui): proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb-auth.py Mon May 04 20:26:27 2009 +0200 @@ -0,0 +1,61 @@ +from mercurial import demandimport; demandimport.enable() +from mercurial import ui +from mercurial import url +from mercurial.error import Abort + +class myui(ui.ui): + def interactive(self): + return False + +origui = myui() + +def writeauth(items): + ui = origui.copy() + for name, value in items.iteritems(): + ui.setconfig('auth', name, value) + return ui + +def dumpdict(dict): + return '{' + ', '.join(['%s: %s' % (k, dict[k]) for k in sorted(dict.iterkeys())]) + '}' + +def test(auth): + print 'CFG:', dumpdict(auth) + prefixes = set() + for k in auth: + prefixes.add(k.split('.', 1)[0]) + for p in prefixes: + auth.update({p + '.username': p, p + '.password': p}) + + ui = writeauth(auth) + + def _test(uri): + print 'URI:', uri + try: + pm = url.passwordmgr(ui) + print ' ', pm.find_user_password('test', uri) + except Abort, e: + print 'abort' + + _test('http://example.org/foo') + _test('http://example.org/foo/bar') + _test('http://example.org/bar') + _test('https://example.org/foo') + _test('https://example.org/foo/bar') + _test('https://example.org/bar') + + +print '\n*** Test in-uri schemes\n' +test({'x.prefix': 'http://example.org'}) +test({'x.prefix': 'https://example.org'}) +test({'x.prefix': 'http://example.org', 'x.schemes': 'https'}) +test({'x.prefix': 'https://example.org', 'x.schemes': 'http'}) + +print '\n*** Test separately configured schemes\n' +test({'x.prefix': 'example.org', 'x.schemes': 'http'}) +test({'x.prefix': 'example.org', 'x.schemes': 'https'}) +test({'x.prefix': 'example.org', 'x.schemes': 'http https'}) + +print '\n*** Test prefix matching\n' +test({'x.prefix': 'http://example.org/foo', 'y.prefix': 'http://example.org/bar'}) +test({'x.prefix': 'http://example.org/foo', 'y.prefix': 'http://example.org/foo/bar'}) +test({'x.prefix': '*', 'y.prefix': 'https://example.org/bar'})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb-auth.py.out Mon May 04 20:26:27 2009 +0200 @@ -0,0 +1,139 @@ + +*** Test in-uri schemes + +CFG: {x.prefix: http://example.org} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('x', 'x') +URI: http://example.org/bar + ('x', 'x') +URI: https://example.org/foo + abort +URI: https://example.org/foo/bar + abort +URI: https://example.org/bar + abort +CFG: {x.prefix: https://example.org} +URI: http://example.org/foo + abort +URI: http://example.org/foo/bar + abort +URI: http://example.org/bar + abort +URI: https://example.org/foo + ('x', 'x') +URI: https://example.org/foo/bar + ('x', 'x') +URI: https://example.org/bar + ('x', 'x') +CFG: {x.prefix: http://example.org, x.schemes: https} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('x', 'x') +URI: http://example.org/bar + ('x', 'x') +URI: https://example.org/foo + abort +URI: https://example.org/foo/bar + abort +URI: https://example.org/bar + abort +CFG: {x.prefix: https://example.org, x.schemes: http} +URI: http://example.org/foo + abort +URI: http://example.org/foo/bar + abort +URI: http://example.org/bar + abort +URI: https://example.org/foo + ('x', 'x') +URI: https://example.org/foo/bar + ('x', 'x') +URI: https://example.org/bar + ('x', 'x') + +*** Test separately configured schemes + +CFG: {x.prefix: example.org, x.schemes: http} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('x', 'x') +URI: http://example.org/bar + ('x', 'x') +URI: https://example.org/foo + abort +URI: https://example.org/foo/bar + abort +URI: https://example.org/bar + abort +CFG: {x.prefix: example.org, x.schemes: https} +URI: http://example.org/foo + abort +URI: http://example.org/foo/bar + abort +URI: http://example.org/bar + abort +URI: https://example.org/foo + ('x', 'x') +URI: https://example.org/foo/bar + ('x', 'x') +URI: https://example.org/bar + ('x', 'x') +CFG: {x.prefix: example.org, x.schemes: http https} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('x', 'x') +URI: http://example.org/bar + ('x', 'x') +URI: https://example.org/foo + ('x', 'x') +URI: https://example.org/foo/bar + ('x', 'x') +URI: https://example.org/bar + ('x', 'x') + +*** Test prefix matching + +CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/bar} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('x', 'x') +URI: http://example.org/bar + ('y', 'y') +URI: https://example.org/foo + abort +URI: https://example.org/foo/bar + abort +URI: https://example.org/bar + abort +CFG: {x.prefix: http://example.org/foo, y.prefix: http://example.org/foo/bar} +URI: http://example.org/foo + ('x', 'x') +URI: http://example.org/foo/bar + ('y', 'y') +URI: http://example.org/bar + abort +URI: https://example.org/foo + abort +URI: https://example.org/foo/bar + abort +URI: https://example.org/bar + abort +CFG: {x.prefix: *, y.prefix: https://example.org/bar} +URI: http://example.org/foo + abort +URI: http://example.org/foo/bar + abort +URI: http://example.org/bar + abort +URI: https://example.org/foo + ('x', 'x') +URI: https://example.org/foo/bar + ('x', 'x') +URI: https://example.org/bar + ('y', 'y')