PageRenderTime 33ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/hgkeychain.py

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