/hgkeychain.py
Python | 134 lines | 82 code | 29 blank | 23 comment | 32 complexity | 9de18f66f66c10b34c37d4a37c656d52 MD5 | raw file
1#!/usr/bin/env python 2 3''' 4### How to install: 5 6Drop _this_ file into your hgext directory. Location of this directory is install dependent. 7 8See: http://www.selenic.com/mercurial/wiki/index.cgi/UsingExtensions?highlight=%28%28WritingExtensions%29%29 9 10Edit your ~/.hgrc file to include: 11 12[extensions] 13MacOSXKeychain= 14 15### Notes 16Tested on Mercurial 0.95 w/Python 2.5 on Mac OS 10.5 17Tested on Mercurial 1.1 w/Python 2.5 on Mac OS 10.5 18Tested on Mercurial 1.3 w/Python 2.6 on Mac OS 10.6 19''' 20 21import mercurial.demandimport 22 23from mercurial import (hg, repo, util) 24from mercurial.i18n import _ 25 26try: 27 from mercurial.url import passwordmgr 28except: 29 from mercurial.httprepo import passwordmgr 30 31import logging 32import urlparse 33import urllib2 34import keychain 35import re 36 37######################################################################################## 38 39logger = logging.getLogger('hgkeychain') 40handler = logging.StreamHandler() 41formatter = logging.Formatter("hgkeychain: %(levelname)-5s|%(name)s| %(message)s") 42handler.setFormatter(formatter) 43logger.addHandler(handler) 44 45######################################################################################## 46 47#### From http://mail.python.org/pipermail/python-dev/2008-January/076194.html 48 49def monkeypatch_class(name, bases, namespace): 50 assert len(bases) == 1, "Exactly one base class required" 51 base = bases[0] 52 for name, value in namespace.iteritems(): 53 if name != "__metaclass__": 54 setattr(base, name, value) 55 return base 56 57######################################################################################## 58 59_find_user_password = passwordmgr.find_user_password 60 61class MyHTTPPasswordMgr(passwordmgr): 62 __metaclass__ = monkeypatch_class 63 64 def prefixUrl(self, base_url, prefix): 65 if not prefix or prefix == '*': 66 return base_url 67 scheme, hostpath = base_url.split('://', 1) 68 p = prefix.split('://', 1) 69 if len(p) > 1: 70 prefix_host_path = p[1] 71 else: 72 prefix_host_path = prefix 73 shortest_url = scheme + '://' + prefix_host_path 74 return shortest_url 75 76 def find_user_password(self, realm, authuri): 77 78 logger.debug('find_user_password() %s %s', realm, (authuri if len(authuri) < 50 else authuri[:47] + '...')) 79 80 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(self, realm, authuri) 81 theUsername, thePassword = authinfo 82 83 if not hasattr(self, '_cache'): 84 self._cache = {} 85 86 auth = self.readauthtoken(authuri) 87 keychainUri = authuri 88 if auth: 89 keychainUri = self.prefixUrl(authuri, auth.get('prefix')) 90 logger.debug("Using URL for keychain: %s\n" % (keychainUri) ) 91 92 if not theUsername and auth: 93 theUsername, thePassword = auth.get('username'), auth.get('password') 94 if not theUsername: 95 if not self.ui.interactive(): 96 raise util.Abort(_('hgkeychain: http authorization required')) 97 self.ui.write(_("http authorization required\n")) 98 self.ui.status(_("realm: %s\n") % realm) 99 theUsername = self.ui.prompt(_("user:"), default=None) 100 101 theKey = (realm, theUsername, keychainUri) 102 if theKey in self._cache: 103 return self._cache[theKey] 104 105 if not thePassword: 106 parsed_url = urlparse.urlparse(keychainUri) 107 port = parsed_url.port if parsed_url.port else 0 108 if parsed_url.scheme == 'https': 109 protocol = keychain.kSecProtocolTypeHTTPS 110 else: 111 protocol = keychain.kSecProtocolTypeHTTP 112 113 logger.info('Searching for username (%s) and url (%s) in keychain' % (theUsername, keychainUri)) 114 thePassword, theKeychainItem = keychain.FindInternetPassword(protocol = protocol, serverName = parsed_url.netloc, accountName = theUsername, port = port, path = parsed_url.path) 115 116 if not thePassword: 117 thePassword = self.ui.getpass(_('password for user \'%s\': ') % theUsername) 118 if thePassword: 119 logger.info('Storing username (%s) and url (%s) in keychain' % (theUsername, keychainUri)) 120 keychain.AddInternetPassword(protocol = protocol, serverName = parsed_url.netloc, accountName = theUsername, port = port, path = parsed_url.path, password = thePassword) 121 122 if thePassword: 123 self._cache[theKey] = (theUsername, thePassword) 124 125 return theUsername, thePassword 126 127######################################################################################## 128 129cmdtable = dict() 130 131def uisetup(ui): 132 theConfig = dict(ui.configitems('hgkeychain')) 133 if 'logging' in theConfig: 134 logger.setLevel(int(theConfig['logging']))