PageRenderTime 1ms CodeModel.GetById 41ms app.highlight 12ms RepoModel.GetById 48ms app.codeStats 0ms

/hgkeychain.py

https://bitbucket.org/schwa/hgkeychain/
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']))