mercurial_keyring-1.3.0/0000775000175000017500000000000013562127441016327 5ustar marcinkmarcink00000000000000mercurial_keyring-1.3.0/.hgtags0000664000175000017500000000554213562127351017613 0ustar marcinkmarcink0000000000000093430018d00ee6cb78e5e72b2272ff7c5260b361 0.1.0 b176c215bd8c1c812ebe0fec39e7ab6acfd4cf07 0.1.1 62d2b5bbd611a5e203c3a0af165b01f52bcc2d71 0.2.0 756027411cc7f05fd8f40edc0bcfb88e28bf93a8 0.3.0 0f1b184b352ee73da40856cef57b5b33b9839e1d 0.3.1 b46cf9276f80f3e11485bf93c675a92a4ddb72d6 0.3.2 8224c263d919edbc49d8d3f7970bcecc0e970861 0.3.3 8224c263d919edbc49d8d3f7970bcecc0e970861 0.3.3 666f58e13c64ce54b701e3cef568c38ecf8acfec 0.3.3 e0c8034ca644c670b5baeceefe1ae4d00aca3936 0.4.0 e0c8034ca644c670b5baeceefe1ae4d00aca3936 0.4.0 86426d5318a8dd0c4b8df709556d068ff5027ae0 0.4.0 86f71f619b61e83ef5c3e14df6c67950bef27d3f 0.4.1 60444bd314a9d429cbc81043f933a57ccf659e4f 0.4.2 e73264f91c9d95ad73b67726e6ddc143afaa9a09 0.4.3 097b47069f24c4a2c9b9e460078f859f5e33415d 0.4.4 a6e455012d1406f694365155941aa7dc3dad5d11 0.4.5 a6e455012d1406f694365155941aa7dc3dad5d11 0.4.5 526bf3869f91c8f2955f3a04ca10ca82ca0ab1f0 0.4.5 0c2a941ceab8ede62677ce98fe4b445539375154 0.4.6 ba9fec5ddd133f67a9534ba0d33be0d6bca71119 0.5.0 eca7f4a18adf928a5f6c24fb3da15e5d8e182780 0.5.1 a1fb7c77a71d64bc77d9408e422a8970ec32832e 0.5.2 c3f5f264335e30dcf8ea45586e715f40e552ff3e 0.5.3 4ea8e9e17c5644bf7179188c4aac8d650251d430 0.5.4 4ea8e9e17c5644bf7179188c4aac8d650251d430 0.5.4 fcd68998229a587bf7b5a66822060eb8f16462cb 0.5.4 4e66cb922f494d1a187c9cc7efceafcfb92f3cd7 0.5.5 7ada7f4d00b49a1384ded33ec398045fc6a8a883 0.5.6 eced894db9a0cb14baae3383583ebd28d6305ce7 0.5.7 b0ed28cc8ba77bf71e26859abf0924e9d8b30176 0.6.0 8d86735b46752f67fd97b2291eaa3b93332a2786 0.6.1 758187e3c94fa2eec0dbc05d138069c2b1312f88 0.6.2 b49b9b0a6c86ae3c652c7263877fc7fd40712aae 0.6.3 7ccb5f2fcc83b4bf3703e4d22eb2925b322e20bb 0.6.4 091834ef2c2b7f74d6fc14ec37ffd17f94cf01ba 0.6.5 081ac9ed5bb856c9391e36a15b918f656ab6f4d3 0.6.6 081ac9ed5bb856c9391e36a15b918f656ab6f4d3 0.6.6 4e3f37414f4e3e0d27636848e263c2b9a051636c 0.6.6 6f333566425e1abddbce2d2a73978c9c203485ad 0.6.7 fb526db12b7b35f2595dc70be6b67cf40b886a6c 0.7.0 fb526db12b7b35f2595dc70be6b67cf40b886a6c 0.7.0 f5c3f8c1c1c14be4e2ab9ffdb41b592c87eb45f5 0.7.0 f5c3f8c1c1c14be4e2ab9ffdb41b592c87eb45f5 0.7.0 335c0c139fb3d7a00b800f5b58693cf83f5ce6e5 0.7.0 a894816abf1a7cad8a5837ad406fe08c93f533e1 0.7.1 c22dbb6b95b352c34dcabb84051b6599634ee179 0.8.0 b8afdf46be8ab496ee7040bcde10e3423a45331a 1.0.0 b92327b47c09f80d36c920aafcbec367c92ed78f 1.0.1 6bbaecea178efa4c4dee22bcd56e16717848b705 1.1.0 734e3a7cd0671a4dd01f28b9e670bd55e6247e09 1.1.1 87c891cda54f14d9f23d3ad008ada78670c98b1f 1.1.2 f116766d109a593b70ada966f062ea9ec521d926 1.1.3 47068cf2ff0499a0438a22b61d7c792f9f975705 1.1.4 eef262b1251ca41cf881972ee725e5ac9e1dd850 1.1.5 6facd03f86e223678a7801c643ddda4e641862b4 1.1.6 656fe41c1965602997032e8d8d00d63b57b825d2 1.1.7 678b0342e43894511601306c2a660cc678a29411 1.1.8 13ceb03ee4b3ba074ee0e12273559da2ba3ff50c 1.1.9 42e1da83e6dde50e12e8708bee649e5291a42b58 1.2.0 e01afc28bac525a133a6ff4fd59812333e4b756d 1.2.1 cfce371e028ddbdf0b18f0cb28bb9ed3089d028c 1.3.0 mercurial_keyring-1.3.0/mercurial_keyring.egg-info/0000775000175000017500000000000013562127441023534 5ustar marcinkmarcink00000000000000mercurial_keyring-1.3.0/mercurial_keyring.egg-info/requires.txt0000664000175000017500000000005613562127441026135 0ustar marcinkmarcink00000000000000keyring>=0.3 mercurial_extension_utils>=1.5.0 mercurial_keyring-1.3.0/mercurial_keyring.egg-info/dependency_links.txt0000664000175000017500000000000113562127441027602 0ustar marcinkmarcink00000000000000 mercurial_keyring-1.3.0/mercurial_keyring.egg-info/SOURCES.txt0000664000175000017500000000065513562127441025426 0ustar marcinkmarcink00000000000000.bitbucket.url .hgignore .hgtags HISTORY.txt LICENSE.txt MANIFEST.in README-devel.txt 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-safe tests/backend/auth_hg_serve.cfg tests/backend/auth_hg_serve.pymercurial_keyring-1.3.0/mercurial_keyring.egg-info/zip-safe0000664000175000017500000000000113042374262025162 0ustar marcinkmarcink00000000000000 mercurial_keyring-1.3.0/mercurial_keyring.egg-info/top_level.txt0000664000175000017500000000002213562127441026260 0ustar marcinkmarcink00000000000000mercurial_keyring mercurial_keyring-1.3.0/mercurial_keyring.egg-info/PKG-INFO0000664000175000017500000004517213562127441024642 0ustar marcinkmarcink00000000000000Metadata-Version: 1.1 Name: mercurial-keyring Version: 1.3.0 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; compile-command: "rst2html README.txt README.html" -*- ======================================================= 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, Windows Vault etc). With ``mercurial_keyring`` active, Mercurial remembers your passwords and reuses them without prompting (as if you stored them in ``.hgrc``), but password storage is reasonably secure. Actual password storage is implemented by the keyring_ library, this extension glues it to Mercurial. .. contents:: :local: :depth: 2 .. sectnum:: .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ======================================================= On your first pull or push to HTTP url (or first email sent via given SMTP server), you are prompted for the password, just like bare Mercurial does. But the password you entered is saved to appropriate password database. On successive runs, whenever the password is needed, ``mercurial_keyring`` checks for password in password database, and uses it without troubling you. In case password turns out to be incorrect (for example, because you changed it, or entered it incorrectly), ``mercurial_keyring`` prompts you again, and overwrites the password. You can use many passwords (for various remote urls). Saved passwords are identified by pair of username and url prefix. See below for information how to configure those properly. Installation ======================================================= Prerequisites ------------- This extension requires keyring_ and `mercurial_extension_utils`_ to work. In many cases both will be installed automatically while you install ``mercurial_keyring``, but you may need to control the process. The keyring_ library can usually be installed by:: pip install --user keyring (or ``easy_install keyring``), but on some systems it is preferable to use official distribution archive. For example, on Debian and Ubuntu, you may install ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet`` packages:: sudo apt-get install python-keyring python-keyring-gnome (this will save you the need to provide working compiler and various development libraries). The `mercurial_extension_utils`_ module is tiny Python-only module, which can be installed by:: pip install --user mercurial_extension_utils but in some cases (Windows…) requires more care. See `mercurial_extension_utils`_ documentation. Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using source clone. To install as a package:: pip install --user mercurial_keyring (or ``sudo pip install mercurial_keyring`` for system-wide installation) and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc`` or ``Mercurial.ini``) using:: [extensions] mercurial_keyring = To install using source clone, install keyring_ according to the instructions above, then clone:: hg clone https://bitbucket.org/Mekk/mercurial_keyring/ hg clone https://bitbucket.org/Mekk/mercurial-extension_utils/ and configure Mercurial using full path to the extension module:: [extensions] mercurial_keyring = /path/to/mercurial_keyring/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ======================================================= The most appropriate password backend should usually be picked without configuration (considering installed libraries, operating system, active desktop session). Still, if necessary, it can be configured using ``keyringrc.cfg`` file. Refer to keyring_ docs for more details. .. note:: With current (as I write) keyring (5.6), this file is (on Linux) located at ``~/.local/share/python_keyring/keyringrc.cfg`` and it's example content looks like:: [backend] default-keyring=keyring.backends.Gnome.Keyring # default-keyring=keyring.backends.kwallet.Keyring For list of known backends run ``pydoc keyring.backends`` or ``keyring --list-backends`` (which of those commands work, depends on the keyring_ version). ``hgrc`` configuration (HTTP) ======================================================= Mercurial Keyring uses standard Mercurial ``[auth]`` configuration to detect your username (on given remote) and url prefix. You are strongly advised to configure both. Without the username ``mercurial_keyring`` can't save or restore passwords, so it disables itself. Without url prefix ``mercurial_keyring`` works, but binds passwords to repository urls. That means you will have to (re)enter password for every repository cloned from given remote (and that there will be many copies of this password in secure storage). Repository level configuration ------------------------------------ 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.prefix = https://my.server.com/hgrepo myremote.username = John Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://John@my.server.com/hgrepo/someproject/ but is not recommended. Note that all repositories sharing the same ``prefix`` share the same password. Mercurial allows also for password in ``.hg/hgrc`` (either given by ``«prefix».password``, or embedded in url). If such password is found, Mercurial Keyring disables itself. Account-level configuration --------------------------- 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 bitbucket.prefix = https://bitbucket.org bitbucket.username = Mekk mydep.prefix = https://dev.acmeorg.com mydep.username = drmartin and as long as you use ``acme`` alias for repositories like ``https://hg.acme.com/repositories/my_beautiful_app``, username ``johnny`` will be used, and the same password reused. Similarly any ``hg push bitbucket`` will share the same password. With such config repository-level ``.hg/hgrc`` need only contain ``[paths]``. Additional advantage of this method is that it works also during `clone`. .. note:: Mercurial Keyring works well with `Path Pattern`_. On my setup I use prefix as above, and:: [path_pattern] bitbucket.local = ~/devel/{below} bitbucket.remote = https://bitbucket.org/Mekk/{below:/=-} so all my repositories understand ``hg push bitbucket`` without any repository-level configuration. ``hgrc`` 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 ====================================================== Saving and restoring passwords ------------------------------------------------------- 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). Checking password status (``hg keyring_check``) ------------------------------------------------------- The ``keyring_check`` command can be used to check whether/which password(s) are saved. It can be used in three ways: - without parameters, it prints info related to all HTTP paths defined for current repository (everything from ``hg paths`` that resolves to HTTP url):: hg keyring_check - given alias as param, it prints info about this alias:: hg keyring_check work - finally, any path can be checked:: hg keyring_check https://bitbucket.org/Mekk/mercurial_keyring Deleting saved password (``hg keyring_clear``) ------------------------------------------------------- The ``keyring_clear`` command removes saved password related to given path. It can be used in two ways: - given alias as param, it drops password used by this alias:: hg keyring_clear work - given full path, it drops password related to this path:: hg keyring_clear https://bitbucket.org/Mekk/mercurial_keyring Managing passwords using GUI tools ------------------------------------------------------ Many password backends provide GUI tools for password management, for example Gnome Keyring passwords can be managed using ``seahorse``, and KDE Wallet using ``kwalletmanager``. Those GUI tools can be used to review, edit, or delete saved passwords. Unfortunately, as I write, keyring_ library does not allow one to configure how/where exactly saved passwords are put in the hierarchy, and the place is not always intuitive. For example, in KDE Wallet, all passwords saved using ``mercurial_keyring`` show up in the folder named ``Python``. .. note:: This is slightly problematic in case ``mercurial_keyring`` is not the only program using keyring_ library. Passwords saved by another Python application or script (which also uses keyring_) will be put into the same place, and it may be unclear which password belongs to which program. To remedy this, ``mercurial_keyring`` applies slightly unusual labels of the form ``«username»@@«urlprefix»@Mercurial`` - for example my bitbucket password is labelled ``Mekk@@https://bitbucket.org@Mercurial``. 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`_. Frequent problems ======================================================= Most problems people face while using ``mercurial_keyring`` are in fact problems with ``keyring`` library and it's backends. In particular, those can manifest by: - technical errors mentioning sentences like ``No recommended backend was available. Install the keyrings.alt package…`` (or similar), - warnings like ``keyring: keyring backend doesn't seem to work…`` - password prompts on every action (= passwords not being saved). Those almost always mean that *natural* keyring backend for given desktop type doesn't work, or is not present at all. For example, some necessary runtime component can be down (say, you use Linux, but neither Gnome Keyring, nor KDE Wallet, is running). Or appropriate backend is not installed because it could not be build during keyring_ library installation (maybe because some required library was not present at the moment of keyring installation, or maybe because compiler as such is not present on the system). To diagnose such problems, try using ``keyring`` utility, as described on keyring_ documentation page, for example by:: keyring --list-backends keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser (of course using appropriate backend). If you miss the ``keyring`` command as such, try ``python -m keyring`` instead:: python -m keyring --list-backends python -m keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser python -m keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser If appropriate backend is missing (not listed), or doesn't work (second or third command fails), your keyring is broken. Try looking for further pointers in keyring_ documentation, that project mailing list, or issue tracker. Typically it will turn out, that you need to install some missing tool, or library, and reinstall keyring. .. note:: Depending on keyring_ version, installation of some dependency may resolve problem. For example (as of late 2018), I got KDE Wallet backend working with pip-installed keyring after:: pip install dbus-python Note also, that recent versions of keyring library (since version 12) use Python entrypoints to find available backends. Those are incompatible with some binary packaging methods (like ``py2app``) and may cause problems. In particular there were packaged installations of TortoiseHG which were unable to load keyring backends. See `#61 `_ for some more details. If ``keyring`` command works, but mercurial with mercurial_keyring does not, try enforcing proper backend (by means of ``keyringrc.cfg``, see above). Only if this doesn't help, there may be a bug in mercurial_keyring. By far easiest way to have properly working keyring is to use packaged binary version (like ``python-keyring`` Ubuntu package, or keyring bundled with TortoiseHG on some systems). If you pip-installed keyring and it doesn't work, you may consider removing it via ``pip uninstall keyring`` and looking for binary package instead. History ======================================================= See `HISTORY.txt`_. 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 Check also `other Mercurial extensions I wrote`_. .. _other Mercurial extensions I wrote: http://mekk.bitbucket.io/mercurial.html .. _HISTORY.txt: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/HISTORY.txt .. _TortoiseHg: http://tortoisehg.bitbucket.org/ .. _Mercurial: http://mercurial.selenic.com .. _mercurial_extension_utils: https://bitbucket.org/Mekk/mercurial-extension_utils/ .. _Path Pattern: https://bitbucket.org/Mekk/mercurial-path_pattern/ 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 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-1.3.0/LICENSE.txt0000664000175000017500000000262313042374243020152 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-1.3.0/.hgignore0000664000175000017500000000021213331136222020114 0ustar marcinkmarcink00000000000000-syntax: regexp \.pyc$ \.pyo$ ~$ ^\.\# ^\.(project|pydevproject)$ ^\.settings/ ^build/ ^dist/ ^mercurial_keyring\.egg-info/ ^README\.htmlmercurial_keyring-1.3.0/MANIFEST.in0000664000175000017500000000001613042374243020057 0ustar marcinkmarcink00000000000000include *.txt mercurial_keyring-1.3.0/README-devel.txt0000644000175000017500000000036313445703061021120 0ustar marcinkmarcink00000000000000 To test in devel virtual environment: pip install Mercurial==… pip install keyring pip install dbus-python (then install meu and keyring, and possibly other necessary extensions) dbus-python makes some Linux backends available. mercurial_keyring-1.3.0/setup.py0000664000175000017500000000247213562127351020046 0ustar marcinkmarcink00000000000000 VERSION = '1.3.0' # pylint: disable=unused-import 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', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Version Control' ], install_requires=[ 'keyring>=0.3', 'mercurial_extension_utils>=1.5.0', ], zip_safe=True, ) mercurial_keyring-1.3.0/setup.cfg0000664000175000017500000000004613562127441020150 0ustar marcinkmarcink00000000000000[egg_info] tag_build = tag_date = 0 mercurial_keyring-1.3.0/.bitbucket.url0000664000175000017500000000006313042374243021101 0ustar marcinkmarcink00000000000000https://Mekk@bitbucket.org/Mekk/mercurial_keyring/ mercurial_keyring-1.3.0/tests/0000775000175000017500000000000013562127441017471 5ustar marcinkmarcink00000000000000mercurial_keyring-1.3.0/tests/backend/0000775000175000017500000000000013562127441021060 5ustar marcinkmarcink00000000000000mercurial_keyring-1.3.0/tests/backend/auth_hg_serve.cfg0000664000175000017500000000002713042374243024360 0ustar marcinkmarcink00000000000000[paths] / = ../../../* mercurial_keyring-1.3.0/tests/backend/auth_hg_serve.py0000664000175000017500000000342613042374243024257 0ustar marcinkmarcink00000000000000# Extended version of Mercurial WSGI wrapper, adding # some minimal HTTP auth. from base64 import b64decode from wsgiref.simple_server import make_server from mercurial import demandimport; demandimport.enable() from mercurial.hgweb import hgweb PORT = 8080 CONFIG_PATH = 'auth_hg_serve.cfg' class TrivialAuth(object): """Authorizes if user==password + user starts from abc""" def __init__(self, application): self.application = application def __call__(self, environ, start_response): if self.is_authenticated(environ.get('HTTP_AUTHORIZATION')): return self.application(environ, start_response) return self.password_prompt(environ, start_response) def is_authenticated(self, header): if not header: return False _, encoded = header.split(None, 1) decoded = b64decode(encoded).decode('utf-8') username, password = decoded.split(':', 1) return username == password and username.startswith("abc") def password_prompt(self, environ, start_response): start_response( '401 Authentication Required', [ ('Content-Type', 'text/html'), ('WWW-Authenticate', 'Basic realm="HGLogin"'), ]) return [b'Please login'] def dummy_app(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'Hello, world!'] if __name__ == '__main__': # httpd = make_server('', PORT, TrivialAuth(dummy_app)) httpd = make_server('', PORT, TrivialAuth(hgweb(CONFIG_PATH))) # application = hgweb(config_path) # httpd = make_server('', 8080, TrivialAuth(application)) print('Serving on port 8080...') try: httpd.serve_forever() except KeyboardInterrupt: pass mercurial_keyring-1.3.0/mercurial_keyring.py0000644000175000017500000010102413562123003022376 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 passwords to encrypted storage mercurial_keyring securely saves HTTP and SMTP passwords in password databases (Gnome Keyring, KDE KWallet, OSXKeyChain, Win32 crypto services). The process is automatic. Whenever bare Mercurial just prompts for the password, Mercurial with mercurial_keyring enabled checks whether saved password is available first. If so, it is used. If not, you will be prompted for the password, but entered password will be saved for the future use. In case saved password turns out to be invalid (HTTP or SMTP login fails) it is dropped, and you are asked for current password. Actual password storage is implemented by Python keyring library, this extension glues those services to Mercurial. Consult keyring documentation for information how to configure actual password backend (by default keyring guesses, usually correctly, for example you get KDE Wallet under KDE, and Gnome Keyring under Gnome or Unity). ''' import socket import os import sys import re if sys.version_info[0] < 3: from urllib2 import ( HTTPPasswordMgrWithDefaultRealm, AbstractBasicAuthHandler, AbstractDigestAuthHandler) else: from urllib.request import ( HTTPPasswordMgrWithDefaultRealm, AbstractBasicAuthHandler, AbstractDigestAuthHandler) import smtplib from mercurial import util, sslutil, error from mercurial.i18n import _ from mercurial.url import passwordmgr from mercurial import mail from mercurial.mail import SMTPS, STARTTLS from mercurial import encoding from mercurial import ui as uimod # pylint: disable=invalid-name, line-too-long, protected-access, too-many-arguments ########################################################################### # Specific import trickery ########################################################################### def import_meu(): """ Convoluted import of mercurial_extension_utils, which helps TortoiseHg/Win setups. This routine and it's use below performs equivalent of from mercurial_extension_utils import monkeypatch_method but looks for some non-path directories. """ try: import mercurial_extension_utils except ImportError: my_dir = os.path.dirname(__file__) sys.path.extend([ # In the same dir (manual or site-packages after pip) my_dir, # Developer clone os.path.join(os.path.dirname(my_dir), "extension_utils"), # Side clone os.path.join(os.path.dirname(my_dir), "mercurial-extension_utils"), ]) try: import mercurial_extension_utils except ImportError: raise error.Abort(_("""Can not import mercurial_extension_utils. Please install this module in Python path. See Installation chapter in https://bitbucket.org/Mekk/mercurial_keyring/ for details (and for info about TortoiseHG on Windows, or other bundled Python).""")) return mercurial_extension_utils meu = import_meu() monkeypatch_method = meu.monkeypatch_method def import_keyring(): """ Importing keyring happens to be costly if wallet is slow, so we delay it until it is really needed. The routine below also works around various demandimport-related problems. """ # 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. mod, was_imported_now = meu.direct_import_ext( "keyring", [ "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", "win32ctypes.pywin32", "win32ctypes.pywin32.pywintypes", "win32ctypes.pywin32.win32cred", "pywintypes", "win32cred", ]) if was_imported_now: # Shut up warning about uninitialized logging by keyring meu.disable_logging("keyring") return mod ################################################################# # Actual implementation ################################################################# KEYRING_SERVICE = "Mercurial" 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): """ Checks whether password of username for url is available, returns it or None """ return self._read_password_from_keyring( self._format_http_key(url, username)) def set_http_password(self, url, username, password): """Saves password to keyring""" self._save_password_to_keyring( self._format_http_key(url, username), password) def clear_http_password(self, url, username): """Drops saved password""" self.set_http_password(url, username, b"") @staticmethod def _format_http_key(url, username): """Construct actual key for password identification""" # keyring expects str, mercurial feeds as here mostly with bytes key = "%s@@%s" % (meu.pycompat.sysstr(username), meu.pycompat.sysstr(url)) return key @staticmethod def _format_smtp_key(machine, port, username): """Construct key for SMTP password identification""" key = "%s@@%s:%s" % (meu.pycompat.sysstr(username), meu.pycompat.sysstr(machine), str(port)) return key def get_smtp_password(self, machine, port, username): """Checks for SMTP password in keyring, returns password or None""" return self._read_password_from_keyring( self._format_smtp_key(machine, port, username)) def set_smtp_password(self, machine, port, username, password): """Saves SMTP password to keyring""" self._save_password_to_keyring( self._format_smtp_key(machine, port, username), password) def clear_smtp_password(self, machine, port, username): """Drops saved SMTP password""" self.set_smtp_password(machine, port, username, "") @staticmethod def _read_password_from_keyring(pwdkey): """Physically read from keyring""" keyring = import_keyring() try: password = keyring.get_password(KEYRING_SERVICE, pwdkey) except Exception as err: ui = uimod.ui() ui.warn(meu.ui_string( "keyring: keyring backend doesn't seem to work, password can not be restored. Falling back to prompts. Error details: %s\n", err)) return b'' # Reverse recoding from next routine if isinstance(password, meu.pycompat.unicode): return encoding.tolocal(password.encode('utf-8')) return password @staticmethod def _save_password_to_keyring(pwdkey, password): """Physically write to keyring""" keyring = import_keyring() # keyring in general expects unicode. # Mercurial provides "local" encoding. See #33. # py3 adds even more fun as we get already unicode from getpass. # To handle those quirks, let go through encoding.fromlocal in case we # got bytestr here, this will handle both normal py2 and emergency py3 cases. if isinstance(password, bytes): password = encoding.fromlocal(password).decode('utf-8') try: keyring.set_password( KEYRING_SERVICE, pwdkey, password) except Exception as err: ui = uimod.ui() ui.warn(meu.ui_string( "keyring: keyring backend doesn't seem to work, password was not saved. Error details: %s\n", err)) password_store = PasswordStore() ############################################################ # Various utils ############################################################ class PwdCache(object): """Short term cache, used to preserve passwords if they are used twice during a command""" def __init__(self): self._cache = {} def store(self, realm, url, user, pwd): """Saves password""" cache_key = (realm, url, user) self._cache[cache_key] = pwd def check(self, realm, url, user): """Checks for cached password""" cache_key = (realm, url, user) return self._cache.get(cache_key) _re_http_url = re.compile(b'^https?://') def is_http_path(url): return bool(_re_http_url.search(url)) def make_passwordmgr(ui): """Constructing passwordmgr in a way compatible with various mercurials""" if hasattr(ui, 'httppasswordmgrdb'): return passwordmgr(ui, ui.httppasswordmgrdb) else: return passwordmgr(ui) ############################################################ # HTTP password management ############################################################ 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 = PwdCache() self.last_reply = None # Markers and also names used in debug notes. Password source SRC_URL = "repository URL" SRC_CFGAUTH = "hgrc" SRC_MEMCACHE = "temporary cache" SRC_URLCACHE = "urllib temporary cache" SRC_KEYRING = "keyring" def get_credentials(self, pwmgr, realm, authuri, skip_caches=False): """ Looks up for user credentials in various places, returns them and information about their source. Used internally inside find_auth and inside informative commands (thiis method doesn't cache, doesn't detect bad passwords etc, doesn't prompt interactively, doesn't store password in keyring). Returns: user, password, SRC_*, actual_url If not found, password and SRC is None, user can be given or not, url is always set """ ui = pwmgr.ui parsed_url, url_user, url_passwd = self.unpack_url(authuri) base_url = bytes(parsed_url) ui.debug(meu.ui_string('keyring: base url: %s, url user: %s, url pwd: %s\n', base_url, url_user, url_passwd and b'******' or b'')) # Extract username (or password) stored directly in url if url_user and url_passwd: return url_user, url_passwd, self.SRC_URL, base_url # Extract data from urllib (in case it was already stored) if isinstance(pwmgr, HTTPPasswordMgrWithDefaultRealm): urllib_user, urllib_pwd = \ HTTPPasswordMgrWithDefaultRealm.find_user_password( pwmgr, realm, authuri) else: urllib_user, urllib_pwd = pwmgr.passwddb.find_user_password( realm, authuri) if urllib_user and urllib_pwd: return urllib_user, urllib_pwd, self.SRC_URLCACHE, base_url actual_user = url_user or urllib_user # Consult configuration to normalize url to prefix, and find username # (and maybe password) auth_user, auth_pwd, keyring_url = self.get_url_config( ui, parsed_url, actual_user) if auth_user and actual_user and (actual_user != auth_user): raise error.Abort(_('keyring: username for %s specified both in repository path (%s) and in .hg/hgrc/[auth] (%s). Please, leave only one of those' % (base_url, actual_user, auth_user))) if auth_user and auth_pwd: return auth_user, auth_pwd, self.SRC_CFGAUTH, keyring_url actual_user = actual_user or auth_user if skip_caches: return actual_user, None, None, keyring_url # Check memory cache (reuse ) # Checking the memory cache (there may be many http calls per command) cached_pwd = self.pwd_cache.check(realm, keyring_url, actual_user) if cached_pwd: return actual_user, cached_pwd, self.SRC_MEMCACHE, keyring_url # Load from keyring. if actual_user: ui.debug(meu.ui_string("keyring: looking for password (user %s, url %s)\n", actual_user, keyring_url)) keyring_pwd = password_store.get_http_password(keyring_url, actual_user) if keyring_pwd: return actual_user, keyring_pwd, self.SRC_KEYRING, keyring_url return actual_user, None, None, keyring_url @staticmethod def prompt_interactively(ui, user, realm, url): """Actual interactive prompt""" if not ui.interactive(): raise error.Abort(_('keyring: http authorization required but program used in non-interactive mode')) if not user: ui.status(meu.ui_string("keyring: username not specified in hgrc (or in url). Password will not be saved.\n")) ui.write(meu.ui_string("http authorization required\n")) ui.status(meu.ui_string("realm: %s\n", realm)) ui.status(meu.ui_string("url: %s\n", url)) if user: ui.write(meu.ui_string("user: %s (fixed in hgrc or url)\n", user)) else: user = ui.prompt(meu.ui_string("user:"), default=None) pwd = ui.getpass(meu.ui_string("password: ")) return user, pwd def find_auth(self, pwmgr, realm, authuri, req): """ Actual implementation of find_user_password - different ways of obtaining the username and password. Returns pair username, password """ ui = pwmgr.ui after_bad_auth = self._after_bad_auth(ui, realm, authuri, req) # Look in url, cache, etc user, pwd, src, final_url = self.get_credentials( pwmgr, realm, authuri, skip_caches=after_bad_auth) if pwd: if src != self.SRC_MEMCACHE: self.pwd_cache.store(realm, final_url, user, pwd) self._note_last_reply(realm, authuri, user, req) ui.debug(meu.ui_string("keyring: Password found in %s\n", src)) return user, pwd # Last resort: interactive prompt user, pwd = self.prompt_interactively(ui, user, realm, final_url) if 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 ui.debug(meu.ui_string("keyring: Saving password for %s to keyring\n", user)) try: password_store.set_http_password(final_url, user, pwd) except Exception as e: keyring = import_keyring() if isinstance(e, keyring.errors.PasswordSetError): ui.traceback() ui.warn(meu.ui_string("warning: failed to save password in keyring\n")) else: raise e # Saving password to the memory cache self.pwd_cache.store(realm, final_url, user, pwd) self._note_last_reply(realm, authuri, user, req) ui.debug(meu.ui_string("keyring: Manually entered password\n")) return user, pwd def get_url_config(self, ui, parsed_url, user): """ Checks configuration to decide whether/which username, prefix, and password are configured for given url. Consults [auth] section. Returns tuple (username, password, prefix) containing elements found. username and password can be None (if unset), if prefix is not found, url itself is returned. """ from mercurial.httpconnection import readauthforuri ui.debug(meu.ui_string("keyring: checking for hgrc info about url %s, user %s\n", parsed_url, user)) res = readauthforuri(ui, bytes(parsed_url), user) # If it user-less version not work, let's try with added username to handle # both config conventions if (not res) and user: parsed_url.user = user res = readauthforuri(ui, bytes(parsed_url), user) parsed_url.user = None if res: group, auth_token = res else: auth_token = None if auth_token: username = auth_token.get(b'username') password = auth_token.get(b'password') prefix = auth_token.get(b'prefix') else: username = user password = None prefix = None password_url = self.password_url(bytes(parsed_url), prefix) ui.debug(meu.ui_string("keyring: Password url: %s, user: %s, password: %s (prefix: %s)\n", password_url, username, b'********' if password else b'', prefix)) return username, password, password_url def _note_last_reply(self, realm, authuri, user, req): """ Internal helper. Saves info about auth-data obtained, preserves them in last_reply, and returns pair user, pwd """ self.last_reply = dict(realm=realm, authuri=authuri, user=user, req=req) def _after_bad_auth(self, ui, realm, authuri, req): """ 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 indefinitely). This routine checks for this condition. """ if self.last_reply: if (self.last_reply['realm'] == realm) \ and (self.last_reply['authuri'] == authuri) \ and (self.last_reply['req'] == req): ui.debug(meu.ui_string( "keyring: Working after bad authentication, cached passwords not used %s\n", str(self.last_reply))) return True return False @staticmethod def password_url(base_url, prefix): """Calculates actual url identifying the password. Takes configured prefix under consideration (so can be shorter than repo url)""" if not prefix or prefix == b'*': return base_url scheme, hostpath = base_url.split(b'://', 1) p = prefix.split(b'://', 1) if len(p) > 1: prefix_host_path = p[1] else: prefix_host_path = prefix password_url = scheme + b'://' + prefix_host_path return password_url @staticmethod def unpack_url(authuri): """ Takes original url for which authentication is attempted and: - 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 - Extracts username and password, if present, and removes them from url (so prefix matching works properly) Returns url, user, password where url is mercurial.util.url object already stripped of all those params. """ # In case of py3, util.url expects bytes authuri = meu.pycompat.bytestr(authuri) # mercurial.util.url, rather handy url parser parsed_url = util.url(authuri) parsed_url.query = b'' parsed_url.fragment = None # Strip arguments to get actual remote repository url. # base_url = "%s://%s%s" % (parsed_url.scheme, parsed_url.netloc, # parsed_url.path) user = parsed_url.user passwd = parsed_url.passwd parsed_url.user = None parsed_url.passwd = None return parsed_url, user, passwd ############################################################ # Mercurial monkey-patching ############################################################ @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 """ # In sync with hg 5.0 assert isinstance(realm, (type(None), str)) assert isinstance(authuri, str) # 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): """Preserves current HTTP request so it can be consulted in find_user_password above""" 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): """Preserves current HTTP request so it can be consulted in find_user_password above""" self.passwd._http_req = req try: return digest_http_error_auth_reqed.orig(self, authreq, host, req, headers) finally: self.passwd._http_req = None ############################################################ # SMTP support ############################################################ 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 as inst: if inst.smtp_code == 535: ui.status(meu.ui_string("SMTP login failed: %s\n\n", inst.smtp_error)) return False else: raise error.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 critical changed part is marked with # >>>>> and # <<<<< markers, there are also some fixes which make the code working on various Mercurials (like parsebool import). """ try: from mercurial.utils.stringutil import parsebool except ImportError: from mercurial.utils import parsebool local_hostname = ui.config('smtp', 'local_hostname') tls = ui.config('smtp', 'tls', 'none') # backward compatible: when tls = true, we use starttls. starttls = tls == 'starttls' or parsebool(tls) smtps = tls == 'smtps' if (starttls or smtps) and not util.safehasattr(socket, 'ssl'): raise error.Abort(_("can't use TLS: Python SSL support not installed")) mailhost = ui.config('smtp', 'host') if not mailhost: raise error.Abort(_('smtp.host not configured - cannot send mail')) if getattr(sslutil, 'sslkwargs', None) is None: sslkwargs = None elif starttls or smtps: sslkwargs = sslutil.sslkwargs(ui, mailhost) else: sslkwargs = {} if smtps: ui.note(_('(using smtps)\n')) # mercurial 3.8 added a mandatory host arg if not sslkwargs: s = SMTPS(ui, local_hostname=local_hostname, host=mailhost) elif 'host' in SMTPS.__init__.__code__.co_varnames: s = SMTPS(sslkwargs, local_hostname=local_hostname, host=mailhost) else: s = SMTPS(sslkwargs, local_hostname=local_hostname) elif starttls: if not sslkwargs: s = STARTTLS(ui, local_hostname=local_hostname, host=mailhost) elif 'host' in STARTTLS.__init__.__code__.co_varnames: s = STARTTLS(sslkwargs, local_hostname=local_hostname, host=mailhost) else: s = STARTTLS(sslkwargs, local_hostname=local_hostname) else: s = smtplib.SMTP(local_hostname=local_hostname) if smtps: defaultport = 465 else: defaultport = 25 mailport = util.getport(ui.config('smtp', 'port', defaultport)) ui.note(_('sending mail: smtp host %s, port %s\n') % (mailhost, mailport)) s.connect(host=mailhost, port=mailport) if starttls: ui.note(_('(using starttls)\n')) s.ehlo() s.starttls() s.ehlo() if starttls or smtps: if getattr(sslutil, 'validatesocket', None): ui.note(_('(verifying remote certificate)\n')) sslutil.validatesocket(s.sock) # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 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 as inst: recipients = [r[1] for r in inst.recipients.values()] raise error.Abort('\n' + '\n'.join(recipients)) except smtplib.SMTPException as inst: raise error.Abort(inst) return send ############################################################ # SMTP monkeypatching ############################################################ @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 _smtp.orig(ui) ############################################################ # Custom commands ############################################################ cmdtable = {} command = meu.command(cmdtable) @command(b'keyring_check', [], _("keyring_check [PATH]"), optionalrepo=True) def cmd_keyring_check(ui, repo, *path_args, **opts): # pylint: disable=unused-argument """ Prints basic info (whether password is currently saved, and how is it identified) for given path. Can be run without parameters to show status for all (current repository) paths which are HTTP-like. """ defined_paths = [(name, url) for name, url in ui.configitems(b'paths')] if path_args: # Maybe parameter is an alias defined_paths_dic = dict(defined_paths) paths = [(path_arg, defined_paths_dic.get(path_arg, path_arg)) for path_arg in path_args] else: if not repo: ui.status(meu.ui_string("Url to check not specified. Either run ``hg keyring_check https://...``, or run the command inside some repository (to test all defined paths).\n")) return paths = [(name, url) for name, url in defined_paths] if not paths: ui.status(meu.ui_string("keyring_check: no paths defined\n")) return handler = HTTPPasswordHandler() ui.status(meu.ui_string("keyring password save status:\n")) for name, url in paths: if not is_http_path(url): if path_args: ui.status(meu.ui_string(" %s: non-http path (%s)\n", name, url)) continue user, pwd, source, final_url = handler.get_credentials( make_passwordmgr(ui), name, url) if pwd: ui.status(meu.ui_string( " %s: password available, source: %s, bound to user %s, url %s\n", name, source, user, final_url)) elif user: ui.status(meu.ui_string( " %s: password not available, once entered, will be bound to user %s, url %s\n", name, user, final_url)) else: ui.status(meu.ui_string( " %s: password not available, user unknown, url %s\n", name, final_url)) @command(b'keyring_clear', [], _('hg keyring_clear PATH-OR-ALIAS'), optionalrepo=True) def cmd_keyring_clear(ui, repo, path, **opts): # pylint: disable=unused-argument """ Drops password bound to given path (if any is saved). Parameter can be given as full url (``https://John@bitbucket.org``) or as the name of path alias (``bitbucket``). """ path_url = path for name, url in ui.configitems(b'paths'): if name == path: path_url = url break if not is_http_path(path_url): ui.status(meu.ui_string( "%s is not a http path (and %s can't be resolved as path alias)\n", path, path_url)) return handler = HTTPPasswordHandler() user, pwd, source, final_url = handler.get_credentials( make_passwordmgr(ui), path, path_url) if not user: ui.status(meu.ui_string("Username not configured for url %s\n", final_url)) return if not pwd: ui.status(meu.ui_string("No password is saved for user %s, url %s\n", user, final_url)) return if source != handler.SRC_KEYRING: ui.status(meu.ui_string("Password for user %s, url %s is saved in %s, not in keyring\n", user, final_url, source)) password_store.clear_http_password(final_url, user) ui.status(meu.ui_string("Password removed for user %s, url %s\n", user, final_url)) buglink = 'https://bitbucket.org/Mekk/mercurial_keyring/issues' mercurial_keyring-1.3.0/HISTORY.txt0000644000175000017500000002014113562124231020217 0ustar marcinkmarcink00000000000000 1.3.0 ~~~~~~~~~~~~ #33 Python3-based Mercurial installs are supported and should work - including sharing passwords with Python2 installations. Note: remember about installing proper keyring backend dependencies under py3 (like python-dbus). Those aren't shared! 1.2.1 ~~~~~~~~~~~~ Tested against hg 4.8 (no changes needed). 1.2.0 ~~~~~~~~~~~~ #62 Compatible with Mercurial 4.7 (some fixes were needed to avoid crashes while working in this version). Most important fixes were made in meu (so here we just depend on proper version), but there are also individual fixes here (related to smtp handling). #61 In case keyring library raises exceptions (like recent versions do when no backend is available), we don't crash mercurial anymore. mercurial_keyring emits the warning and falls back to normal password handling (propts). Refreshing smtp monkeypatch to be a bit more like modern mercurials (in particular, obligatory cert falidaton). 1.1.9 ~~~~~~~~~~~~ 4.6-compatibility, 4.5-testing. 1.1.8 ~~~~~~~~~~~~~ Updated links after bitbucket changes. 1.1.7 ~~~~~~~~~~~~~~~~~~ #52 hg keyring_check and hg keyring_clear did not work since Mercurial 3.9 (incompatibility caused by commit 2c019aac6b99, introducing passwdb). 1.1.6 ~~~~~~~~~~~~~~~~~~ Fixed NameError showing up in some password saving scenarios, in particular in case of password save failures (thanks to Andrew Taumoefolau for reporting and fixing). 1.1.5 ~~~~~~~~~~~~~~~~~~ Mercurial 3.9 compatibility. 1.1.4 ~~~~~~~~~~~~~~~~~~ Gracefully handle failures to save passwords - they are reported as warnings, but don't break the operation being executed. Compatibility fixes for upcoming 3.9 release (which changes SSL API noticeably, what impact SMTP passwords handling in mercurial_keyring). 1.1.3 ~~~~~~~~~~~~~~~~~~ Mercurial 3.8 compatibility for email over SSL/TLS (SMTPS/STARTTLS constructors changed). Should not spoil older versions. 1.1.2 ~~~~~~~~~~~~~~~~~~ The keyring_check and keyring_clear commands can be run outside repository (if given some path as parameter). Fixed some messages. README updates (a few language fixes, added note about GUI tools). 1.1.1 ~~~~~~~~~~~~~~~~~~ #49 Fixed the bug due to url-stored usernames did not work (introduced in 1.0.0 and not completely fixed in 1.0.1). #50 Bad doc url in error message 1.1.0 ~~~~~~~~~~~~~~~~~~ Forward compatibility for Mercurial 3.8 (should not break old mercurials) 1.0.1 ~~~~~~~~~~~~~~~~~~ URLs containing usernames (https://John@some.service/somewhat) were not working unless username was also configured separately (username presence in url was not detected properly). Liberated prefix matching, path https://John@some.service/somewhat can be matched both against prefix https://some.service and against https://John@some.service. That mostly matches what mercurial itself does. 1.0.0 ~~~~~~~~~~~~~~~~~~ Added hg keyring_check and hg keyring_clear PATH-OR-ALIAS commands Removed obsolete workarounds (compatibility for very old Mercurials - some for pre-1.0, some for 1.4, some for 1.8/1.9). Mercurial 2.0 is now required. Improved information about path prefix. In particular it is shown whenever user is asked for password, for example: hg pull bitbucket http authorization required realm: BitBucket url: https://bitbucket.org/Mekk user: Mekk (fixed in hgrc or url) password: Improved README. Improved debug information. 0.8.0 ~~~~~~~~~~~~~~~~~~ Module is simplified a bit, but requires mercurial_extension_utils. Debug messages are prefixed with keyring: not [HgKeyring] 0.7.1 ~~~~~~~~~~~~~~~~~~ #48 NullHandler import failure no longer breaks the extension. May help python 2.6 compatibility. 0.7.0 ~~~~~~~~~~~~~~~~~~~ Delaying keyring module import until passwords are really needed. It can noticeably improve Mercurial (non pull/push) performance in some cases (no longer slow hg status because D-Bus is busy an keyring tries to activate KDE Wallet through it…). 0.6.7 ~~~~~~~~~~~~~~~~~ #46 Fixed syntax of smtp.tls configuration setting (current Mercurials doesn't handle "true" anymore, TortoiseHG crashed with mercurial keyring enabled while currently recommended starttls/smtps/none values were in use). 0.6.6 ~~~~~~~~~~~~~~~~~ #44 Handling some more mercurial versions in demandimport-detection logic. 0.6.5 ~~~~~~~~~~~~~~~~~ #36 Shutting up warning about no logging handlers. 0.6.4 ~~~~~~~~~~~~~~~~~ #44 Pre-2.9.1 Mercurials compatibility (probing for active demandimport differently). 0.6.3 ~~~~~~~~~~~~~~~~~ #41 Fix for incorrect demandimport activity check logic, which could cause various problems with imports after mercurial_keyring is imported. 0.6.2 ~~~~~~~~~~~~~~~~~ #33 Fix for UnicodeDecodeErrors happening on some backends (especially Vault) when passwords with non-ascii characters are in use and native locale is not utf-8. Passwords are no longer saved to keyring backends as-entered, they are now decoded from local encoding (whichever is detected by Mercurial), then encoded to unicode. 0.6.1 ~~~~~~~~~~~~~~~~~ #30 Yet another demandimport conflict fixed. 0.6.0 ~~~~~~~~~~~~~~~~~ #28 Disable demandimport completely during keyring import. Mayhaps it will resolve (most) demandimport conflict errors. 0.5.7 ~~~~~~~~~~~~~~~~~ #27 Some more demandimport ignores. 0.5.6 ~~~~~~~~~~~~~~~~~ #24, #25 Demandimport fixes (import failures in specific cases). Better way of demandimport-ignoring modules. In particular, we append more of them if gobject happens to be on the list. 0.5.5 ~~~~~~~~~~~~~~~~~ Fix for gnome keyring import problems. 0.5.4 ~~~~~~~~~~~~~~~~~ #22 Some more demandimport ignores (fix import failures). SMTP password was not cleared properly (after detecting that it is invalid). Clarified license to be modified BSD style license. 0.5.3 ~~~~~~~~~~~~~~~~~ Remove useless import which caused problems on Mercurial 2.3 when demandimport was not enabled 0.5.1 ~~~~~~~~~~~~~~~~~ Add help text to output for hg help. 0.5.0 ~~~~~~~~~~~~~~~~~ Improved bad password detection. Internally: extension is now able to properly differentiate between an authentication failure and a new request to the same url. Fixes in debug message Further debug messages patching Improving debug messages handling. Mercurial Keyring debug messages are now prefixed with [HgKeyring] to make distinguishing them easier 0.4.6 ~~~~~~~~~~~~~~~~~ More compatibility (changed signature of httpconnection.readauthforuri , introduced post Mercurial 1.9 - since hg.0593e8f81c71) Fix compatibility code which did not work due to demandimport issues (attempts to catch ImportErrors on "from mercurial.url import readauthforuri" were not working properly). 0.4.5 ~~~~~~~~~~~~~~~~~ Mercurial 1.9 compatibility (readauthforuri has been moved into new httpconnection module). 0.4.4 ~~~~~~~~~~~~~~~~~ Mercurial 1.8 compatibility (passwordmgr.readauthtoken() has been moved into mercurial.url.readauthforuri). 0.4.3 ~~~~~~~~~~~~~~~~~ Keyring fork no longer is needed as keyring releases are available again. Workaround for gnomekeyring mercurial.demandimport incompatibility: mercurial.demandimport, which is enabled while in a mercurial extensions, prevents the correct import of gobject._gobject and consequently doesn't allow the loading of the gnomekeyring module, which can be used by keyring. This just adds the proper module to demandimport ignore list. 0.4.2 ~~~~~~~~~~~~~~~~~ No longer raising an error when username is specified both in ~/.hgrc and /.hg/hgrc if it is the same in both places. Docs recommend sborho keyring fork. 0.4.1 ~~~~~~~~~~~~~~~~~ Some tweaks and docs related to prefix handling. Explicit information that keyring is not used due to lack of username. 0.4.0 ~~~~~~~~~~~~~~~~~ Store and lookup prefix from [auth] so that password is shared amongst shared auth entries 0.3.3 ~~~~~~~~~~~~~~~~~ Better error message 0.3.2 ~~~~~~~~~~~~~~~~~ Doc tweaks 0.3.1 ~~~~~~~~~~~~~~~~~ Introduced and documented PyPi package, added setup.py 0.2.0 ~~~~~~~~~~~~~~~~~ Added handling of SMTP passwords (tested on patchbomb extension but should work on anything what utilizes mercurial.mail) Docstrings mention Debian keyring packages. 0.1.1 ~~~~~~~~~~~~~~~~~ Initial public release mercurial_keyring-1.3.0/PKG-INFO0000664000175000017500000004517213562127441017435 0ustar marcinkmarcink00000000000000Metadata-Version: 1.1 Name: mercurial_keyring Version: 1.3.0 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; compile-command: "rst2html README.txt README.html" -*- ======================================================= 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, Windows Vault etc). With ``mercurial_keyring`` active, Mercurial remembers your passwords and reuses them without prompting (as if you stored them in ``.hgrc``), but password storage is reasonably secure. Actual password storage is implemented by the keyring_ library, this extension glues it to Mercurial. .. contents:: :local: :depth: 2 .. sectnum:: .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ======================================================= On your first pull or push to HTTP url (or first email sent via given SMTP server), you are prompted for the password, just like bare Mercurial does. But the password you entered is saved to appropriate password database. On successive runs, whenever the password is needed, ``mercurial_keyring`` checks for password in password database, and uses it without troubling you. In case password turns out to be incorrect (for example, because you changed it, or entered it incorrectly), ``mercurial_keyring`` prompts you again, and overwrites the password. You can use many passwords (for various remote urls). Saved passwords are identified by pair of username and url prefix. See below for information how to configure those properly. Installation ======================================================= Prerequisites ------------- This extension requires keyring_ and `mercurial_extension_utils`_ to work. In many cases both will be installed automatically while you install ``mercurial_keyring``, but you may need to control the process. The keyring_ library can usually be installed by:: pip install --user keyring (or ``easy_install keyring``), but on some systems it is preferable to use official distribution archive. For example, on Debian and Ubuntu, you may install ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet`` packages:: sudo apt-get install python-keyring python-keyring-gnome (this will save you the need to provide working compiler and various development libraries). The `mercurial_extension_utils`_ module is tiny Python-only module, which can be installed by:: pip install --user mercurial_extension_utils but in some cases (Windows…) requires more care. See `mercurial_extension_utils`_ documentation. Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using source clone. To install as a package:: pip install --user mercurial_keyring (or ``sudo pip install mercurial_keyring`` for system-wide installation) and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc`` or ``Mercurial.ini``) using:: [extensions] mercurial_keyring = To install using source clone, install keyring_ according to the instructions above, then clone:: hg clone https://bitbucket.org/Mekk/mercurial_keyring/ hg clone https://bitbucket.org/Mekk/mercurial-extension_utils/ and configure Mercurial using full path to the extension module:: [extensions] mercurial_keyring = /path/to/mercurial_keyring/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ======================================================= The most appropriate password backend should usually be picked without configuration (considering installed libraries, operating system, active desktop session). Still, if necessary, it can be configured using ``keyringrc.cfg`` file. Refer to keyring_ docs for more details. .. note:: With current (as I write) keyring (5.6), this file is (on Linux) located at ``~/.local/share/python_keyring/keyringrc.cfg`` and it's example content looks like:: [backend] default-keyring=keyring.backends.Gnome.Keyring # default-keyring=keyring.backends.kwallet.Keyring For list of known backends run ``pydoc keyring.backends`` or ``keyring --list-backends`` (which of those commands work, depends on the keyring_ version). ``hgrc`` configuration (HTTP) ======================================================= Mercurial Keyring uses standard Mercurial ``[auth]`` configuration to detect your username (on given remote) and url prefix. You are strongly advised to configure both. Without the username ``mercurial_keyring`` can't save or restore passwords, so it disables itself. Without url prefix ``mercurial_keyring`` works, but binds passwords to repository urls. That means you will have to (re)enter password for every repository cloned from given remote (and that there will be many copies of this password in secure storage). Repository level configuration ------------------------------------ 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.prefix = https://my.server.com/hgrepo myremote.username = John Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://John@my.server.com/hgrepo/someproject/ but is not recommended. Note that all repositories sharing the same ``prefix`` share the same password. Mercurial allows also for password in ``.hg/hgrc`` (either given by ``«prefix».password``, or embedded in url). If such password is found, Mercurial Keyring disables itself. Account-level configuration --------------------------- 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 bitbucket.prefix = https://bitbucket.org bitbucket.username = Mekk mydep.prefix = https://dev.acmeorg.com mydep.username = drmartin and as long as you use ``acme`` alias for repositories like ``https://hg.acme.com/repositories/my_beautiful_app``, username ``johnny`` will be used, and the same password reused. Similarly any ``hg push bitbucket`` will share the same password. With such config repository-level ``.hg/hgrc`` need only contain ``[paths]``. Additional advantage of this method is that it works also during `clone`. .. note:: Mercurial Keyring works well with `Path Pattern`_. On my setup I use prefix as above, and:: [path_pattern] bitbucket.local = ~/devel/{below} bitbucket.remote = https://bitbucket.org/Mekk/{below:/=-} so all my repositories understand ``hg push bitbucket`` without any repository-level configuration. ``hgrc`` 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 ====================================================== Saving and restoring passwords ------------------------------------------------------- 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). Checking password status (``hg keyring_check``) ------------------------------------------------------- The ``keyring_check`` command can be used to check whether/which password(s) are saved. It can be used in three ways: - without parameters, it prints info related to all HTTP paths defined for current repository (everything from ``hg paths`` that resolves to HTTP url):: hg keyring_check - given alias as param, it prints info about this alias:: hg keyring_check work - finally, any path can be checked:: hg keyring_check https://bitbucket.org/Mekk/mercurial_keyring Deleting saved password (``hg keyring_clear``) ------------------------------------------------------- The ``keyring_clear`` command removes saved password related to given path. It can be used in two ways: - given alias as param, it drops password used by this alias:: hg keyring_clear work - given full path, it drops password related to this path:: hg keyring_clear https://bitbucket.org/Mekk/mercurial_keyring Managing passwords using GUI tools ------------------------------------------------------ Many password backends provide GUI tools for password management, for example Gnome Keyring passwords can be managed using ``seahorse``, and KDE Wallet using ``kwalletmanager``. Those GUI tools can be used to review, edit, or delete saved passwords. Unfortunately, as I write, keyring_ library does not allow one to configure how/where exactly saved passwords are put in the hierarchy, and the place is not always intuitive. For example, in KDE Wallet, all passwords saved using ``mercurial_keyring`` show up in the folder named ``Python``. .. note:: This is slightly problematic in case ``mercurial_keyring`` is not the only program using keyring_ library. Passwords saved by another Python application or script (which also uses keyring_) will be put into the same place, and it may be unclear which password belongs to which program. To remedy this, ``mercurial_keyring`` applies slightly unusual labels of the form ``«username»@@«urlprefix»@Mercurial`` - for example my bitbucket password is labelled ``Mekk@@https://bitbucket.org@Mercurial``. 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`_. Frequent problems ======================================================= Most problems people face while using ``mercurial_keyring`` are in fact problems with ``keyring`` library and it's backends. In particular, those can manifest by: - technical errors mentioning sentences like ``No recommended backend was available. Install the keyrings.alt package…`` (or similar), - warnings like ``keyring: keyring backend doesn't seem to work…`` - password prompts on every action (= passwords not being saved). Those almost always mean that *natural* keyring backend for given desktop type doesn't work, or is not present at all. For example, some necessary runtime component can be down (say, you use Linux, but neither Gnome Keyring, nor KDE Wallet, is running). Or appropriate backend is not installed because it could not be build during keyring_ library installation (maybe because some required library was not present at the moment of keyring installation, or maybe because compiler as such is not present on the system). To diagnose such problems, try using ``keyring`` utility, as described on keyring_ documentation page, for example by:: keyring --list-backends keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser (of course using appropriate backend). If you miss the ``keyring`` command as such, try ``python -m keyring`` instead:: python -m keyring --list-backends python -m keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser python -m keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser If appropriate backend is missing (not listed), or doesn't work (second or third command fails), your keyring is broken. Try looking for further pointers in keyring_ documentation, that project mailing list, or issue tracker. Typically it will turn out, that you need to install some missing tool, or library, and reinstall keyring. .. note:: Depending on keyring_ version, installation of some dependency may resolve problem. For example (as of late 2018), I got KDE Wallet backend working with pip-installed keyring after:: pip install dbus-python Note also, that recent versions of keyring library (since version 12) use Python entrypoints to find available backends. Those are incompatible with some binary packaging methods (like ``py2app``) and may cause problems. In particular there were packaged installations of TortoiseHG which were unable to load keyring backends. See `#61 `_ for some more details. If ``keyring`` command works, but mercurial with mercurial_keyring does not, try enforcing proper backend (by means of ``keyringrc.cfg``, see above). Only if this doesn't help, there may be a bug in mercurial_keyring. By far easiest way to have properly working keyring is to use packaged binary version (like ``python-keyring`` Ubuntu package, or keyring bundled with TortoiseHG on some systems). If you pip-installed keyring and it doesn't work, you may consider removing it via ``pip uninstall keyring`` and looking for binary package instead. History ======================================================= See `HISTORY.txt`_. 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 Check also `other Mercurial extensions I wrote`_. .. _other Mercurial extensions I wrote: http://mekk.bitbucket.io/mercurial.html .. _HISTORY.txt: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/HISTORY.txt .. _TortoiseHg: http://tortoisehg.bitbucket.org/ .. _Mercurial: http://mercurial.selenic.com .. _mercurial_extension_utils: https://bitbucket.org/Mekk/mercurial-extension_utils/ .. _Path Pattern: https://bitbucket.org/Mekk/mercurial-path_pattern/ 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 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-1.3.0/README.txt0000644000175000017500000003477213562125517020042 0ustar marcinkmarcink00000000000000.. -*- mode: rst; compile-command: "rst2html README.txt README.html" -*- ======================================================= 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, Windows Vault etc). With ``mercurial_keyring`` active, Mercurial remembers your passwords and reuses them without prompting (as if you stored them in ``.hgrc``), but password storage is reasonably secure. Actual password storage is implemented by the keyring_ library, this extension glues it to Mercurial. .. contents:: :local: :depth: 2 .. sectnum:: .. _keyring: http://pypi.python.org/pypi/keyring .. _Mercurial: http://mercurial.selenic.com How does it work ======================================================= On your first pull or push to HTTP url (or first email sent via given SMTP server), you are prompted for the password, just like bare Mercurial does. But the password you entered is saved to appropriate password database. On successive runs, whenever the password is needed, ``mercurial_keyring`` checks for password in password database, and uses it without troubling you. In case password turns out to be incorrect (for example, because you changed it, or entered it incorrectly), ``mercurial_keyring`` prompts you again, and overwrites the password. You can use many passwords (for various remote urls). Saved passwords are identified by pair of username and url prefix. See below for information how to configure those properly. Installation ======================================================= Prerequisites ------------- This extension requires keyring_ and `mercurial_extension_utils`_ to work. In many cases both will be installed automatically while you install ``mercurial_keyring``, but you may need to control the process. The keyring_ library can usually be installed by:: pip install --user keyring (or ``easy_install keyring``), but on some systems it is preferable to use official distribution archive. For example, on Debian and Ubuntu, you may install ``python-keyring`` and either ``python-keyring-gnome`` or ``python-keyring-kwallet`` packages:: sudo apt-get install python-keyring python-keyring-gnome (this will save you the need to provide working compiler and various development libraries). The `mercurial_extension_utils`_ module is tiny Python-only module, which can be installed by:: pip install --user mercurial_extension_utils but in some cases (Windows…) requires more care. See `mercurial_extension_utils`_ documentation. Extension installation ---------------------- There are two possible ways of installing the extension: using PyPi package, or using source clone. To install as a package:: pip install --user mercurial_keyring (or ``sudo pip install mercurial_keyring`` for system-wide installation) and then enable it in ``~/.hgrc`` (or ``/etc/mercurial/hgrc`` or ``Mercurial.ini``) using:: [extensions] mercurial_keyring = To install using source clone, install keyring_ according to the instructions above, then clone:: hg clone https://bitbucket.org/Mekk/mercurial_keyring/ hg clone https://bitbucket.org/Mekk/mercurial-extension_utils/ and configure Mercurial using full path to the extension module:: [extensions] mercurial_keyring = /path/to/mercurial_keyring/mercurial_keyring.py .. _the code: .. _mercurial_keyring.py: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/mercurial_keyring.py Password backend configuration ======================================================= The most appropriate password backend should usually be picked without configuration (considering installed libraries, operating system, active desktop session). Still, if necessary, it can be configured using ``keyringrc.cfg`` file. Refer to keyring_ docs for more details. .. note:: With current (as I write) keyring (5.6), this file is (on Linux) located at ``~/.local/share/python_keyring/keyringrc.cfg`` and it's example content looks like:: [backend] default-keyring=keyring.backends.Gnome.Keyring # default-keyring=keyring.backends.kwallet.Keyring For list of known backends run ``pydoc keyring.backends`` or ``keyring --list-backends`` (which of those commands work, depends on the keyring_ version). ``hgrc`` configuration (HTTP) ======================================================= Mercurial Keyring uses standard Mercurial ``[auth]`` configuration to detect your username (on given remote) and url prefix. You are strongly advised to configure both. Without the username ``mercurial_keyring`` can't save or restore passwords, so it disables itself. Without url prefix ``mercurial_keyring`` works, but binds passwords to repository urls. That means you will have to (re)enter password for every repository cloned from given remote (and that there will be many copies of this password in secure storage). Repository level configuration ------------------------------------ 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.prefix = https://my.server.com/hgrepo myremote.username = John Simpler form with url-embedded name can also be used: :: [paths] bitbucket = https://John@my.server.com/hgrepo/someproject/ but is not recommended. Note that all repositories sharing the same ``prefix`` share the same password. Mercurial allows also for password in ``.hg/hgrc`` (either given by ``«prefix».password``, or embedded in url). If such password is found, Mercurial Keyring disables itself. Account-level configuration --------------------------- 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 bitbucket.prefix = https://bitbucket.org bitbucket.username = Mekk mydep.prefix = https://dev.acmeorg.com mydep.username = drmartin and as long as you use ``acme`` alias for repositories like ``https://hg.acme.com/repositories/my_beautiful_app``, username ``johnny`` will be used, and the same password reused. Similarly any ``hg push bitbucket`` will share the same password. With such config repository-level ``.hg/hgrc`` need only contain ``[paths]``. Additional advantage of this method is that it works also during `clone`. .. note:: Mercurial Keyring works well with `Path Pattern`_. On my setup I use prefix as above, and:: [path_pattern] bitbucket.local = ~/devel/{below} bitbucket.remote = https://bitbucket.org/Mekk/{below:/=-} so all my repositories understand ``hg push bitbucket`` without any repository-level configuration. ``hgrc`` 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 ====================================================== Saving and restoring passwords ------------------------------------------------------- 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). Checking password status (``hg keyring_check``) ------------------------------------------------------- The ``keyring_check`` command can be used to check whether/which password(s) are saved. It can be used in three ways: - without parameters, it prints info related to all HTTP paths defined for current repository (everything from ``hg paths`` that resolves to HTTP url):: hg keyring_check - given alias as param, it prints info about this alias:: hg keyring_check work - finally, any path can be checked:: hg keyring_check https://bitbucket.org/Mekk/mercurial_keyring Deleting saved password (``hg keyring_clear``) ------------------------------------------------------- The ``keyring_clear`` command removes saved password related to given path. It can be used in two ways: - given alias as param, it drops password used by this alias:: hg keyring_clear work - given full path, it drops password related to this path:: hg keyring_clear https://bitbucket.org/Mekk/mercurial_keyring Managing passwords using GUI tools ------------------------------------------------------ Many password backends provide GUI tools for password management, for example Gnome Keyring passwords can be managed using ``seahorse``, and KDE Wallet using ``kwalletmanager``. Those GUI tools can be used to review, edit, or delete saved passwords. Unfortunately, as I write, keyring_ library does not allow one to configure how/where exactly saved passwords are put in the hierarchy, and the place is not always intuitive. For example, in KDE Wallet, all passwords saved using ``mercurial_keyring`` show up in the folder named ``Python``. .. note:: This is slightly problematic in case ``mercurial_keyring`` is not the only program using keyring_ library. Passwords saved by another Python application or script (which also uses keyring_) will be put into the same place, and it may be unclear which password belongs to which program. To remedy this, ``mercurial_keyring`` applies slightly unusual labels of the form ``«username»@@«urlprefix»@Mercurial`` - for example my bitbucket password is labelled ``Mekk@@https://bitbucket.org@Mercurial``. 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`_. Frequent problems ======================================================= Most problems people face while using ``mercurial_keyring`` are in fact problems with ``keyring`` library and it's backends. In particular, those can manifest by: - technical errors mentioning sentences like ``No recommended backend was available. Install the keyrings.alt package…`` (or similar), - warnings like ``keyring: keyring backend doesn't seem to work…`` - password prompts on every action (= passwords not being saved). Those almost always mean that *natural* keyring backend for given desktop type doesn't work, or is not present at all. For example, some necessary runtime component can be down (say, you use Linux, but neither Gnome Keyring, nor KDE Wallet, is running). Or appropriate backend is not installed because it could not be build during keyring_ library installation (maybe because some required library was not present at the moment of keyring installation, or maybe because compiler as such is not present on the system). To diagnose such problems, try using ``keyring`` utility, as described on keyring_ documentation page, for example by:: keyring --list-backends keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser (of course using appropriate backend). If you miss the ``keyring`` command as such, try ``python -m keyring`` instead:: python -m keyring --list-backends python -m keyring -b keyrings.alt.Gnome.Keyring set testsvc testuser python -m keyring -b keyrings.alt.Gnome.Keyring get testsvc testuser If appropriate backend is missing (not listed), or doesn't work (second or third command fails), your keyring is broken. Try looking for further pointers in keyring_ documentation, that project mailing list, or issue tracker. Typically it will turn out, that you need to install some missing tool, or library, and reinstall keyring. .. note:: Depending on keyring_ version, installation of some dependency may resolve problem. For example (as of late 2018), I got KDE Wallet backend working with pip-installed keyring after:: pip install dbus-python Note also, that recent versions of keyring library (since version 12) use Python entrypoints to find available backends. Those are incompatible with some binary packaging methods (like ``py2app``) and may cause problems. In particular there were packaged installations of TortoiseHG which were unable to load keyring backends. See `#61 `_ for some more details. If ``keyring`` command works, but mercurial with mercurial_keyring does not, try enforcing proper backend (by means of ``keyringrc.cfg``, see above). Only if this doesn't help, there may be a bug in mercurial_keyring. By far easiest way to have properly working keyring is to use packaged binary version (like ``python-keyring`` Ubuntu package, or keyring bundled with TortoiseHG on some systems). If you pip-installed keyring and it doesn't work, you may consider removing it via ``pip uninstall keyring`` and looking for binary package instead. History ======================================================= See `HISTORY.txt`_. 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 Check also `other Mercurial extensions I wrote`_. .. _other Mercurial extensions I wrote: http://mekk.bitbucket.io/mercurial.html .. _HISTORY.txt: http://bitbucket.org/Mekk/mercurial_keyring/src/tip/HISTORY.txt .. _TortoiseHg: http://tortoisehg.bitbucket.org/ .. _Mercurial: http://mercurial.selenic.com .. _mercurial_extension_utils: https://bitbucket.org/Mekk/mercurial-extension_utils/ .. _Path Pattern: https://bitbucket.org/Mekk/mercurial-path_pattern/