mercurial_keyring-0.6.1/0000775000175000017500000000000012223116305016320 5ustar marcinkmarcink00000000000000mercurial_keyring-0.6.1/mercurial_keyring.egg-info/0000775000175000017500000000000012223116305023525 5ustar marcinkmarcink00000000000000mercurial_keyring-0.6.1/mercurial_keyring.egg-info/zip-safe0000664000175000017500000000000112165502763025171 0ustar marcinkmarcink00000000000000 mercurial_keyring-0.6.1/mercurial_keyring.egg-info/PKG-INFO0000664000175000017500000001777412223116304024641 0ustar marcinkmarcink00000000000000Metadata-Version: 1.1 Name: mercurial-keyring Version: 0.6.1 Summary: Mercurial Keyring Extension Home-page: http://bitbucket.org/Mekk/mercurial_keyring Author: Marcin Kasperski Author-email: Marcin.Kasperski@mekk.waw.pl License: BSD Description: .. -*- mode: rst -*- ================= mercurial_keyring ================= ``mercurial_keyring`` is a Mercurial_ extension used to securely save HTTP and SMTP authentication passwords in password databases (Gnome Keyring, KDE KWallet, OSXKeyChain, specific solutions for Win32 and command line). This extension uses and wraps services of the keyring_ library. .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ================ The extension prompts for the password on the first pull/push (in case of HTTP) or first email (in case of SMTP), just like it is done by default, but saves the password. On successive runs it checks for the username in ``.hg/hgrc``, then for suitable password in the password database, and uses those credentials (if found). In case password turns out to be incorrect (either because it was invalid, or because it was changed on the server) or missing it just prompts the user again. Passwords are identified by the combination of username and remote address, so they can be reused between repositories if they access the same remote repository (or the same SMTP server). Installation ============ Prerequisites ------------- Install the keyring_ library: :: easy_install keyring (or ``pip keyring``). On Debian "Sid" the library can be also installed from the official archive (packages ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet``). Note: keyring >= 0.3 is strongly recommended, especially in case text backend is to be used. .. _this keyring fork: https://bitbucket.org/sborho/python-keyring-lib Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using individual file. To install as a package use ``easy_install``: :: easy_install mercurial_keyring and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``) using: :: [extensions] mercurial_keyring = To install using individual file, download the `mercurial_keyring.py`_ file, save it anywhere you like, and put the following in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``): :: [extensions] hgext.mercurial_keyring = /path/to/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ============================== The library should usually pick the most appropriate password backend without configuration. Still, if necessary, it can be configured using ``~/keyringrc.cfg`` file (``keyringrc.cfg`` in the home directory of the current user). Refer to keyring_ docs for more details. *I considered handling similar options in hgrc, but decided that single user may use more than one keyring-based script. Still, I am open to suggestions.* Repository configuration (HTTP) =============================== Edit repository-local ``.hg/hgrc`` and save there the remote repository path and the username, but do not save the password. For example: :: [paths] myremote = https://my.server.com/hgrepo/someproject [auth] myremote.schemes = http https myremote.prefix = my.server.com/hgrepo myremote.username = mekk Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://User@bitbucket.org/User/project_name/ If prefix is specified, it is used to identify the password (so all repositories with the same prefix and the same username will share the same password). Otherwise full repository URL is used for this purpose. Note: if both username and password are given in ``.hg/hgrc``, extension will use them without using the password database. If username is not given, extension will prompt for credentials every time, also without saving the password. Finally, if you are consistent about remote repository nicknames, you can configure the username in your `~/.hgrc` (`.hgrc` in your home directory). For example, write there:: [auth] acme.prefix = hg.acme.com/repositories acme.username = johnny acme.schemes = http https and as long as you will be using alias `acme` for repositories like `https://hg.acme.com/repositories/my_beautiful_app`, username `johnnny` will be used, and the same password reused. The advantage of this method is that it works also for `clone`. Repository configuration (SMTP) =============================== Edit either repository-local ``.hg/hgrc``, or ``~/.hgrc`` and set there all standard email and smtp properties, including SMTP username, but without SMTP password. For example: :: [email] method = smtp from = Joe Doe [smtp] host = smtp.gmail.com port = 587 username = JoeDoe@gmail.com tls = true Just as in case of HTTP, you *must* set username, but *must not* set password here to use the extension, in other cases it will revert to the default behavior. Usage ===== Configure the repository as above, then just ``hg pull``, ``hg push``, etc. You should be asked for the password only once (per every username and remote repository prefix or url combination). Similarly, for email, configure as above and just ``hg email``. Again, you will be asked for the password once (per every username and email server address combination). Implementation details ====================== The extension is monkey-patching the mercurial ``passwordmgr`` class to replace the find_user_password method. Detailed order of operations is described in the comments inside `the code`_. Development =========== Development is tracked on BitBucket, see http://bitbucket.org/Mekk/mercurial_keyring/ Additional notes ================ Information about this extension is also available on Mercurial Wiki: http://mercurial.selenic.com/wiki/KeyringExtension Keywords: mercurial hg keyring password Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: DFSG approved Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Version Control mercurial_keyring-0.6.1/mercurial_keyring.egg-info/top_level.txt0000664000175000017500000000002212223116304026250 0ustar marcinkmarcink00000000000000mercurial_keyring mercurial_keyring-0.6.1/mercurial_keyring.egg-info/SOURCES.txt0000664000175000017500000000046012223116304025410 0ustar marcinkmarcink00000000000000LICENSE.txt MANIFEST.in README.txt mercurial_keyring.py setup.py mercurial_keyring.egg-info/PKG-INFO mercurial_keyring.egg-info/SOURCES.txt mercurial_keyring.egg-info/dependency_links.txt mercurial_keyring.egg-info/requires.txt mercurial_keyring.egg-info/top_level.txt mercurial_keyring.egg-info/zip-safemercurial_keyring-0.6.1/mercurial_keyring.egg-info/requires.txt0000664000175000017500000000001412223116304026117 0ustar marcinkmarcink00000000000000keyring>=0.3mercurial_keyring-0.6.1/mercurial_keyring.egg-info/dependency_links.txt0000664000175000017500000000000112223116304027572 0ustar marcinkmarcink00000000000000 mercurial_keyring-0.6.1/mercurial_keyring.py0000664000175000017500000004545212223116153022420 0ustar marcinkmarcink00000000000000# -*- coding: utf-8 -*- # # mercurial_keyring: save passwords in password database # # Copyright (c) 2009 Marcin Kasperski # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # See README.txt for more details. ''' securely save HTTP and SMTP authentication details mercurial_keyring is a Mercurial extension used to securely save HTTP and SMTP authentication passwords in password databases (Gnome Keyring, KDE KWallet, OSXKeyChain, specific solutions for Win32 and command line). This extension uses and wraps services of the keyring library. ''' from mercurial import util from mercurial.i18n import _ try: from mercurial.url import passwordmgr except: from mercurial.httprepo import passwordmgr from mercurial import mail from urllib2 import AbstractBasicAuthHandler, AbstractDigestAuthHandler # mercurial.demandimport incompatibility workaround. # various keyring backends fail as they can't properly import helper # modules (as demandimport modifies python import behaviour). # If you get import errors with demandimport in backtrace, try # guessing what to block and extending the list below. from mercurial import demandimport for blocked_module in [ "gobject._gobject", "configparser", "json", "abc", "io", "keyring", "gdata.docs.service", "gdata.service", "types", "atom.http", "atom.http_interface", "atom.service", "atom.token_store", "ctypes", "secretstorage.exceptions", "fs.opener", ]: if blocked_module not in demandimport.ignore: demandimport.ignore.append(blocked_module) # Temporarily disable demandimport to make the need of extending # the list above less likely. if __import__ == demandimport._import: demandimport.disable() try: import keyring finally: demandimport.enable() else: import keyring from urlparse import urlparse import urllib2 import smtplib, socket import os KEYRING_SERVICE = "Mercurial" ############################################################ def monkeypatch_method(cls,fname=None): def decorator(func): local_fname = fname if local_fname is None: local_fname = func.__name__ setattr(func, "orig", getattr(cls, local_fname, None)) setattr(cls, local_fname, func) return func return decorator ############################################################ class PasswordStore(object): """ Helper object handling keyring usage (password save&restore, the way passwords are keyed in the keyring). """ def __init__(self): self.cache = dict() def get_http_password(self, url, username): return keyring.get_password(KEYRING_SERVICE, self._format_http_key(url, username)) def set_http_password(self, url, username, password): keyring.set_password(KEYRING_SERVICE, self._format_http_key(url, username), password) def clear_http_password(self, url, username): self.set_http_password(url, username, "") def _format_http_key(self, url, username): return "%s@@%s" % (username, url) def get_smtp_password(self, machine, port, username): return keyring.get_password( KEYRING_SERVICE, self._format_smtp_key(machine, port, username)) def set_smtp_password(self, machine, port, username, password): keyring.set_password( KEYRING_SERVICE, self._format_smtp_key(machine, port, username), password) def clear_smtp_password(self, machine, port, username): self.set_smtp_password(machine, port, username, "") def _format_smtp_key(self, machine, port, username): return "%s@@%s:%s" % (username, machine, str(port)) password_store = PasswordStore() ############################################################ def _debug(ui, msg): ui.debug("[HgKeyring] " + msg + "\n") def _debug_reply(ui, msg, url, user, pwd): _debug(ui, "%s. Url: %s, user: %s, passwd: %s" % ( msg, url, user, pwd and '*' * len(pwd) or 'not set')) ############################################################ class HTTPPasswordHandler(object): """ Actual implementation of password handling (user prompting, configuration file searching, keyring save&restore). Object of this class is bound as passwordmgr attribute. """ def __init__(self): self.pwd_cache = {} self.last_reply = None def find_auth(self, pwmgr, realm, authuri, req): """ Actual implementation of find_user_password - different ways of obtaining the username and password. """ ui = pwmgr.ui # If we are called again just after identical previous # request, then the previously returned auth must have been # wrong. So we note this to force password prompt (and avoid # reusing bad password indifinitely). after_bad_auth = (self.last_reply \ and (self.last_reply['realm'] == realm) \ and (self.last_reply['authuri'] == authuri) \ and (self.last_reply['req'] == req)) if after_bad_auth: _debug(ui, _("Working after bad authentication, cached passwords not used %s") % str(self.last_reply)) # Strip arguments to get actual remote repository url. base_url = self.canonical_url(authuri) # Extracting possible username (or password) # stored directly in repository url user, pwd = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password( pwmgr, realm, authuri) if user and pwd: _debug_reply(ui, _("Auth data found in repository URL"), base_url, user, pwd) self.last_reply = dict(realm=realm,authuri=authuri,user=user,req=req) return user, pwd # Loading .hg/hgrc [auth] section contents. If prefix is given, # it will be used as a key to lookup password in the keyring. auth_user, pwd, prefix_url = self.load_hgrc_auth(ui, base_url, user) if prefix_url: keyring_url = prefix_url else: keyring_url = base_url _debug(ui, _("Keyring URL: %s") % keyring_url) # Checking the memory cache (there may be many http calls per command) cache_key = (realm, keyring_url) if not after_bad_auth: cached_auth = self.pwd_cache.get(cache_key) if cached_auth: user, pwd = cached_auth _debug_reply(ui, _("Cached auth data found"), base_url, user, pwd) self.last_reply = dict(realm=realm,authuri=authuri,user=user,req=req) return user, pwd if auth_user: if user and (user != auth_user): raise util.Abort(_('mercurial_keyring: username for %s specified both in repository path (%s) and in .hg/hgrc/[auth] (%s). Please, leave only one of those' % (base_url, user, auth_user))) user = auth_user if pwd: self.pwd_cache[cache_key] = user, pwd _debug_reply(ui, _("Auth data set in .hg/hgrc"), base_url, user, pwd) self.last_reply = dict(realm=realm,authuri=authuri,user=user,req=req) return user, pwd else: _debug(ui, _("Username found in .hg/hgrc: %s") % user) # Loading password from keyring. # Only if username is known (so we know the key) and we are # not after failure (so we don't reuse the bad password). if user and not after_bad_auth: _debug(ui, _("Looking for password for user %s and url %s") % (user, keyring_url)) pwd = password_store.get_http_password(keyring_url, user) if pwd: self.pwd_cache[cache_key] = user, pwd _debug_reply(ui, _("Keyring password found"), base_url, user, pwd) self.last_reply = dict(realm=realm,authuri=authuri,user=user,req=req) return user, pwd else: _debug(ui, _("Password not present in the keyring")) # Is the username permanently set? fixed_user = (user and True or False) # Last resort: interactive prompt if not ui.interactive(): raise util.Abort(_('mercurial_keyring: http authorization required but program used in non-interactive mode')) if not fixed_user: ui.status(_("Username not specified in .hg/hgrc. Keyring will not be used.\n")) ui.write(_("http authorization required\n")) ui.status(_("realm: %s\n") % realm) if fixed_user: ui.write(_("user: %s (fixed in .hg/hgrc)\n" % user)) else: user = ui.prompt(_("user:"), default=None) pwd = ui.getpass(_("password: ")) if fixed_user: # Saving password to the keyring. # It is done only if username is permanently set. # Otherwise we won't be able to find the password so it # does not make much sense to preserve it _debug(ui, _("Saving password for %s to keyring") % user) password_store.set_http_password(keyring_url, user, pwd) # Saving password to the memory cache self.pwd_cache[cache_key] = user, pwd _debug_reply(ui, _("Manually entered password"), base_url, user, pwd) self.last_reply = dict(realm=realm,authuri=authuri,user=user,req=req) return user, pwd def load_hgrc_auth(self, ui, base_url, user): """ Loading [auth] section contents from local .hgrc Returns (username, password, prefix) tuple (every element can be None) """ # Theoretically 3 lines below should do: #auth_token = self.readauthtoken(base_url) #if auth_token: # user, pwd = auth.get('username'), auth.get('password') # Unfortunately they do not work, readauthtoken always return # None. Why? Because ui (self.ui of passwordmgr) describes the # *remote* repository, so does *not* contain any option from # local .hg/hgrc. # TODO: mercurial 1.4.2 is claimed to resolve this problem # (thanks to: http://hg.xavamedia.nl/mercurial/crew/rev/fb45c1e4396f) # so since this version workaround implemented below should # not be necessary. As it will take some time until people # migrate to >= 1.4.2, it would be best to implement # workaround conditionally. # Workaround: we recreate the repository object repo_root = ui.config("bundle", "mainreporoot") from mercurial.ui import ui as _ui local_ui = _ui(ui) if repo_root: local_ui.readconfig(os.path.join(repo_root, ".hg", "hgrc")) try: local_passwordmgr = passwordmgr(local_ui) auth_token = local_passwordmgr.readauthtoken(base_url) except AttributeError: try: # hg 1.8 import mercurial.url readauthforuri = mercurial.url.readauthforuri except (ImportError, AttributeError): # hg 1.9 import mercurial.httpconnection readauthforuri = mercurial.httpconnection.readauthforuri if readauthforuri.func_code.co_argcount == 3: # Since hg.0593e8f81c71 res = readauthforuri(local_ui, base_url, user) else: res = readauthforuri(local_ui, base_url) if res: group, auth_token = res else: auth_token = None if auth_token: username = auth_token.get('username') password = auth_token.get('password') prefix = auth_token.get('prefix') shortest_url = self.shortest_url(base_url, prefix) return username, password, shortest_url return None, None, None def shortest_url(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 canonical_url(self, authuri): """ Strips query params from url. Used to convert urls like https://repo.machine.com/repos/apps/module?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between to https://repo.machine.com/repos/apps/module """ parsed_url = urlparse(authuri) return "%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, parsed_url.path) ############################################################ @monkeypatch_method(passwordmgr) def find_user_password(self, realm, authuri): """ keyring-based implementation of username/password query for HTTP(S) connections Passwords are saved in gnome keyring, OSX/Chain or other platform specific storage and keyed by the repository url """ # Extend object attributes if not hasattr(self, '_pwd_handler'): self._pwd_handler = HTTPPasswordHandler() if hasattr(self, '_http_req'): req = self._http_req else: req = None return self._pwd_handler.find_auth(self, realm, authuri, req) @monkeypatch_method(AbstractBasicAuthHandler, "http_error_auth_reqed") def basic_http_error_auth_reqed(self, authreq, host, req, headers): self.passwd._http_req = req try: return basic_http_error_auth_reqed.orig(self, authreq, host, req, headers) finally: self.passwd._http_req = None @monkeypatch_method(AbstractDigestAuthHandler, "http_error_auth_reqed") def digest_http_error_auth_reqed(self, authreq, host, req, headers): self.passwd._http_req = req try: return digest_http_error_auth_reqed.orig(self, authreq, host, req, headers) finally: self.passwd._http_req = None ############################################################ def try_smtp_login(ui, smtp_obj, username, password): """ Attempts smtp login on smtp_obj (smtplib.SMTP) using username and password. Returns: - True if login succeeded - False if login failed due to the wrong credentials Throws Abort exception if login failed for any other reason. Immediately returns False if password is empty """ if not password: return False try: ui.note(_('(authenticating to mail server as %s)\n') % (username)) smtp_obj.login(username, password) return True except smtplib.SMTPException, inst: if inst.smtp_code == 535: ui.status(_("SMTP login failed: %s\n\n") % inst.smtp_error) return False else: raise util.Abort(inst) def keyring_supported_smtp(ui, username): """ keyring-integrated replacement for mercurial.mail._smtp Used only when configuration file contains username, but does not contain the password. Most of the routine below is copied as-is from mercurial.mail._smtp. The only changed part is marked with #>>>>> and #<<<<< markers """ local_hostname = ui.config('smtp', 'local_hostname') s = smtplib.SMTP(local_hostname=local_hostname) mailhost = ui.config('smtp', 'host') if not mailhost: raise util.Abort(_('no [smtp]host in hgrc - cannot send mail')) mailport = int(ui.config('smtp', 'port', 25)) ui.note(_('sending mail: smtp host %s, port %s\n') % (mailhost, mailport)) s.connect(host=mailhost, port=mailport) if ui.configbool('smtp', 'tls'): if not hasattr(socket, 'ssl'): raise util.Abort(_("can't use TLS: Python SSL support " "not installed")) ui.note(_('(using tls)\n')) s.ehlo() s.starttls() s.ehlo() #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> stored = password = password_store.get_smtp_password( mailhost, mailport, username) # No need to check whether password was found as try_smtp_login # just returns False if it is absent. while not try_smtp_login(ui, s, username, password): password = ui.getpass(_("Password for %s on %s:%d: ") % (username, mailhost, mailport)) if stored != password: password_store.set_smtp_password( mailhost, mailport, username, password) #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< def send(sender, recipients, msg): try: return s.sendmail(sender, recipients, msg) except smtplib.SMTPRecipientsRefused, inst: recipients = [r[1] for r in inst.recipients.values()] raise util.Abort('\n' + '\n'.join(recipients)) except smtplib.SMTPException, inst: raise util.Abort(inst) return send ############################################################ orig_smtp = mail._smtp @monkeypatch_method(mail) def _smtp(ui): """ build an smtp connection and return a function to send email This is the monkeypatched version of _smtp(ui) function from mercurial/mail.py. It calls the original unless username without password is given in the configuration. """ username = ui.config('smtp', 'username') password = ui.config('smtp', 'password') if username and not password: return keyring_supported_smtp(ui, username) else: return orig_smtp(ui) mercurial_keyring-0.6.1/LICENSE.txt0000664000175000017500000000262312142543556020162 0ustar marcinkmarcink00000000000000Copyright (c) 2009 Marcin Kasperski All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mercurial_keyring-0.6.1/MANIFEST.in0000664000175000017500000000001612142543556020067 0ustar marcinkmarcink00000000000000include *.txt mercurial_keyring-0.6.1/PKG-INFO0000664000175000017500000001777412223116305017435 0ustar marcinkmarcink00000000000000Metadata-Version: 1.1 Name: mercurial_keyring Version: 0.6.1 Summary: Mercurial Keyring Extension Home-page: http://bitbucket.org/Mekk/mercurial_keyring Author: Marcin Kasperski Author-email: Marcin.Kasperski@mekk.waw.pl License: BSD Description: .. -*- mode: rst -*- ================= mercurial_keyring ================= ``mercurial_keyring`` is a Mercurial_ extension used to securely save HTTP and SMTP authentication passwords in password databases (Gnome Keyring, KDE KWallet, OSXKeyChain, specific solutions for Win32 and command line). This extension uses and wraps services of the keyring_ library. .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ================ The extension prompts for the password on the first pull/push (in case of HTTP) or first email (in case of SMTP), just like it is done by default, but saves the password. On successive runs it checks for the username in ``.hg/hgrc``, then for suitable password in the password database, and uses those credentials (if found). In case password turns out to be incorrect (either because it was invalid, or because it was changed on the server) or missing it just prompts the user again. Passwords are identified by the combination of username and remote address, so they can be reused between repositories if they access the same remote repository (or the same SMTP server). Installation ============ Prerequisites ------------- Install the keyring_ library: :: easy_install keyring (or ``pip keyring``). On Debian "Sid" the library can be also installed from the official archive (packages ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet``). Note: keyring >= 0.3 is strongly recommended, especially in case text backend is to be used. .. _this keyring fork: https://bitbucket.org/sborho/python-keyring-lib Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using individual file. To install as a package use ``easy_install``: :: easy_install mercurial_keyring and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``) using: :: [extensions] mercurial_keyring = To install using individual file, download the `mercurial_keyring.py`_ file, save it anywhere you like, and put the following in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``): :: [extensions] hgext.mercurial_keyring = /path/to/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ============================== The library should usually pick the most appropriate password backend without configuration. Still, if necessary, it can be configured using ``~/keyringrc.cfg`` file (``keyringrc.cfg`` in the home directory of the current user). Refer to keyring_ docs for more details. *I considered handling similar options in hgrc, but decided that single user may use more than one keyring-based script. Still, I am open to suggestions.* Repository configuration (HTTP) =============================== Edit repository-local ``.hg/hgrc`` and save there the remote repository path and the username, but do not save the password. For example: :: [paths] myremote = https://my.server.com/hgrepo/someproject [auth] myremote.schemes = http https myremote.prefix = my.server.com/hgrepo myremote.username = mekk Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://User@bitbucket.org/User/project_name/ If prefix is specified, it is used to identify the password (so all repositories with the same prefix and the same username will share the same password). Otherwise full repository URL is used for this purpose. Note: if both username and password are given in ``.hg/hgrc``, extension will use them without using the password database. If username is not given, extension will prompt for credentials every time, also without saving the password. Finally, if you are consistent about remote repository nicknames, you can configure the username in your `~/.hgrc` (`.hgrc` in your home directory). For example, write there:: [auth] acme.prefix = hg.acme.com/repositories acme.username = johnny acme.schemes = http https and as long as you will be using alias `acme` for repositories like `https://hg.acme.com/repositories/my_beautiful_app`, username `johnnny` will be used, and the same password reused. The advantage of this method is that it works also for `clone`. Repository configuration (SMTP) =============================== Edit either repository-local ``.hg/hgrc``, or ``~/.hgrc`` and set there all standard email and smtp properties, including SMTP username, but without SMTP password. For example: :: [email] method = smtp from = Joe Doe [smtp] host = smtp.gmail.com port = 587 username = JoeDoe@gmail.com tls = true Just as in case of HTTP, you *must* set username, but *must not* set password here to use the extension, in other cases it will revert to the default behavior. Usage ===== Configure the repository as above, then just ``hg pull``, ``hg push``, etc. You should be asked for the password only once (per every username and remote repository prefix or url combination). Similarly, for email, configure as above and just ``hg email``. Again, you will be asked for the password once (per every username and email server address combination). Implementation details ====================== The extension is monkey-patching the mercurial ``passwordmgr`` class to replace the find_user_password method. Detailed order of operations is described in the comments inside `the code`_. Development =========== Development is tracked on BitBucket, see http://bitbucket.org/Mekk/mercurial_keyring/ Additional notes ================ Information about this extension is also available on Mercurial Wiki: http://mercurial.selenic.com/wiki/KeyringExtension Keywords: mercurial hg keyring password Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: DFSG approved Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Version Control mercurial_keyring-0.6.1/setup.py0000664000175000017500000000206012223116245020033 0ustar marcinkmarcink00000000000000 version = '0.6.1' try: from setuptools import setup, find_packages except ImportError: from ez_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages long_description = open("README.txt").read() setup( name = "mercurial_keyring", version = version, author = 'Marcin Kasperski', author_email = 'Marcin.Kasperski@mekk.waw.pl', url = 'http://bitbucket.org/Mekk/mercurial_keyring', description = 'Mercurial Keyring Extension', long_description = long_description, license = 'BSD', py_modules = ['mercurial_keyring'], keywords = "mercurial hg keyring password", classifiers = [ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: DFSG approved', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Version Control' ], install_requires = ['keyring>=0.3'], zip_safe = True, ) mercurial_keyring-0.6.1/setup.cfg0000664000175000017500000000007312223116305020141 0ustar marcinkmarcink00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 mercurial_keyring-0.6.1/README.txt0000664000175000017500000001332412131465652020033 0ustar marcinkmarcink00000000000000.. -*- mode: rst -*- ================= mercurial_keyring ================= ``mercurial_keyring`` is a Mercurial_ extension used to securely save HTTP and SMTP authentication passwords in password databases (Gnome Keyring, KDE KWallet, OSXKeyChain, specific solutions for Win32 and command line). This extension uses and wraps services of the keyring_ library. .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ================ The extension prompts for the password on the first pull/push (in case of HTTP) or first email (in case of SMTP), just like it is done by default, but saves the password. On successive runs it checks for the username in ``.hg/hgrc``, then for suitable password in the password database, and uses those credentials (if found). In case password turns out to be incorrect (either because it was invalid, or because it was changed on the server) or missing it just prompts the user again. Passwords are identified by the combination of username and remote address, so they can be reused between repositories if they access the same remote repository (or the same SMTP server). Installation ============ Prerequisites ------------- Install the keyring_ library: :: easy_install keyring (or ``pip keyring``). On Debian "Sid" the library can be also installed from the official archive (packages ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet``). Note: keyring >= 0.3 is strongly recommended, especially in case text backend is to be used. .. _this keyring fork: https://bitbucket.org/sborho/python-keyring-lib Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using individual file. To install as a package use ``easy_install``: :: easy_install mercurial_keyring and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``) using: :: [extensions] mercurial_keyring = To install using individual file, download the `mercurial_keyring.py`_ file, save it anywhere you like, and put the following in ``~/.hgrc`` (or ``/etc/mercurial/hgrc``): :: [extensions] hgext.mercurial_keyring = /path/to/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ============================== The library should usually pick the most appropriate password backend without configuration. Still, if necessary, it can be configured using ``~/keyringrc.cfg`` file (``keyringrc.cfg`` in the home directory of the current user). Refer to keyring_ docs for more details. *I considered handling similar options in hgrc, but decided that single user may use more than one keyring-based script. Still, I am open to suggestions.* Repository configuration (HTTP) =============================== Edit repository-local ``.hg/hgrc`` and save there the remote repository path and the username, but do not save the password. For example: :: [paths] myremote = https://my.server.com/hgrepo/someproject [auth] myremote.schemes = http https myremote.prefix = my.server.com/hgrepo myremote.username = mekk Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://User@bitbucket.org/User/project_name/ If prefix is specified, it is used to identify the password (so all repositories with the same prefix and the same username will share the same password). Otherwise full repository URL is used for this purpose. Note: if both username and password are given in ``.hg/hgrc``, extension will use them without using the password database. If username is not given, extension will prompt for credentials every time, also without saving the password. Finally, if you are consistent about remote repository nicknames, you can configure the username in your `~/.hgrc` (`.hgrc` in your home directory). For example, write there:: [auth] acme.prefix = hg.acme.com/repositories acme.username = johnny acme.schemes = http https and as long as you will be using alias `acme` for repositories like `https://hg.acme.com/repositories/my_beautiful_app`, username `johnnny` will be used, and the same password reused. The advantage of this method is that it works also for `clone`. Repository configuration (SMTP) =============================== Edit either repository-local ``.hg/hgrc``, or ``~/.hgrc`` and set there all standard email and smtp properties, including SMTP username, but without SMTP password. For example: :: [email] method = smtp from = Joe Doe [smtp] host = smtp.gmail.com port = 587 username = JoeDoe@gmail.com tls = true Just as in case of HTTP, you *must* set username, but *must not* set password here to use the extension, in other cases it will revert to the default behavior. Usage ===== Configure the repository as above, then just ``hg pull``, ``hg push``, etc. You should be asked for the password only once (per every username and remote repository prefix or url combination). Similarly, for email, configure as above and just ``hg email``. Again, you will be asked for the password once (per every username and email server address combination). Implementation details ====================== The extension is monkey-patching the mercurial ``passwordmgr`` class to replace the find_user_password method. Detailed order of operations is described in the comments inside `the code`_. Development =========== Development is tracked on BitBucket, see http://bitbucket.org/Mekk/mercurial_keyring/ Additional notes ================ Information about this extension is also available on Mercurial Wiki: http://mercurial.selenic.com/wiki/KeyringExtension