/hgkeychain.py
Python | 134 lines | 82 code | 29 blank | 23 comment | 25 complexity | 9de18f66f66c10b34c37d4a37c656d52 MD5 | raw file
- #!/usr/bin/env python
- '''
- ### How to install:
- Drop _this_ file into your hgext directory. Location of this directory is install dependent.
- See: http://www.selenic.com/mercurial/wiki/index.cgi/UsingExtensions?highlight=%28%28WritingExtensions%29%29
- Edit your ~/.hgrc file to include:
- [extensions]
- MacOSXKeychain=
- ### Notes
- Tested on Mercurial 0.95 w/Python 2.5 on Mac OS 10.5
- Tested on Mercurial 1.1 w/Python 2.5 on Mac OS 10.5
- Tested on Mercurial 1.3 w/Python 2.6 on Mac OS 10.6
- '''
- import mercurial.demandimport
- from mercurial import (hg, repo, util)
- from mercurial.i18n import _
- try:
- from mercurial.url import passwordmgr
- except:
- from mercurial.httprepo import passwordmgr
- import logging
- import urlparse
- import urllib2
- import keychain
- import re
- ########################################################################################
- logger = logging.getLogger('hgkeychain')
- handler = logging.StreamHandler()
- formatter = logging.Formatter("hgkeychain: %(levelname)-5s|%(name)s| %(message)s")
- handler.setFormatter(formatter)
- logger.addHandler(handler)
- ########################################################################################
- #### From http://mail.python.org/pipermail/python-dev/2008-January/076194.html
- def monkeypatch_class(name, bases, namespace):
- assert len(bases) == 1, "Exactly one base class required"
- base = bases[0]
- for name, value in namespace.iteritems():
- if name != "__metaclass__":
- setattr(base, name, value)
- return base
- ########################################################################################
- _find_user_password = passwordmgr.find_user_password
- class MyHTTPPasswordMgr(passwordmgr):
- __metaclass__ = monkeypatch_class
- def prefixUrl(self, base_url, prefix):
- if not prefix or prefix == '*':
- return base_url
- scheme, hostpath = base_url.split('://', 1)
- p = prefix.split('://', 1)
- if len(p) > 1:
- prefix_host_path = p[1]
- else:
- prefix_host_path = prefix
- shortest_url = scheme + '://' + prefix_host_path
- return shortest_url
- def find_user_password(self, realm, authuri):
- logger.debug('find_user_password() %s %s', realm, (authuri if len(authuri) < 50 else authuri[:47] + '...'))
- authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(self, realm, authuri)
- theUsername, thePassword = authinfo
- if not hasattr(self, '_cache'):
- self._cache = {}
- auth = self.readauthtoken(authuri)
- keychainUri = authuri
- if auth:
- keychainUri = self.prefixUrl(authuri, auth.get('prefix'))
- logger.debug("Using URL for keychain: %s\n" % (keychainUri) )
- if not theUsername and auth:
- theUsername, thePassword = auth.get('username'), auth.get('password')
- if not theUsername:
- if not self.ui.interactive():
- raise util.Abort(_('hgkeychain: http authorization required'))
- self.ui.write(_("http authorization required\n"))
- self.ui.status(_("realm: %s\n") % realm)
- theUsername = self.ui.prompt(_("user:"), default=None)
- theKey = (realm, theUsername, keychainUri)
- if theKey in self._cache:
- return self._cache[theKey]
- if not thePassword:
- parsed_url = urlparse.urlparse(keychainUri)
- port = parsed_url.port if parsed_url.port else 0
- if parsed_url.scheme == 'https':
- protocol = keychain.kSecProtocolTypeHTTPS
- else:
- protocol = keychain.kSecProtocolTypeHTTP
- logger.info('Searching for username (%s) and url (%s) in keychain' % (theUsername, keychainUri))
- thePassword, theKeychainItem = keychain.FindInternetPassword(protocol = protocol, serverName = parsed_url.netloc, accountName = theUsername, port = port, path = parsed_url.path)
- if not thePassword:
- thePassword = self.ui.getpass(_('password for user \'%s\': ') % theUsername)
- if thePassword:
- logger.info('Storing username (%s) and url (%s) in keychain' % (theUsername, keychainUri))
- keychain.AddInternetPassword(protocol = protocol, serverName = parsed_url.netloc, accountName = theUsername, port = port, path = parsed_url.path, password = thePassword)
- if thePassword:
- self._cache[theKey] = (theUsername, thePassword)
- return theUsername, thePassword
- ########################################################################################
- cmdtable = dict()
- def uisetup(ui):
- theConfig = dict(ui.configitems('hgkeychain'))
- if 'logging' in theConfig:
- logger.setLevel(int(theConfig['logging']))