keyring-3.5/0000775000000000000000000000000012300426570011530 5ustar rootrootkeyring-3.5/demo/0000775000000000000000000000000012300426570012454 5ustar rootrootkeyring-3.5/demo/simplekeyring.py0000666000000000000000000000106512242130154015707 0ustar rootroot""" simplekeyring.py A simple keyring class for the keyring_demo.py Created by Kang Zhang on 2009-07-12 """ from keyring.backend import KeyringBackend class SimpleKeyring(KeyringBackend): """Simple Keyring is a keyring which can store only one password in memory. """ def __init__(self): self.password = '' def supported(self): return 0 def get_password(self, service, username): return self.password def set_password(self, service, username, password): self.password = password return 0 keyring-3.5/demo/auth_demo.py0000666000000000000000000000267112242130154014776 0ustar rootroot""" auth_demo.py Created by Kang Zhang 2009-08-14 """ import keyring import getpass import ConfigParser def auth(username, password): """A faked authorization function. """ return username == password def main(): """This scrip demos how to use keyring facilite the authorization. The username is stored in a config named 'auth_demo.cfg' """ config_file = 'auth_demo.cfg' config = ConfigParser.SafeConfigParser({ 'username':'', }) config.read(config_file) if not config.has_section('auth_demo_login'): config.add_section('auth_demo_login') username = config.get('auth_demo_login','username') password = None if username != '': password = keyring.get_password('auth_demo_login', username) if password == None or not auth(username, password): while 1: username = raw_input("Username:\n") password = getpass.getpass("Password:\n") if auth(username, password): break else: print "Authorization failed." # store the username config.set('auth_demo_login', 'username', username) config.write(open(config_file, 'w')) # store the password keyring.set_password('auth_demo_login', username, password) # the stuff that needs authorization here print "Authorization successful." if __name__ == "__main__": main() keyring-3.5/demo/keyring_demo.py0000666000000000000000000000445312242130154015505 0ustar rootroot""" keyring_demo.py This demo shows how to create a new keyring and enable it in keyring lib. Created by Kang Zhang on 2009-07-12 """ import os KEYRINGRC = "keyringrc.cfg" def load_keyring_by_config(): """This function shows how to enable a keyring using config file """ # create the config file config_file = open(KEYRINGRC,'w') config_file.writelines(["[backend]\n", # the path for the user created keyring "keyring-path= %s\n" % str(os.path.abspath(__file__))[:-16], # the name of the keyring class "default-keyring=simplekeyring.SimpleKeyring\n" ]) config_file.close() # import the keyring lib, the lib will automaticlly load the # config file and load the user defined module import keyring # invoke the keyring to store and fetch the password try: keyring.set_password("demo-service", "tarek", "passexample") print "password stored sucessfully" except keyring.backend.PasswordSetError: print "failed to store password" print "password", keyring.get_password("demo-service", "tarek") os.remove(KEYRINGRC) def set_keyring_in_runtime(): """This function shows how to create a keyring manully and use it in runtime """ # define a new keyring class which extends the KeyringBackend import keyring.backend class TestKeyring(keyring.backend.KeyringBackend): """A test keyring which always outputs same password """ def supported(self): return 0 def set_password(self, servicename, username, password): return 0 def get_password(self, servicename, username): return "password from TestKeyring" # set the keyring for keyring lib import keyring keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print "password stored sucessfully" except keyring.backend.PasswordSetError: print "failed to store password" print "password", keyring.get_password("demo-service", "tarek") def main(): """This script shows how to enable the keyring using the config file and in runtime. """ load_keyring_by_config() set_keyring_in_runtime() if __name__ == '__main__': main() keyring-3.5/demo/demo.py0000666000000000000000000000123512242130154013750 0ustar rootroot#!/usr/bin/env python # encoding: utf-8 """ demo.py Created by Kang Zhang on 2009-06-13. """ import keyring import getpass def main(): """This script demos the python keyring lib api. To see the changes that keyring lib has made on your machine, open your Keychain and search for demo-service in your login keychain. """ username = raw_input("Username for demo:\n") password = keyring.get_password("demo-service", username) print "Password fetched from the keyring: ", password password = getpass.getpass("Password:\n") keyring.set_password("demo-service", username, password) if __name__ == '__main__': main() keyring-3.5/MANIFEST.in0000666000000000000000000000022312246200106013257 0ustar rootrootrecursive-include keyring *.py recursive-include demo *.py include *.py include *.txt include *.rst include setup.cfg exclude .hg/last-message.txt keyring-3.5/hook-keyring.backend.py0000666000000000000000000000051112273742000016073 0ustar rootroot# Used by pyinstaller to expose hidden imports # TODO: can this be loaded from keyring.backend directly? _backend_mod_names = ('file', 'Gnome', 'Google', 'keyczar', 'kwallet', 'multi', 'OS_X', 'pyfs', 'SecretService', 'Windows') hiddenimports = [ 'keyring.backends.' + mod_name for mod_name in _backend_mod_names ] keyring-3.5/CHANGES.rst0000666000000000000000000004027212277422576013360 0ustar rootroot======= CHANGES ======= --- 3.5 --- * Issue #49: Give the backend priorities a 1.5 multiplier bump when an XDG_CURRENT_DESKTOP environment variable matches the keyring's target environment. * Issue #99: Clarified documentation on location of config and data files. Prepared the code base to treat the two differently on Unix-based systems. For now, the behavior is unchanged. --- 3.4 --- * Extracted FileBacked and Encrypted base classes. * Add a pyinstaller hook to expose backend modules. Ref #124 * Pull request #41: Use errno module instead of hardcoding error codes. * SecretService backend: correctly handle cases when user dismissed the collection creation or unlock prompt. --- 3.3 --- * Pull request #40: KWallet backend will now honor the ``KDE_FULL_SESSION`` environment variable as found on openSUSE. ----- 3.2.1 ----- * SecretService backend: use a different function to check that the backend is functional. The default collection may not exist, but the collection will remain usable in that case. Also, make the error message more verbose. Resolves https://bugs.launchpad.net/bugs/1242412. --- 3.2 --- * Issue #120: Invoke KeyringBackend.priority during load_keyring to ensure that any keyring loaded is actually viable (or raises an informative exception). * File keyring: - Issue #123: fix removing items. - Correctly escape item name when removing. - Use with statement when working with files. * Add a test for removing one item in group. * Issue #81: Added experimental support for third-party backends. See `keyring.core._load_library_extensions` for information on supplying a third-party backend. --- 3.1 --- * All code now runs natively on both Python 2 and Python 3, no 2to3 conversion is required. * Testsuite: clean up, and make more use of unittest2 methods. ----- 3.0.5 ----- * Issue #114: Fix logic in pyfs detection. ----- 3.0.4 ----- * Issue #114: Fix detection of pyfs under Mercurial Demand Import. ----- 3.0.3 ----- * Simplified the implementation of ``keyring.core.load_keyring``. It now uses ``__import__`` instead of loading modules explicitly. The ``keyring_path`` parameter to ``load_keyring`` is now deprecated. Callers should instead ensure their module is available on ``sys.path`` before calling ``load_keyring``. Keyring still honors ``keyring-path``. This change fixes Issue #113 in which the explicit module loading of keyring modules was breaking package-relative imports. ----- 3.0.2 ----- * Renamed ``keyring.util.platform`` to ``keyring.util.platform_``. As reported in Issue #112 and `mercurial_keyring #31 `_ and in `Mercurial itself `_, Mercurial's Demand Import does not honor ``absolute_import`` directives, so it's not possible to have a module with the same name as another top-level module. A patch is in place to fix this issue upstream, but to support older Mercurial versions, this patch will remain for some time. ----- 3.0.1 ----- * Ensure that modules are actually imported even in Mercurial's Demand Import environment. --- 3.0 --- * Removed support for Python 2.5. * Removed names in ``keyring.backend`` moved in 1.1 and previously retained for compatibilty. ----- 2.1.1 ----- * Restored Python 2.5 compatibility (lost in 2.0). --- 2.1 --- * Issue #10: Added a 'store' attribute to the OS X Keyring, enabling custom instances of the KeyringBackend to use another store, such as the 'internet' store. For example:: keys = keyring.backends.OS_X.Keyring() keys.store = 'internet' keys.set_password(system, user, password) keys.get_password(system, user) The default for all instances can be set in the class:: keyring.backends.OS_X.Keyring.store = 'internet' * GnomeKeyring: fix availability checks, and make sure the warning message from pygobject is not printed. * Fixes to GnomeKeyring and SecretService tests. ----- 2.0.3 ----- * Issue #112: Backend viability/priority checks now are more aggressive about module presence checking, requesting ``__name__`` from imported modules to force the demand importer to actually attempt the import. ----- 2.0.2 ----- * Issue #111: Windows backend isn't viable on non-Windows platforms. ----- 2.0.1 ----- * Issue #110: Fix issues with ``Windows.RegistryKeyring``. --- 2.0 --- * Issue #80: Prioritized backend support. The primary interface for Keyring backend classes has been refactored to now emit a 'priority' based on the current environment (operating system, libraries available, etc). These priorities provide an indication of the applicability of that backend for the current environment. Users are still welcome to specify a particular backend in configuration, but the default behavior should now be to select the most appropriate backend by default. ----- 1.6.1 ----- * Only include pytest-runner in 'setup requirements' when ptr invocation is indicated in the command-line (Issue #105). --- 1.6 --- * GNOME Keyring backend: - Use the same attributes (``username`` / ``service``) as the SecretService backend uses, allow searching for old ones for compatibility. - Also set ``application`` attribute. - Correctly handle all types of errors, not only ``CANCELLED`` and ``NO_MATCH``. - Avoid printing warnings to stderr when GnomeKeyring is not available. * Secret Service backend: - Use a better label for passwords, the same as GNOME Keyring backend uses. --- 1.5 --- * SecretService: allow deleting items created using previous python-keyring versions. Before the switch to secretstorage, python-keyring didn't set "application" attribute. Now in addition to supporting searching for items without that attribute, python-keyring also supports deleting them. * Use ``secretstorage.get_default_collection`` if it's available. On secretstorage 1.0 or later, python-keyring now tries to create the default collection if it doesn't exist, instead of just raising the error. * Improvements for tests, including fix for Issue #102. --- 1.4 --- * Switch GnomeKeyring backend to use native libgnome-keyring via GObject Introspection, not the obsolete python-gnomekeyring module. --- 1.3 --- * Use the `SecretStorage library `_ to implement the Secret Service backend (instead of using dbus directly). Now the keyring supports prompting for and deleting passwords. Fixes #69, #77, and #93. * Catch `gnomekeyring.IOError` per the issue `reported in Nova client `_. * Issue #92 Added support for delete_password on Mac OS X Keychain. ----- 1.2.3 ----- * Fix for Encrypted File backend on Python 3. * Issue #97 Improved support for PyPy. ----- 1.2.2 ----- * Fixed handling situations when user cancels kwallet dialog or denies access for the app. ----- 1.2.1 ----- * Fix for kwallet delete. * Fix for OS X backend on Python 3. * Issue #84: Fix for Google backend on Python 3 (use of raw_input not caught by 2to3). --- 1.2 --- * Implemented delete_password on most keyrings. Keyring 2.0 will require delete_password to implement a Keyring. Fixes #79. ----- 1.1.2 ----- * Issue #78: pyfilesystem backend now works on Windows. ----- 1.1.1 ----- * Fixed MANIFEST.in so .rst files are included. --- 1.1 --- This is the last build that will support installation in a pure-distutils mode. Subsequent releases will require setuptools/distribute to install. Python 3 installs have always had this requirement (for 2to3 install support), but starting with the next minor release (1.2+), setuptools will be required. Additionally, this release has made some substantial refactoring in an attempt to modularize the backends. An attempt has been made to maintain 100% backward-compatibility, although if your library does anything fancy with module structure or clasess, some tweaking may be necessary. The backward-compatible references will be removed in 2.0, so the 1.1+ releases represent a transitional implementation which should work with both legacy and updated module structure. * Added a console-script 'keyring' invoking the command-line interface. * Deprecated _ExtensionKeyring. * Moved PasswordSetError and InitError to an `errors` module (references kept for backward-compatibility). * Moved concrete backend implementations into their own modules (references kept for backward compatibility): - OSXKeychain -> backends.OS_X.Keyring - GnomeKeyring -> backends.Gnome.Keyring - SecretServiceKeyring -> backends.SecretService.Keyring - KDEKWallet -> backends.kwallet.Keyring - BasicFileKeyring -> backends.file.BaseKeyring - CryptedFileKeyring -> backends.file.EncryptedKeyring - UncryptedFileKeyring -> backends.file.PlaintextKeyring - Win32CryptoKeyring -> backends.Windows.EncryptedKeyring - WinVaultKeyring -> backends.Windows.WinVaultKeyring - Win32CryptoRegistry -> backends.Windows.RegistryKeyring - select_windows_backend -> backends.Windows.select_windows_backend - GoogleDocsKeyring -> backends.Google.DocsKeyring - Credential -> keyring.credentials.Credential - BaseCredential -> keyring.credentials.SimpleCredential - EnvironCredential -> keyring.credentials.EnvironCredential - GoogleEnvironCredential -> backends.Google.EnvironCredential - BaseKeyczarCrypter -> backends.keyczar.BaseCrypter - KeyczarCrypter -> backends.keyczar.Crypter - EnvironKeyczarCrypter -> backends.keyczar.EnvironCrypter - EnvironGoogleDocsKeyring -> backends.Google.KeyczarDocsKeyring - BasicPyfilesystemKeyring -> backends.pyfs.BasicKeyring - UnencryptedPyfilesystemKeyring -> backends.pyfs.PlaintextKeyring - EncryptedPyfilesystemKeyring -> backends.pyfs.EncryptedKeyring - EnvironEncryptedPyfilesystemKeyring -> backends.pyfs.KeyczarKeyring - MultipartKeyringWrapper -> backends.multi.MultipartKeyringWrapper * Officially require Python 2.5 or greater (although unofficially, this requirement has been in place since 0.10). --- 1.0 --- This backward-incompatible release attempts to remove some cruft from the codebase that's accumulated over the versions. * Removed legacy file relocation support. `keyring` no longer supports loading configuration or file-based backends from ~. If upgrading from 0.8 or later, the files should already have been migrated to their new proper locations. If upgrading from 0.7.x or earlier, the files will have to be migrated manually. * Removed CryptedFileKeyring migration support. To maintain an existing CryptedFileKeyring, one must first upgrade to 0.9.2 or later and access the keyring before upgrading to 1.0 to retain the existing keyring. * File System backends now create files without group and world permissions. Fixes #67. ------ 0.10.1 ------ * Merged 0.9.3 to include fix for #75. ---- 0.10 ---- * Add support for using `Keyczar `_ to encrypt keyrings. Keyczar is "an open source cryptographic toolkit designed to make it easier and safer for developers to use cryptography in their applications." * Added support for storing keyrings on Google Docs or any other filesystem supported by pyfilesystem. * Fixed issue in Gnome Keyring when unicode is passed as the service name, username, or password. * Tweaked SecretService code to pass unicode to DBus, as unicode is the preferred format. * Issue #71 - Fixed logic in CryptedFileKeyring. * Unencrypted keyring file will be saved with user read/write (and not group or world read/write). ----- 0.9.3 ----- * Ensure migration is run when get_password is called. Fixes #75. Thanks to Marc Deslauriers for reporting the bug and supplying the patch. ----- 0.9.2 ----- * Keyring 0.9.1 introduced a whole different storage format for the CryptedFileKeyring, but this introduced some potential compatibility issues. This release incorporates the security updates but reverts to the INI file format for storage, only encrypting the passwords and leaving the service and usernames in plaintext. Subsequent releases may incorporate a new keyring to implement a whole-file encrypted version. Fixes #64. * The CryptedFileKeyring now requires simplejson for Python 2.5 clients. ----- 0.9.1 ----- * Fix for issue where SecretServiceBackend.set_password would raise a UnicodeError on Python 3 or when a unicode password was provided on Python 2. * CryptedFileKeyring now uses PBKDF2 to derive the key from the user's password and a random hash. The IV is chosen randomly as well. All the stored passwords are encrypted at once. Any keyrings using the old format will be automatically converted to the new format (but will no longer be compatible with 0.9 and earlier). The user's password is no longer limited to 32 characters. PyCrypto 2.5 or greater is now required for this keyring. --- 0.9 --- * Add support for GTK 3 and secret service D-Bus. Fixes #52. * Issue #60 - Use correct method for decoding. ----- 0.8.1 ----- * Fix regression in keyring lib on Windows XP where the LOCALAPPDATA environment variable is not present. --- 0.8 --- * Mac OS X keyring backend now uses subprocess calls to the `security` command instead of calling the API, which with the latest updates, no longer allows Python to invoke from a virtualenv. Fixes issue #13. * When using file-based storage, the keyring files are no longer stored in the user's home directory, but are instead stored in platform-friendly locations (`%localappdata%\Python Keyring` on Windows and according to the freedesktop.org Base Dir Specification (`$XDG_DATA_HOME/python_keyring` or `$HOME/.local/share/python_keyring`) on other operating systems). This fixes #21. *Backward Compatibility Notice* Due to the new storage location for file-based keyrings, keyring 0.8 supports backward compatibility by automatically moving the password files to the updated location. In general, users can upgrade to 0.8 and continue to operate normally. Any applications that customize the storage location or make assumptions about the storage location will need to take this change into consideration. Additionally, after upgrading to 0.8, it is not possible to downgrade to 0.7 without manually moving configuration files. In 1.0, the backward compatibilty will be removed. ----- 0.7.1 ----- * Removed non-ASCII characters from README and CHANGES docs (required by distutils if we're to include them in the long_description). Fixes #55. --- 0.7 --- * Python 3 is now supported. All tests now pass under Python 3.2 on Windows and Linux (although Linux backend support is limited). Fixes #28. * Extension modules on Mac and Windows replaced by pure-Python ctypes implementations. Thanks to Jerome Laheurte. * WinVaultKeyring now supports multiple passwords for the same service. Fixes #47. * Most of the tests don't require user interaction anymore. * Entries stored in Gnome Keyring appears now with a meaningful name if you try to browser your keyring (for ex. with Seahorse) * Tests from Gnome Keyring no longer pollute the user own keyring. * `keyring.util.escape` now accepts only unicode strings. Don't try to encode strings passed to it. ----- 0.6.2 ----- * fix compiling on OSX with XCode 4.0 ----- 0.6.1 ----- * Gnome keyring should not be used if there is no DISPLAY or if the dbus is not around (https://bugs.launchpad.net/launchpadlib/+bug/752282). --- 0.6 --- * Added `keyring.http` for facilitating HTTP Auth using keyring. * Add a utility to access the keyring from the command line. ----- 0.5.1 ----- * Remove a spurious KDE debug message when using KWallet * Fix a bug that caused an exception if the user canceled the KWallet dialog (https://bitbucket.org/kang/python-keyring-lib/issue/37/user-canceling-of-kde-wallet-dialogs). --- 0.5 --- * Now using the existing Gnome and KDE python libs instead of custom C++ code. * Using the getpass module instead of custom code --- 0.4 --- * Fixed the setup script (some subdirs were not included in the release.) --- 0.3 --- * Fixed keyring.core when the user doesn't have a cfg, or is not properly configured. * Fixed escaping issues for usernames with non-ascii characters --- 0.2 --- * Add support for Python 2.4+ http://bitbucket.org/kang/python-keyring-lib/issue/2 * Fix the bug in KDE Kwallet extension compiling http://bitbucket.org/kang/python-keyring-lib/issue/3 keyring-3.5/keyring.egg-info/0000775000000000000000000000000012300426570014672 5ustar rootrootkeyring-3.5/keyring.egg-info/SOURCES.txt0000666000000000000000000000322612277424710016572 0ustar rootrootCHANGES.rst CONTRIBUTORS.txt MANIFEST.in README.rst hook-keyring.backend.py release.py setup.cfg setup.py demo/auth_demo.py demo/demo.py demo/keyring_demo.py demo/simplekeyring.py keyring/__init__.py keyring/backend.py keyring/cli.py keyring/core.py keyring/credentials.py keyring/errors.py keyring/getpassbackend.py keyring/http.py keyring/py27compat.py keyring.egg-info/PKG-INFO keyring.egg-info/SOURCES.txt keyring.egg-info/dependency_links.txt keyring.egg-info/entry_points.txt keyring.egg-info/requires.txt keyring.egg-info/top_level.txt keyring/backends/Gnome.py keyring/backends/Google.py keyring/backends/OS_X.py keyring/backends/SecretService.py keyring/backends/Windows.py keyring/backends/__init__.py keyring/backends/_win_crypto.py keyring/backends/file.py keyring/backends/keyczar.py keyring/backends/kwallet.py keyring/backends/multi.py keyring/backends/pyfs.py keyring/tests/__init__.py keyring/tests/mocks.py keyring/tests/py30compat.py keyring/tests/test_XDG.py keyring/tests/test_backend.py keyring/tests/test_cli.py keyring/tests/test_core.py keyring/tests/test_util.py keyring/tests/util.py keyring/tests/backends/__init__.py keyring/tests/backends/test_Gnome.py keyring/tests/backends/test_Google.py keyring/tests/backends/test_OS_X.py keyring/tests/backends/test_SecretService.py keyring/tests/backends/test_Windows.py keyring/tests/backends/test_crypto.py keyring/tests/backends/test_file.py keyring/tests/backends/test_keyczar.py keyring/tests/backends/test_kwallet.py keyring/tests/backends/test_multi.py keyring/tests/backends/test_pyfs.py keyring/util/XDG.py keyring/util/__init__.py keyring/util/escape.py keyring/util/platform_.py keyring/util/properties.pykeyring-3.5/keyring.egg-info/dependency_links.txt0000666000000000000000000000000112277424710020751 0ustar rootroot keyring-3.5/keyring.egg-info/requires.txt0000666000000000000000000000003512277424710017301 0ustar rootroot [test] pytest mock pycryptokeyring-3.5/keyring.egg-info/top_level.txt0000666000000000000000000000001012277424710017424 0ustar rootrootkeyring keyring-3.5/keyring.egg-info/entry_points.txt0000666000000000000000000000005612277424710020202 0ustar rootroot[console_scripts] keyring = keyring.cli:main keyring-3.5/keyring.egg-info/PKG-INFO0000666000000000000000000011156012277424710016004 0ustar rootrootMetadata-Version: 1.1 Name: keyring Version: 3.5 Summary: Store and access your passwords safely. Home-page: http://bitbucket.org/kang/python-keyring-lib Author: Jason R. Coombs Author-email: jaraco@jaraco.com License: PSF and MIT Description: ======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides a easy way to access the system keyring service from python. It can be used in any application that needs safe password storage. The keyring library is licensed under both the `MIT license `_ and the PSF license. The keyring services supported by the Python keyring lib: * **OSXKeychain**: supports the Keychain service in Mac OS X. * **KDEKWallet**: supports the KDE's Kwallet service. * **GnomeKeyring**: for Gnome 2 environment. * **SecretServiceKeyring**: for newer GNOME and KDE environments. * **WinVaultKeyring**: supports the Windows Credential Vault Besides these native password storing services provided by operating systems. Python keyring lib also provides following build-in keyrings. * **Win32CryptoKeyring**: for Windows 2k+. * **CryptedFileKeyring**: a command line interface keyring base on PyCrypto. * **UncryptedFileKeyring**: a keyring which leaves passwords directly in file. ------------------------- Installation Instructions ------------------------- easy_install or pip =================== Run easy_install or pip:: $ easy_install keyring $ pip install keyring Source installation =================== Download the source tarball, and uncompress it, then run the install command:: $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.3.tar.gz $ tar -xzvf keyring-0.3.tar.gz $ cd keyring-0.3 $ python setup.py install ------------- Using Keyring ------------- The basic usage of keyring is pretty simple: just call `keyring.set_password` and `keyring.get_password`: >>> import keyring >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends, including **OSX Keychain**, **Gnome Keyring**, **KDE Kwallet** and etc. The lib will automatically choose the keyring that is most suitable for your current environment. You can also specify the keyring you like to be used in the config file or by calling the ``set_keyring()`` function. Customize your keyring by config file ===================================== This section is about how to change your option in the config file. Config file path ---------------- The configuration of the lib is stored in a file named "keyringrc.cfg". This file must be found in a platform-specific location. To determine where the config file is stored, run the following:: python -c "import keyring.util.platform_; print(keyring.util.platform_.config_root())" Some keyrings also store the keyring data in the file system. To determine where the data files are stored, run this command:: python -c "import keyring.util.platform_; print(keyring.util.platform_.data_root())" Config file content ------------------- To specify a keyring backend, you need tell the lib the module name of the backend, such as ``keyring.backends.OS_X.Keyring``. If the backend is not shipped with the lib, in another word, is made by you own, you need also tell the lib the path of your own backend module. The module name should be written after the **default-keyring** option, while the module path belongs the **keyring-path** option. Here's a sample config file(The full demo can be accessed in the ``demo/keyring.py``): :: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=/home/kang/pyworkspace/python-keyring-lib/demo/ Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. By extending this base class and implementing the three functions ``supported()``, ``get_password()`` and ``set_password()``, you can easily create your own backend for keyring lib. The usage of the three functions: * ``supported(self)`` : Return if this backend is supported in current environment. The returned value can be **0**, **1** , or **-1**. **0** means suitable; **1** means recommended and **-1** means this backend is not available for current environment. * ``get_password(self, service, username)`` : Return the stored password for the ``username`` of the ``service``. * ``set_password(self, service, username, password)`` : Store the ``password`` for ``username`` of the ``service`` in the backend. * ``delete_password(self, service, username)`` : Delete the stored password for the ``username`` of the ``service``. For an instance, there's the source code of the demo mentioned above. It's a simple keyring which stores the password directly in memory. :: """ simplekeyring.py A simple keyring class for the keyring_demo.py Created by Kang Zhang on 2009-07-12 """ from keyring.backend import KeyringBackend class SimpleKeyring(KeyringBackend): """Simple Keyring is a keyring which can store only one password in memory. """ def __init__(self): self.password = '' def supported(self): return 0 def get_password(self, service, username): return self.password def set_password(self, service, username, password): self.password = password return 0 def delete_password(self, service, username): self.password = None Set the keyring in runtime ========================== Besides setting the backend through the config file, you can also set the backend to use by calling the api ``set_keyring()``. The backend you passed in will be used to store the password in your application. Here's a code snippet from the ``keyringdemo.py``. It shows the usage of ``set_keyring()`` :: # define a new keyring class which extends the KeyringBackend import keyring.backend class TestKeyring(keyring.backend.KeyringBackend): """A test keyring which always outputs same password """ def supported(self): return 0 def set_password(self, servicename, username, password): return 0 def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): return 0 # set the keyring for keyring lib import keyring keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print "password stored sucessfully" except keyring.backend.PasswordSetError: print "failed to store password" print "password", keyring.get_password("demo-service", "tarek") ----------------------------------------------- Integrate the keyring lib with your application ----------------------------------------------- API interface ============= The keyring lib has a few functions: * ``get_keyring()`` : Return the currently-loaded keyring implementation. * ``get_password(service, username)`` : Returns the password stored in keyring. If the password does not exist, it will return None. * ``set_password(service, username, password)`` : Store the password in the keyring. * ``delete_password(service, username)`` : Delete the password stored in keyring. If the password does not exist, it will raise an exception. Example ======= Here's an example of using keyring for application authorization. It can be found in the demo folder of the repository. Note that the faked auth function only returns true when the password equals to the username. :: """ auth_demo.py Created by Kang Zhang 2009-08-14 """ import keyring import getpass import ConfigParser def auth(username, password): """A faked authorization function. """ return username == password def main(): """This scrip demos how to use keyring facilite the authorization. The username is stored in a config named 'auth_demo.cfg' """ # config file init config_file = 'auth_demo.cfg' config = ConfigParser.SafeConfigParser({ 'username':'', }) config.read(config_file) if not config.has_section('auth_demo_login'): config.add_section('auth_demo_login') username = config.get('auth_demo_login','username') password = None if username != '': password = keyring.get_password('auth_demo_login', username) if password == None or not auth(username, password): while 1: username = raw_input("Username:\n") password = getpass.getpass("Password:\n") if auth(username, password): break else: print "Authorization failed." # store the username config.set('auth_demo_login', 'username', username) config.write(open(config_file, 'w')) # store the password keyring.set_password('auth_demo_login', username, password) # the stuff that needs authorization here print "Authorization successful." if __name__ == "__main__": main() ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: http://bitbucket.org/kang/python-keyring-lib/ * Bug Tracker: http://bitbucket.org/kang/python-keyring-lib/issues/ * Mailing list: http://groups.google.com/group/python-keyring Running Tests ============= Tests are `continuously run `_ using Travis-CI. |BuildStatus|_ .. |BuildStatus| image:: https://secure.travis-ci.org/jaraco/keyring.png .. _BuildStatus: http://travis-ci.org/jaraco/keyring To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Three recommended techniques are described below. Using pytest runner ------------------- Keyring is instrumented with `pytest runner `_. Thus, you may invoke the tests from any supported Python (with distribute installed) using this command:: python setup.py ptr pytest runner will download any unmet dependencies and run the tests using `pytest `_. This technique is the one used by the Travis-CI script. Using virtualenv and pytest/nose/unittest2 ------------------------------------------ Pytest and Nose are two popular test runners that will discover tests and run them. Unittest (unittest2 under Python 2.6) also has a mode to discover tests. First, however, these test runners typically need a test environment in which to run. It is recommended that you install keyring to a virtual environment to avoid interfering with your system environment. For more information, see the `virtualenv homepage `_. After you've created (or designated) your environment, install keyring into the environment by running:: python setup.py develop Then, invoke your favorite test runner, e.g.:: py.test or:: nosetests Using buildout -------------- Keyring supplies a buildout.cfg for use with buildout. If you have buildout installed, tests can be invoked as so:: 1. bin/buildout # prepare the buildout. 2. bin/test # execute the test runner. For more information about the options that the script provides do execute:: python bin/test --help ------- Credits ------- The project was based on Tarek Ziade's idea in `this post`_. Kang Zhang initially carried it out as a `Google Summer of Code`_ project, and Tarek mentored Kang on this project. .. _this post: http://tarekziade.wordpress.com/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/ .. _Google Summer of Code: http://socghop.appspot.com/ See CONTRIBUTORS.txt for a complete list of contributors. ======= CHANGES ======= --- 3.5 --- * Issue #49: Give the backend priorities a 1.5 multiplier bump when an XDG_CURRENT_DESKTOP environment variable matches the keyring's target environment. * Issue #99: Clarified documentation on location of config and data files. Prepared the code base to treat the two differently on Unix-based systems. For now, the behavior is unchanged. --- 3.4 --- * Extracted FileBacked and Encrypted base classes. * Add a pyinstaller hook to expose backend modules. Ref #124 * Pull request #41: Use errno module instead of hardcoding error codes. * SecretService backend: correctly handle cases when user dismissed the collection creation or unlock prompt. --- 3.3 --- * Pull request #40: KWallet backend will now honor the ``KDE_FULL_SESSION`` environment variable as found on openSUSE. ----- 3.2.1 ----- * SecretService backend: use a different function to check that the backend is functional. The default collection may not exist, but the collection will remain usable in that case. Also, make the error message more verbose. Resolves https://bugs.launchpad.net/bugs/1242412. --- 3.2 --- * Issue #120: Invoke KeyringBackend.priority during load_keyring to ensure that any keyring loaded is actually viable (or raises an informative exception). * File keyring: - Issue #123: fix removing items. - Correctly escape item name when removing. - Use with statement when working with files. * Add a test for removing one item in group. * Issue #81: Added experimental support for third-party backends. See `keyring.core._load_library_extensions` for information on supplying a third-party backend. --- 3.1 --- * All code now runs natively on both Python 2 and Python 3, no 2to3 conversion is required. * Testsuite: clean up, and make more use of unittest2 methods. ----- 3.0.5 ----- * Issue #114: Fix logic in pyfs detection. ----- 3.0.4 ----- * Issue #114: Fix detection of pyfs under Mercurial Demand Import. ----- 3.0.3 ----- * Simplified the implementation of ``keyring.core.load_keyring``. It now uses ``__import__`` instead of loading modules explicitly. The ``keyring_path`` parameter to ``load_keyring`` is now deprecated. Callers should instead ensure their module is available on ``sys.path`` before calling ``load_keyring``. Keyring still honors ``keyring-path``. This change fixes Issue #113 in which the explicit module loading of keyring modules was breaking package-relative imports. ----- 3.0.2 ----- * Renamed ``keyring.util.platform`` to ``keyring.util.platform_``. As reported in Issue #112 and `mercurial_keyring #31 `_ and in `Mercurial itself `_, Mercurial's Demand Import does not honor ``absolute_import`` directives, so it's not possible to have a module with the same name as another top-level module. A patch is in place to fix this issue upstream, but to support older Mercurial versions, this patch will remain for some time. ----- 3.0.1 ----- * Ensure that modules are actually imported even in Mercurial's Demand Import environment. --- 3.0 --- * Removed support for Python 2.5. * Removed names in ``keyring.backend`` moved in 1.1 and previously retained for compatibilty. ----- 2.1.1 ----- * Restored Python 2.5 compatibility (lost in 2.0). --- 2.1 --- * Issue #10: Added a 'store' attribute to the OS X Keyring, enabling custom instances of the KeyringBackend to use another store, such as the 'internet' store. For example:: keys = keyring.backends.OS_X.Keyring() keys.store = 'internet' keys.set_password(system, user, password) keys.get_password(system, user) The default for all instances can be set in the class:: keyring.backends.OS_X.Keyring.store = 'internet' * GnomeKeyring: fix availability checks, and make sure the warning message from pygobject is not printed. * Fixes to GnomeKeyring and SecretService tests. ----- 2.0.3 ----- * Issue #112: Backend viability/priority checks now are more aggressive about module presence checking, requesting ``__name__`` from imported modules to force the demand importer to actually attempt the import. ----- 2.0.2 ----- * Issue #111: Windows backend isn't viable on non-Windows platforms. ----- 2.0.1 ----- * Issue #110: Fix issues with ``Windows.RegistryKeyring``. --- 2.0 --- * Issue #80: Prioritized backend support. The primary interface for Keyring backend classes has been refactored to now emit a 'priority' based on the current environment (operating system, libraries available, etc). These priorities provide an indication of the applicability of that backend for the current environment. Users are still welcome to specify a particular backend in configuration, but the default behavior should now be to select the most appropriate backend by default. ----- 1.6.1 ----- * Only include pytest-runner in 'setup requirements' when ptr invocation is indicated in the command-line (Issue #105). --- 1.6 --- * GNOME Keyring backend: - Use the same attributes (``username`` / ``service``) as the SecretService backend uses, allow searching for old ones for compatibility. - Also set ``application`` attribute. - Correctly handle all types of errors, not only ``CANCELLED`` and ``NO_MATCH``. - Avoid printing warnings to stderr when GnomeKeyring is not available. * Secret Service backend: - Use a better label for passwords, the same as GNOME Keyring backend uses. --- 1.5 --- * SecretService: allow deleting items created using previous python-keyring versions. Before the switch to secretstorage, python-keyring didn't set "application" attribute. Now in addition to supporting searching for items without that attribute, python-keyring also supports deleting them. * Use ``secretstorage.get_default_collection`` if it's available. On secretstorage 1.0 or later, python-keyring now tries to create the default collection if it doesn't exist, instead of just raising the error. * Improvements for tests, including fix for Issue #102. --- 1.4 --- * Switch GnomeKeyring backend to use native libgnome-keyring via GObject Introspection, not the obsolete python-gnomekeyring module. --- 1.3 --- * Use the `SecretStorage library `_ to implement the Secret Service backend (instead of using dbus directly). Now the keyring supports prompting for and deleting passwords. Fixes #69, #77, and #93. * Catch `gnomekeyring.IOError` per the issue `reported in Nova client `_. * Issue #92 Added support for delete_password on Mac OS X Keychain. ----- 1.2.3 ----- * Fix for Encrypted File backend on Python 3. * Issue #97 Improved support for PyPy. ----- 1.2.2 ----- * Fixed handling situations when user cancels kwallet dialog or denies access for the app. ----- 1.2.1 ----- * Fix for kwallet delete. * Fix for OS X backend on Python 3. * Issue #84: Fix for Google backend on Python 3 (use of raw_input not caught by 2to3). --- 1.2 --- * Implemented delete_password on most keyrings. Keyring 2.0 will require delete_password to implement a Keyring. Fixes #79. ----- 1.1.2 ----- * Issue #78: pyfilesystem backend now works on Windows. ----- 1.1.1 ----- * Fixed MANIFEST.in so .rst files are included. --- 1.1 --- This is the last build that will support installation in a pure-distutils mode. Subsequent releases will require setuptools/distribute to install. Python 3 installs have always had this requirement (for 2to3 install support), but starting with the next minor release (1.2+), setuptools will be required. Additionally, this release has made some substantial refactoring in an attempt to modularize the backends. An attempt has been made to maintain 100% backward-compatibility, although if your library does anything fancy with module structure or clasess, some tweaking may be necessary. The backward-compatible references will be removed in 2.0, so the 1.1+ releases represent a transitional implementation which should work with both legacy and updated module structure. * Added a console-script 'keyring' invoking the command-line interface. * Deprecated _ExtensionKeyring. * Moved PasswordSetError and InitError to an `errors` module (references kept for backward-compatibility). * Moved concrete backend implementations into their own modules (references kept for backward compatibility): - OSXKeychain -> backends.OS_X.Keyring - GnomeKeyring -> backends.Gnome.Keyring - SecretServiceKeyring -> backends.SecretService.Keyring - KDEKWallet -> backends.kwallet.Keyring - BasicFileKeyring -> backends.file.BaseKeyring - CryptedFileKeyring -> backends.file.EncryptedKeyring - UncryptedFileKeyring -> backends.file.PlaintextKeyring - Win32CryptoKeyring -> backends.Windows.EncryptedKeyring - WinVaultKeyring -> backends.Windows.WinVaultKeyring - Win32CryptoRegistry -> backends.Windows.RegistryKeyring - select_windows_backend -> backends.Windows.select_windows_backend - GoogleDocsKeyring -> backends.Google.DocsKeyring - Credential -> keyring.credentials.Credential - BaseCredential -> keyring.credentials.SimpleCredential - EnvironCredential -> keyring.credentials.EnvironCredential - GoogleEnvironCredential -> backends.Google.EnvironCredential - BaseKeyczarCrypter -> backends.keyczar.BaseCrypter - KeyczarCrypter -> backends.keyczar.Crypter - EnvironKeyczarCrypter -> backends.keyczar.EnvironCrypter - EnvironGoogleDocsKeyring -> backends.Google.KeyczarDocsKeyring - BasicPyfilesystemKeyring -> backends.pyfs.BasicKeyring - UnencryptedPyfilesystemKeyring -> backends.pyfs.PlaintextKeyring - EncryptedPyfilesystemKeyring -> backends.pyfs.EncryptedKeyring - EnvironEncryptedPyfilesystemKeyring -> backends.pyfs.KeyczarKeyring - MultipartKeyringWrapper -> backends.multi.MultipartKeyringWrapper * Officially require Python 2.5 or greater (although unofficially, this requirement has been in place since 0.10). --- 1.0 --- This backward-incompatible release attempts to remove some cruft from the codebase that's accumulated over the versions. * Removed legacy file relocation support. `keyring` no longer supports loading configuration or file-based backends from ~. If upgrading from 0.8 or later, the files should already have been migrated to their new proper locations. If upgrading from 0.7.x or earlier, the files will have to be migrated manually. * Removed CryptedFileKeyring migration support. To maintain an existing CryptedFileKeyring, one must first upgrade to 0.9.2 or later and access the keyring before upgrading to 1.0 to retain the existing keyring. * File System backends now create files without group and world permissions. Fixes #67. ------ 0.10.1 ------ * Merged 0.9.3 to include fix for #75. ---- 0.10 ---- * Add support for using `Keyczar `_ to encrypt keyrings. Keyczar is "an open source cryptographic toolkit designed to make it easier and safer for developers to use cryptography in their applications." * Added support for storing keyrings on Google Docs or any other filesystem supported by pyfilesystem. * Fixed issue in Gnome Keyring when unicode is passed as the service name, username, or password. * Tweaked SecretService code to pass unicode to DBus, as unicode is the preferred format. * Issue #71 - Fixed logic in CryptedFileKeyring. * Unencrypted keyring file will be saved with user read/write (and not group or world read/write). ----- 0.9.3 ----- * Ensure migration is run when get_password is called. Fixes #75. Thanks to Marc Deslauriers for reporting the bug and supplying the patch. ----- 0.9.2 ----- * Keyring 0.9.1 introduced a whole different storage format for the CryptedFileKeyring, but this introduced some potential compatibility issues. This release incorporates the security updates but reverts to the INI file format for storage, only encrypting the passwords and leaving the service and usernames in plaintext. Subsequent releases may incorporate a new keyring to implement a whole-file encrypted version. Fixes #64. * The CryptedFileKeyring now requires simplejson for Python 2.5 clients. ----- 0.9.1 ----- * Fix for issue where SecretServiceBackend.set_password would raise a UnicodeError on Python 3 or when a unicode password was provided on Python 2. * CryptedFileKeyring now uses PBKDF2 to derive the key from the user's password and a random hash. The IV is chosen randomly as well. All the stored passwords are encrypted at once. Any keyrings using the old format will be automatically converted to the new format (but will no longer be compatible with 0.9 and earlier). The user's password is no longer limited to 32 characters. PyCrypto 2.5 or greater is now required for this keyring. --- 0.9 --- * Add support for GTK 3 and secret service D-Bus. Fixes #52. * Issue #60 - Use correct method for decoding. ----- 0.8.1 ----- * Fix regression in keyring lib on Windows XP where the LOCALAPPDATA environment variable is not present. --- 0.8 --- * Mac OS X keyring backend now uses subprocess calls to the `security` command instead of calling the API, which with the latest updates, no longer allows Python to invoke from a virtualenv. Fixes issue #13. * When using file-based storage, the keyring files are no longer stored in the user's home directory, but are instead stored in platform-friendly locations (`%localappdata%\Python Keyring` on Windows and according to the freedesktop.org Base Dir Specification (`$XDG_DATA_HOME/python_keyring` or `$HOME/.local/share/python_keyring`) on other operating systems). This fixes #21. *Backward Compatibility Notice* Due to the new storage location for file-based keyrings, keyring 0.8 supports backward compatibility by automatically moving the password files to the updated location. In general, users can upgrade to 0.8 and continue to operate normally. Any applications that customize the storage location or make assumptions about the storage location will need to take this change into consideration. Additionally, after upgrading to 0.8, it is not possible to downgrade to 0.7 without manually moving configuration files. In 1.0, the backward compatibilty will be removed. ----- 0.7.1 ----- * Removed non-ASCII characters from README and CHANGES docs (required by distutils if we're to include them in the long_description). Fixes #55. --- 0.7 --- * Python 3 is now supported. All tests now pass under Python 3.2 on Windows and Linux (although Linux backend support is limited). Fixes #28. * Extension modules on Mac and Windows replaced by pure-Python ctypes implementations. Thanks to Jerome Laheurte. * WinVaultKeyring now supports multiple passwords for the same service. Fixes #47. * Most of the tests don't require user interaction anymore. * Entries stored in Gnome Keyring appears now with a meaningful name if you try to browser your keyring (for ex. with Seahorse) * Tests from Gnome Keyring no longer pollute the user own keyring. * `keyring.util.escape` now accepts only unicode strings. Don't try to encode strings passed to it. ----- 0.6.2 ----- * fix compiling on OSX with XCode 4.0 ----- 0.6.1 ----- * Gnome keyring should not be used if there is no DISPLAY or if the dbus is not around (https://bugs.launchpad.net/launchpadlib/+bug/752282). --- 0.6 --- * Added `keyring.http` for facilitating HTTP Auth using keyring. * Add a utility to access the keyring from the command line. ----- 0.5.1 ----- * Remove a spurious KDE debug message when using KWallet * Fix a bug that caused an exception if the user canceled the KWallet dialog (https://bitbucket.org/kang/python-keyring-lib/issue/37/user-canceling-of-kde-wallet-dialogs). --- 0.5 --- * Now using the existing Gnome and KDE python libs instead of custom C++ code. * Using the getpass module instead of custom code --- 0.4 --- * Fixed the setup script (some subdirs were not included in the release.) --- 0.3 --- * Fixed keyring.core when the user doesn't have a cfg, or is not properly configured. * Fixed escaping issues for usernames with non-ascii characters --- 0.2 --- * Add support for Python 2.4+ http://bitbucket.org/kang/python-keyring-lib/issue/2 * Fix the bug in KDE Kwallet extension compiling http://bitbucket.org/kang/python-keyring-lib/issue/3 Keywords: keyring Keychain GnomeKeyring Kwallet password storage Platform: Many Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: License :: OSI Approved :: MIT License keyring-3.5/README.rst0000666000000000000000000002763312277422306013242 0ustar rootroot======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides a easy way to access the system keyring service from python. It can be used in any application that needs safe password storage. The keyring library is licensed under both the `MIT license `_ and the PSF license. The keyring services supported by the Python keyring lib: * **OSXKeychain**: supports the Keychain service in Mac OS X. * **KDEKWallet**: supports the KDE's Kwallet service. * **GnomeKeyring**: for Gnome 2 environment. * **SecretServiceKeyring**: for newer GNOME and KDE environments. * **WinVaultKeyring**: supports the Windows Credential Vault Besides these native password storing services provided by operating systems. Python keyring lib also provides following build-in keyrings. * **Win32CryptoKeyring**: for Windows 2k+. * **CryptedFileKeyring**: a command line interface keyring base on PyCrypto. * **UncryptedFileKeyring**: a keyring which leaves passwords directly in file. ------------------------- Installation Instructions ------------------------- easy_install or pip =================== Run easy_install or pip:: $ easy_install keyring $ pip install keyring Source installation =================== Download the source tarball, and uncompress it, then run the install command:: $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.3.tar.gz $ tar -xzvf keyring-0.3.tar.gz $ cd keyring-0.3 $ python setup.py install ------------- Using Keyring ------------- The basic usage of keyring is pretty simple: just call `keyring.set_password` and `keyring.get_password`: >>> import keyring >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends, including **OSX Keychain**, **Gnome Keyring**, **KDE Kwallet** and etc. The lib will automatically choose the keyring that is most suitable for your current environment. You can also specify the keyring you like to be used in the config file or by calling the ``set_keyring()`` function. Customize your keyring by config file ===================================== This section is about how to change your option in the config file. Config file path ---------------- The configuration of the lib is stored in a file named "keyringrc.cfg". This file must be found in a platform-specific location. To determine where the config file is stored, run the following:: python -c "import keyring.util.platform_; print(keyring.util.platform_.config_root())" Some keyrings also store the keyring data in the file system. To determine where the data files are stored, run this command:: python -c "import keyring.util.platform_; print(keyring.util.platform_.data_root())" Config file content ------------------- To specify a keyring backend, you need tell the lib the module name of the backend, such as ``keyring.backends.OS_X.Keyring``. If the backend is not shipped with the lib, in another word, is made by you own, you need also tell the lib the path of your own backend module. The module name should be written after the **default-keyring** option, while the module path belongs the **keyring-path** option. Here's a sample config file(The full demo can be accessed in the ``demo/keyring.py``): :: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=/home/kang/pyworkspace/python-keyring-lib/demo/ Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. By extending this base class and implementing the three functions ``supported()``, ``get_password()`` and ``set_password()``, you can easily create your own backend for keyring lib. The usage of the three functions: * ``supported(self)`` : Return if this backend is supported in current environment. The returned value can be **0**, **1** , or **-1**. **0** means suitable; **1** means recommended and **-1** means this backend is not available for current environment. * ``get_password(self, service, username)`` : Return the stored password for the ``username`` of the ``service``. * ``set_password(self, service, username, password)`` : Store the ``password`` for ``username`` of the ``service`` in the backend. * ``delete_password(self, service, username)`` : Delete the stored password for the ``username`` of the ``service``. For an instance, there's the source code of the demo mentioned above. It's a simple keyring which stores the password directly in memory. :: """ simplekeyring.py A simple keyring class for the keyring_demo.py Created by Kang Zhang on 2009-07-12 """ from keyring.backend import KeyringBackend class SimpleKeyring(KeyringBackend): """Simple Keyring is a keyring which can store only one password in memory. """ def __init__(self): self.password = '' def supported(self): return 0 def get_password(self, service, username): return self.password def set_password(self, service, username, password): self.password = password return 0 def delete_password(self, service, username): self.password = None Set the keyring in runtime ========================== Besides setting the backend through the config file, you can also set the backend to use by calling the api ``set_keyring()``. The backend you passed in will be used to store the password in your application. Here's a code snippet from the ``keyringdemo.py``. It shows the usage of ``set_keyring()`` :: # define a new keyring class which extends the KeyringBackend import keyring.backend class TestKeyring(keyring.backend.KeyringBackend): """A test keyring which always outputs same password """ def supported(self): return 0 def set_password(self, servicename, username, password): return 0 def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): return 0 # set the keyring for keyring lib import keyring keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print "password stored sucessfully" except keyring.backend.PasswordSetError: print "failed to store password" print "password", keyring.get_password("demo-service", "tarek") ----------------------------------------------- Integrate the keyring lib with your application ----------------------------------------------- API interface ============= The keyring lib has a few functions: * ``get_keyring()`` : Return the currently-loaded keyring implementation. * ``get_password(service, username)`` : Returns the password stored in keyring. If the password does not exist, it will return None. * ``set_password(service, username, password)`` : Store the password in the keyring. * ``delete_password(service, username)`` : Delete the password stored in keyring. If the password does not exist, it will raise an exception. Example ======= Here's an example of using keyring for application authorization. It can be found in the demo folder of the repository. Note that the faked auth function only returns true when the password equals to the username. :: """ auth_demo.py Created by Kang Zhang 2009-08-14 """ import keyring import getpass import ConfigParser def auth(username, password): """A faked authorization function. """ return username == password def main(): """This scrip demos how to use keyring facilite the authorization. The username is stored in a config named 'auth_demo.cfg' """ # config file init config_file = 'auth_demo.cfg' config = ConfigParser.SafeConfigParser({ 'username':'', }) config.read(config_file) if not config.has_section('auth_demo_login'): config.add_section('auth_demo_login') username = config.get('auth_demo_login','username') password = None if username != '': password = keyring.get_password('auth_demo_login', username) if password == None or not auth(username, password): while 1: username = raw_input("Username:\n") password = getpass.getpass("Password:\n") if auth(username, password): break else: print "Authorization failed." # store the username config.set('auth_demo_login', 'username', username) config.write(open(config_file, 'w')) # store the password keyring.set_password('auth_demo_login', username, password) # the stuff that needs authorization here print "Authorization successful." if __name__ == "__main__": main() ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: http://bitbucket.org/kang/python-keyring-lib/ * Bug Tracker: http://bitbucket.org/kang/python-keyring-lib/issues/ * Mailing list: http://groups.google.com/group/python-keyring Running Tests ============= Tests are `continuously run `_ using Travis-CI. |BuildStatus|_ .. |BuildStatus| image:: https://secure.travis-ci.org/jaraco/keyring.png .. _BuildStatus: http://travis-ci.org/jaraco/keyring To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Three recommended techniques are described below. Using pytest runner ------------------- Keyring is instrumented with `pytest runner `_. Thus, you may invoke the tests from any supported Python (with distribute installed) using this command:: python setup.py ptr pytest runner will download any unmet dependencies and run the tests using `pytest `_. This technique is the one used by the Travis-CI script. Using virtualenv and pytest/nose/unittest2 ------------------------------------------ Pytest and Nose are two popular test runners that will discover tests and run them. Unittest (unittest2 under Python 2.6) also has a mode to discover tests. First, however, these test runners typically need a test environment in which to run. It is recommended that you install keyring to a virtual environment to avoid interfering with your system environment. For more information, see the `virtualenv homepage `_. After you've created (or designated) your environment, install keyring into the environment by running:: python setup.py develop Then, invoke your favorite test runner, e.g.:: py.test or:: nosetests Using buildout -------------- Keyring supplies a buildout.cfg for use with buildout. If you have buildout installed, tests can be invoked as so:: 1. bin/buildout # prepare the buildout. 2. bin/test # execute the test runner. For more information about the options that the script provides do execute:: python bin/test --help ------- Credits ------- The project was based on Tarek Ziade's idea in `this post`_. Kang Zhang initially carried it out as a `Google Summer of Code`_ project, and Tarek mentored Kang on this project. .. _this post: http://tarekziade.wordpress.com/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/ .. _Google Summer of Code: http://socghop.appspot.com/ See CONTRIBUTORS.txt for a complete list of contributors. keyring-3.5/setup.cfg0000666000000000000000000000015612277424710013364 0ustar rootroot[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [pytest] norecursedirs = *.egg build dist keyring-3.5/PKG-INFO0000666000000000000000000011156012277424710012642 0ustar rootrootMetadata-Version: 1.1 Name: keyring Version: 3.5 Summary: Store and access your passwords safely. Home-page: http://bitbucket.org/kang/python-keyring-lib Author: Jason R. Coombs Author-email: jaraco@jaraco.com License: PSF and MIT Description: ======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides a easy way to access the system keyring service from python. It can be used in any application that needs safe password storage. The keyring library is licensed under both the `MIT license `_ and the PSF license. The keyring services supported by the Python keyring lib: * **OSXKeychain**: supports the Keychain service in Mac OS X. * **KDEKWallet**: supports the KDE's Kwallet service. * **GnomeKeyring**: for Gnome 2 environment. * **SecretServiceKeyring**: for newer GNOME and KDE environments. * **WinVaultKeyring**: supports the Windows Credential Vault Besides these native password storing services provided by operating systems. Python keyring lib also provides following build-in keyrings. * **Win32CryptoKeyring**: for Windows 2k+. * **CryptedFileKeyring**: a command line interface keyring base on PyCrypto. * **UncryptedFileKeyring**: a keyring which leaves passwords directly in file. ------------------------- Installation Instructions ------------------------- easy_install or pip =================== Run easy_install or pip:: $ easy_install keyring $ pip install keyring Source installation =================== Download the source tarball, and uncompress it, then run the install command:: $ wget http://pypi.python.org/packages/source/k/keyring/keyring-0.3.tar.gz $ tar -xzvf keyring-0.3.tar.gz $ cd keyring-0.3 $ python setup.py install ------------- Using Keyring ------------- The basic usage of keyring is pretty simple: just call `keyring.set_password` and `keyring.get_password`: >>> import keyring >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends, including **OSX Keychain**, **Gnome Keyring**, **KDE Kwallet** and etc. The lib will automatically choose the keyring that is most suitable for your current environment. You can also specify the keyring you like to be used in the config file or by calling the ``set_keyring()`` function. Customize your keyring by config file ===================================== This section is about how to change your option in the config file. Config file path ---------------- The configuration of the lib is stored in a file named "keyringrc.cfg". This file must be found in a platform-specific location. To determine where the config file is stored, run the following:: python -c "import keyring.util.platform_; print(keyring.util.platform_.config_root())" Some keyrings also store the keyring data in the file system. To determine where the data files are stored, run this command:: python -c "import keyring.util.platform_; print(keyring.util.platform_.data_root())" Config file content ------------------- To specify a keyring backend, you need tell the lib the module name of the backend, such as ``keyring.backends.OS_X.Keyring``. If the backend is not shipped with the lib, in another word, is made by you own, you need also tell the lib the path of your own backend module. The module name should be written after the **default-keyring** option, while the module path belongs the **keyring-path** option. Here's a sample config file(The full demo can be accessed in the ``demo/keyring.py``): :: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=/home/kang/pyworkspace/python-keyring-lib/demo/ Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. By extending this base class and implementing the three functions ``supported()``, ``get_password()`` and ``set_password()``, you can easily create your own backend for keyring lib. The usage of the three functions: * ``supported(self)`` : Return if this backend is supported in current environment. The returned value can be **0**, **1** , or **-1**. **0** means suitable; **1** means recommended and **-1** means this backend is not available for current environment. * ``get_password(self, service, username)`` : Return the stored password for the ``username`` of the ``service``. * ``set_password(self, service, username, password)`` : Store the ``password`` for ``username`` of the ``service`` in the backend. * ``delete_password(self, service, username)`` : Delete the stored password for the ``username`` of the ``service``. For an instance, there's the source code of the demo mentioned above. It's a simple keyring which stores the password directly in memory. :: """ simplekeyring.py A simple keyring class for the keyring_demo.py Created by Kang Zhang on 2009-07-12 """ from keyring.backend import KeyringBackend class SimpleKeyring(KeyringBackend): """Simple Keyring is a keyring which can store only one password in memory. """ def __init__(self): self.password = '' def supported(self): return 0 def get_password(self, service, username): return self.password def set_password(self, service, username, password): self.password = password return 0 def delete_password(self, service, username): self.password = None Set the keyring in runtime ========================== Besides setting the backend through the config file, you can also set the backend to use by calling the api ``set_keyring()``. The backend you passed in will be used to store the password in your application. Here's a code snippet from the ``keyringdemo.py``. It shows the usage of ``set_keyring()`` :: # define a new keyring class which extends the KeyringBackend import keyring.backend class TestKeyring(keyring.backend.KeyringBackend): """A test keyring which always outputs same password """ def supported(self): return 0 def set_password(self, servicename, username, password): return 0 def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): return 0 # set the keyring for keyring lib import keyring keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print "password stored sucessfully" except keyring.backend.PasswordSetError: print "failed to store password" print "password", keyring.get_password("demo-service", "tarek") ----------------------------------------------- Integrate the keyring lib with your application ----------------------------------------------- API interface ============= The keyring lib has a few functions: * ``get_keyring()`` : Return the currently-loaded keyring implementation. * ``get_password(service, username)`` : Returns the password stored in keyring. If the password does not exist, it will return None. * ``set_password(service, username, password)`` : Store the password in the keyring. * ``delete_password(service, username)`` : Delete the password stored in keyring. If the password does not exist, it will raise an exception. Example ======= Here's an example of using keyring for application authorization. It can be found in the demo folder of the repository. Note that the faked auth function only returns true when the password equals to the username. :: """ auth_demo.py Created by Kang Zhang 2009-08-14 """ import keyring import getpass import ConfigParser def auth(username, password): """A faked authorization function. """ return username == password def main(): """This scrip demos how to use keyring facilite the authorization. The username is stored in a config named 'auth_demo.cfg' """ # config file init config_file = 'auth_demo.cfg' config = ConfigParser.SafeConfigParser({ 'username':'', }) config.read(config_file) if not config.has_section('auth_demo_login'): config.add_section('auth_demo_login') username = config.get('auth_demo_login','username') password = None if username != '': password = keyring.get_password('auth_demo_login', username) if password == None or not auth(username, password): while 1: username = raw_input("Username:\n") password = getpass.getpass("Password:\n") if auth(username, password): break else: print "Authorization failed." # store the username config.set('auth_demo_login', 'username', username) config.write(open(config_file, 'w')) # store the password keyring.set_password('auth_demo_login', username, password) # the stuff that needs authorization here print "Authorization successful." if __name__ == "__main__": main() ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: http://bitbucket.org/kang/python-keyring-lib/ * Bug Tracker: http://bitbucket.org/kang/python-keyring-lib/issues/ * Mailing list: http://groups.google.com/group/python-keyring Running Tests ============= Tests are `continuously run `_ using Travis-CI. |BuildStatus|_ .. |BuildStatus| image:: https://secure.travis-ci.org/jaraco/keyring.png .. _BuildStatus: http://travis-ci.org/jaraco/keyring To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Three recommended techniques are described below. Using pytest runner ------------------- Keyring is instrumented with `pytest runner `_. Thus, you may invoke the tests from any supported Python (with distribute installed) using this command:: python setup.py ptr pytest runner will download any unmet dependencies and run the tests using `pytest `_. This technique is the one used by the Travis-CI script. Using virtualenv and pytest/nose/unittest2 ------------------------------------------ Pytest and Nose are two popular test runners that will discover tests and run them. Unittest (unittest2 under Python 2.6) also has a mode to discover tests. First, however, these test runners typically need a test environment in which to run. It is recommended that you install keyring to a virtual environment to avoid interfering with your system environment. For more information, see the `virtualenv homepage `_. After you've created (or designated) your environment, install keyring into the environment by running:: python setup.py develop Then, invoke your favorite test runner, e.g.:: py.test or:: nosetests Using buildout -------------- Keyring supplies a buildout.cfg for use with buildout. If you have buildout installed, tests can be invoked as so:: 1. bin/buildout # prepare the buildout. 2. bin/test # execute the test runner. For more information about the options that the script provides do execute:: python bin/test --help ------- Credits ------- The project was based on Tarek Ziade's idea in `this post`_. Kang Zhang initially carried it out as a `Google Summer of Code`_ project, and Tarek mentored Kang on this project. .. _this post: http://tarekziade.wordpress.com/2009/03/27/pycon-hallway-session-1-a-keyring-library-for-python/ .. _Google Summer of Code: http://socghop.appspot.com/ See CONTRIBUTORS.txt for a complete list of contributors. ======= CHANGES ======= --- 3.5 --- * Issue #49: Give the backend priorities a 1.5 multiplier bump when an XDG_CURRENT_DESKTOP environment variable matches the keyring's target environment. * Issue #99: Clarified documentation on location of config and data files. Prepared the code base to treat the two differently on Unix-based systems. For now, the behavior is unchanged. --- 3.4 --- * Extracted FileBacked and Encrypted base classes. * Add a pyinstaller hook to expose backend modules. Ref #124 * Pull request #41: Use errno module instead of hardcoding error codes. * SecretService backend: correctly handle cases when user dismissed the collection creation or unlock prompt. --- 3.3 --- * Pull request #40: KWallet backend will now honor the ``KDE_FULL_SESSION`` environment variable as found on openSUSE. ----- 3.2.1 ----- * SecretService backend: use a different function to check that the backend is functional. The default collection may not exist, but the collection will remain usable in that case. Also, make the error message more verbose. Resolves https://bugs.launchpad.net/bugs/1242412. --- 3.2 --- * Issue #120: Invoke KeyringBackend.priority during load_keyring to ensure that any keyring loaded is actually viable (or raises an informative exception). * File keyring: - Issue #123: fix removing items. - Correctly escape item name when removing. - Use with statement when working with files. * Add a test for removing one item in group. * Issue #81: Added experimental support for third-party backends. See `keyring.core._load_library_extensions` for information on supplying a third-party backend. --- 3.1 --- * All code now runs natively on both Python 2 and Python 3, no 2to3 conversion is required. * Testsuite: clean up, and make more use of unittest2 methods. ----- 3.0.5 ----- * Issue #114: Fix logic in pyfs detection. ----- 3.0.4 ----- * Issue #114: Fix detection of pyfs under Mercurial Demand Import. ----- 3.0.3 ----- * Simplified the implementation of ``keyring.core.load_keyring``. It now uses ``__import__`` instead of loading modules explicitly. The ``keyring_path`` parameter to ``load_keyring`` is now deprecated. Callers should instead ensure their module is available on ``sys.path`` before calling ``load_keyring``. Keyring still honors ``keyring-path``. This change fixes Issue #113 in which the explicit module loading of keyring modules was breaking package-relative imports. ----- 3.0.2 ----- * Renamed ``keyring.util.platform`` to ``keyring.util.platform_``. As reported in Issue #112 and `mercurial_keyring #31 `_ and in `Mercurial itself `_, Mercurial's Demand Import does not honor ``absolute_import`` directives, so it's not possible to have a module with the same name as another top-level module. A patch is in place to fix this issue upstream, but to support older Mercurial versions, this patch will remain for some time. ----- 3.0.1 ----- * Ensure that modules are actually imported even in Mercurial's Demand Import environment. --- 3.0 --- * Removed support for Python 2.5. * Removed names in ``keyring.backend`` moved in 1.1 and previously retained for compatibilty. ----- 2.1.1 ----- * Restored Python 2.5 compatibility (lost in 2.0). --- 2.1 --- * Issue #10: Added a 'store' attribute to the OS X Keyring, enabling custom instances of the KeyringBackend to use another store, such as the 'internet' store. For example:: keys = keyring.backends.OS_X.Keyring() keys.store = 'internet' keys.set_password(system, user, password) keys.get_password(system, user) The default for all instances can be set in the class:: keyring.backends.OS_X.Keyring.store = 'internet' * GnomeKeyring: fix availability checks, and make sure the warning message from pygobject is not printed. * Fixes to GnomeKeyring and SecretService tests. ----- 2.0.3 ----- * Issue #112: Backend viability/priority checks now are more aggressive about module presence checking, requesting ``__name__`` from imported modules to force the demand importer to actually attempt the import. ----- 2.0.2 ----- * Issue #111: Windows backend isn't viable on non-Windows platforms. ----- 2.0.1 ----- * Issue #110: Fix issues with ``Windows.RegistryKeyring``. --- 2.0 --- * Issue #80: Prioritized backend support. The primary interface for Keyring backend classes has been refactored to now emit a 'priority' based on the current environment (operating system, libraries available, etc). These priorities provide an indication of the applicability of that backend for the current environment. Users are still welcome to specify a particular backend in configuration, but the default behavior should now be to select the most appropriate backend by default. ----- 1.6.1 ----- * Only include pytest-runner in 'setup requirements' when ptr invocation is indicated in the command-line (Issue #105). --- 1.6 --- * GNOME Keyring backend: - Use the same attributes (``username`` / ``service``) as the SecretService backend uses, allow searching for old ones for compatibility. - Also set ``application`` attribute. - Correctly handle all types of errors, not only ``CANCELLED`` and ``NO_MATCH``. - Avoid printing warnings to stderr when GnomeKeyring is not available. * Secret Service backend: - Use a better label for passwords, the same as GNOME Keyring backend uses. --- 1.5 --- * SecretService: allow deleting items created using previous python-keyring versions. Before the switch to secretstorage, python-keyring didn't set "application" attribute. Now in addition to supporting searching for items without that attribute, python-keyring also supports deleting them. * Use ``secretstorage.get_default_collection`` if it's available. On secretstorage 1.0 or later, python-keyring now tries to create the default collection if it doesn't exist, instead of just raising the error. * Improvements for tests, including fix for Issue #102. --- 1.4 --- * Switch GnomeKeyring backend to use native libgnome-keyring via GObject Introspection, not the obsolete python-gnomekeyring module. --- 1.3 --- * Use the `SecretStorage library `_ to implement the Secret Service backend (instead of using dbus directly). Now the keyring supports prompting for and deleting passwords. Fixes #69, #77, and #93. * Catch `gnomekeyring.IOError` per the issue `reported in Nova client `_. * Issue #92 Added support for delete_password on Mac OS X Keychain. ----- 1.2.3 ----- * Fix for Encrypted File backend on Python 3. * Issue #97 Improved support for PyPy. ----- 1.2.2 ----- * Fixed handling situations when user cancels kwallet dialog or denies access for the app. ----- 1.2.1 ----- * Fix for kwallet delete. * Fix for OS X backend on Python 3. * Issue #84: Fix for Google backend on Python 3 (use of raw_input not caught by 2to3). --- 1.2 --- * Implemented delete_password on most keyrings. Keyring 2.0 will require delete_password to implement a Keyring. Fixes #79. ----- 1.1.2 ----- * Issue #78: pyfilesystem backend now works on Windows. ----- 1.1.1 ----- * Fixed MANIFEST.in so .rst files are included. --- 1.1 --- This is the last build that will support installation in a pure-distutils mode. Subsequent releases will require setuptools/distribute to install. Python 3 installs have always had this requirement (for 2to3 install support), but starting with the next minor release (1.2+), setuptools will be required. Additionally, this release has made some substantial refactoring in an attempt to modularize the backends. An attempt has been made to maintain 100% backward-compatibility, although if your library does anything fancy with module structure or clasess, some tweaking may be necessary. The backward-compatible references will be removed in 2.0, so the 1.1+ releases represent a transitional implementation which should work with both legacy and updated module structure. * Added a console-script 'keyring' invoking the command-line interface. * Deprecated _ExtensionKeyring. * Moved PasswordSetError and InitError to an `errors` module (references kept for backward-compatibility). * Moved concrete backend implementations into their own modules (references kept for backward compatibility): - OSXKeychain -> backends.OS_X.Keyring - GnomeKeyring -> backends.Gnome.Keyring - SecretServiceKeyring -> backends.SecretService.Keyring - KDEKWallet -> backends.kwallet.Keyring - BasicFileKeyring -> backends.file.BaseKeyring - CryptedFileKeyring -> backends.file.EncryptedKeyring - UncryptedFileKeyring -> backends.file.PlaintextKeyring - Win32CryptoKeyring -> backends.Windows.EncryptedKeyring - WinVaultKeyring -> backends.Windows.WinVaultKeyring - Win32CryptoRegistry -> backends.Windows.RegistryKeyring - select_windows_backend -> backends.Windows.select_windows_backend - GoogleDocsKeyring -> backends.Google.DocsKeyring - Credential -> keyring.credentials.Credential - BaseCredential -> keyring.credentials.SimpleCredential - EnvironCredential -> keyring.credentials.EnvironCredential - GoogleEnvironCredential -> backends.Google.EnvironCredential - BaseKeyczarCrypter -> backends.keyczar.BaseCrypter - KeyczarCrypter -> backends.keyczar.Crypter - EnvironKeyczarCrypter -> backends.keyczar.EnvironCrypter - EnvironGoogleDocsKeyring -> backends.Google.KeyczarDocsKeyring - BasicPyfilesystemKeyring -> backends.pyfs.BasicKeyring - UnencryptedPyfilesystemKeyring -> backends.pyfs.PlaintextKeyring - EncryptedPyfilesystemKeyring -> backends.pyfs.EncryptedKeyring - EnvironEncryptedPyfilesystemKeyring -> backends.pyfs.KeyczarKeyring - MultipartKeyringWrapper -> backends.multi.MultipartKeyringWrapper * Officially require Python 2.5 or greater (although unofficially, this requirement has been in place since 0.10). --- 1.0 --- This backward-incompatible release attempts to remove some cruft from the codebase that's accumulated over the versions. * Removed legacy file relocation support. `keyring` no longer supports loading configuration or file-based backends from ~. If upgrading from 0.8 or later, the files should already have been migrated to their new proper locations. If upgrading from 0.7.x or earlier, the files will have to be migrated manually. * Removed CryptedFileKeyring migration support. To maintain an existing CryptedFileKeyring, one must first upgrade to 0.9.2 or later and access the keyring before upgrading to 1.0 to retain the existing keyring. * File System backends now create files without group and world permissions. Fixes #67. ------ 0.10.1 ------ * Merged 0.9.3 to include fix for #75. ---- 0.10 ---- * Add support for using `Keyczar `_ to encrypt keyrings. Keyczar is "an open source cryptographic toolkit designed to make it easier and safer for developers to use cryptography in their applications." * Added support for storing keyrings on Google Docs or any other filesystem supported by pyfilesystem. * Fixed issue in Gnome Keyring when unicode is passed as the service name, username, or password. * Tweaked SecretService code to pass unicode to DBus, as unicode is the preferred format. * Issue #71 - Fixed logic in CryptedFileKeyring. * Unencrypted keyring file will be saved with user read/write (and not group or world read/write). ----- 0.9.3 ----- * Ensure migration is run when get_password is called. Fixes #75. Thanks to Marc Deslauriers for reporting the bug and supplying the patch. ----- 0.9.2 ----- * Keyring 0.9.1 introduced a whole different storage format for the CryptedFileKeyring, but this introduced some potential compatibility issues. This release incorporates the security updates but reverts to the INI file format for storage, only encrypting the passwords and leaving the service and usernames in plaintext. Subsequent releases may incorporate a new keyring to implement a whole-file encrypted version. Fixes #64. * The CryptedFileKeyring now requires simplejson for Python 2.5 clients. ----- 0.9.1 ----- * Fix for issue where SecretServiceBackend.set_password would raise a UnicodeError on Python 3 or when a unicode password was provided on Python 2. * CryptedFileKeyring now uses PBKDF2 to derive the key from the user's password and a random hash. The IV is chosen randomly as well. All the stored passwords are encrypted at once. Any keyrings using the old format will be automatically converted to the new format (but will no longer be compatible with 0.9 and earlier). The user's password is no longer limited to 32 characters. PyCrypto 2.5 or greater is now required for this keyring. --- 0.9 --- * Add support for GTK 3 and secret service D-Bus. Fixes #52. * Issue #60 - Use correct method for decoding. ----- 0.8.1 ----- * Fix regression in keyring lib on Windows XP where the LOCALAPPDATA environment variable is not present. --- 0.8 --- * Mac OS X keyring backend now uses subprocess calls to the `security` command instead of calling the API, which with the latest updates, no longer allows Python to invoke from a virtualenv. Fixes issue #13. * When using file-based storage, the keyring files are no longer stored in the user's home directory, but are instead stored in platform-friendly locations (`%localappdata%\Python Keyring` on Windows and according to the freedesktop.org Base Dir Specification (`$XDG_DATA_HOME/python_keyring` or `$HOME/.local/share/python_keyring`) on other operating systems). This fixes #21. *Backward Compatibility Notice* Due to the new storage location for file-based keyrings, keyring 0.8 supports backward compatibility by automatically moving the password files to the updated location. In general, users can upgrade to 0.8 and continue to operate normally. Any applications that customize the storage location or make assumptions about the storage location will need to take this change into consideration. Additionally, after upgrading to 0.8, it is not possible to downgrade to 0.7 without manually moving configuration files. In 1.0, the backward compatibilty will be removed. ----- 0.7.1 ----- * Removed non-ASCII characters from README and CHANGES docs (required by distutils if we're to include them in the long_description). Fixes #55. --- 0.7 --- * Python 3 is now supported. All tests now pass under Python 3.2 on Windows and Linux (although Linux backend support is limited). Fixes #28. * Extension modules on Mac and Windows replaced by pure-Python ctypes implementations. Thanks to Jerome Laheurte. * WinVaultKeyring now supports multiple passwords for the same service. Fixes #47. * Most of the tests don't require user interaction anymore. * Entries stored in Gnome Keyring appears now with a meaningful name if you try to browser your keyring (for ex. with Seahorse) * Tests from Gnome Keyring no longer pollute the user own keyring. * `keyring.util.escape` now accepts only unicode strings. Don't try to encode strings passed to it. ----- 0.6.2 ----- * fix compiling on OSX with XCode 4.0 ----- 0.6.1 ----- * Gnome keyring should not be used if there is no DISPLAY or if the dbus is not around (https://bugs.launchpad.net/launchpadlib/+bug/752282). --- 0.6 --- * Added `keyring.http` for facilitating HTTP Auth using keyring. * Add a utility to access the keyring from the command line. ----- 0.5.1 ----- * Remove a spurious KDE debug message when using KWallet * Fix a bug that caused an exception if the user canceled the KWallet dialog (https://bitbucket.org/kang/python-keyring-lib/issue/37/user-canceling-of-kde-wallet-dialogs). --- 0.5 --- * Now using the existing Gnome and KDE python libs instead of custom C++ code. * Using the getpass module instead of custom code --- 0.4 --- * Fixed the setup script (some subdirs were not included in the release.) --- 0.3 --- * Fixed keyring.core when the user doesn't have a cfg, or is not properly configured. * Fixed escaping issues for usernames with non-ascii characters --- 0.2 --- * Add support for Python 2.4+ http://bitbucket.org/kang/python-keyring-lib/issue/2 * Fix the bug in KDE Kwallet extension compiling http://bitbucket.org/kang/python-keyring-lib/issue/3 Keywords: keyring Keychain GnomeKeyring Kwallet password storage Platform: Many Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: License :: OSI Approved :: MIT License keyring-3.5/keyring/0000775000000000000000000000000012300426570013200 5ustar rootrootkeyring-3.5/keyring/backend.py0000666000000000000000000000726612273742000015154 0ustar rootroot""" Keyring implementation support """ from __future__ import absolute_import import abc from keyring import errors from keyring.util import properties from keyring.py27compat import add_metaclass, filter import keyring.util class KeyringBackendMeta(abc.ABCMeta): """ A metaclass that's both an ABCMeta and a type that keeps a registry of all (non-abstract) types. """ def __init__(cls, name, bases, dict): super(KeyringBackendMeta, cls).__init__(name, bases, dict) if not hasattr(cls, '_classes'): cls._classes = set() classes = cls._classes if not cls.__abstractmethods__: classes.add(cls) @add_metaclass(KeyringBackendMeta) class KeyringBackend(object): """The abstract base class of the keyring, every backend must implement this interface. """ #@abc.abstractproperty def priority(cls): """ Each backend class must supply a priority, a number (float or integer) indicating the priority of the backend relative to all other backends. The priority need not be static -- it may (and should) vary based attributes of the environment in which is runs (platform, available packages, etc.). A higher number indicates a higher priority. The priority should raise a RuntimeError with a message indicating the underlying cause if the backend is not suitable for the current environment. As a rule of thumb, a priority between zero but less than one is suitable, but a priority of one or greater is recommended. """ @properties.ClassProperty @classmethod def viable(cls): with errors.ExceptionRaisedContext() as exc: cls.priority return not bool(exc) @abc.abstractmethod def get_password(self, service, username): """Get password of the username for the service """ return None @abc.abstractmethod def set_password(self, service, username, password): """Set password for the username of the service """ raise errors.PasswordSetError("reason") # for backward-compatibility, don't require a backend to implement # delete_password #@abc.abstractmethod def delete_password(self, service, username): """Delete the password for the username of the service. """ raise errors.PasswordDeleteError("reason") class Crypter(object): """Base class providing encryption and decryption """ @abc.abstractmethod def encrypt(self, value): """Encrypt the value. """ pass @abc.abstractmethod def decrypt(self, value): """Decrypt the value. """ pass class NullCrypter(Crypter): """A crypter that does nothing """ def encrypt(self, value): return value def decrypt(self, value): return value @keyring.util.once def get_all_keyring(): """ Return a list of all implemented keyrings that can be constructed without parameters. """ # ensure that all keyring backends are loaded for mod_name in ('file', 'Gnome', 'Google', 'keyczar', 'kwallet', 'multi', 'OS_X', 'pyfs', 'SecretService', 'Windows'): # use fromlist to cause the module to resolve under Demand Import __import__('keyring.backends.'+mod_name, fromlist=('__name__',)) def is_class_viable(keyring_cls): try: keyring_cls.priority except RuntimeError: return False return True all_classes = KeyringBackend._classes viable_classes = filter(is_class_viable, all_classes) return list(keyring.util.suppress_exceptions(viable_classes, exceptions=TypeError)) keyring-3.5/keyring/backends/0000775000000000000000000000000012300426570014752 5ustar rootrootkeyring-3.5/keyring/backends/_win_crypto.py0000666000000000000000000000662512246200106017665 0ustar rootroot from ctypes import Structure, POINTER, c_void_p, cast, create_string_buffer, \ c_char_p, byref, memmove from ctypes import windll, WinDLL, WINFUNCTYPE try: from ctypes import wintypes except ValueError: # see http://bugs.python.org/issue16396 raise ImportError("wintypes") from keyring.util.escape import u # Crypto API ctypes bindings class DATA_BLOB(Structure): _fields_ = [('cbData', wintypes.DWORD), ('pbData', POINTER(wintypes.BYTE))] class CRYPTPROTECT_PROMPTSTRUCT(Structure): _fields_ = [('cbSize', wintypes.DWORD), ('dwPromptFlags', wintypes.DWORD), ('hwndApp', wintypes.HWND), ('szPrompt', POINTER(wintypes.WCHAR))] # Flags for CRYPTPROTECT_PROMPTSTRUCT CRYPTPROTECT_PROMPT_ON_UNPROTECT = 1 CRYPTPROTECT_PROMPT_ON_PROTECT = 2 # Flags for CryptProtectData/CryptUnprotectData CRYPTPROTECT_UI_FORBIDDEN = 0x01 CRYPTPROTECT_LOCAL_MACHINE = 0x04 CRYPTPROTECT_CRED_SYNC = 0x08 CRYPTPROTECT_AUDIT = 0x10 CRYPTPROTECT_NO_RECOVERY = 0x20 CRYPTPROTECT_VERIFY_PROTECTION = 0x40 CRYPTPROTECT_CRED_REGENERATE = 0x80 # Crypto API Functions _dll = WinDLL('CRYPT32.DLL') CryptProtectData = WINFUNCTYPE(wintypes.BOOL, POINTER(DATA_BLOB), POINTER(wintypes.WCHAR), POINTER(DATA_BLOB), c_void_p, POINTER(CRYPTPROTECT_PROMPTSTRUCT), wintypes.DWORD, POINTER(DATA_BLOB))(('CryptProtectData', _dll)) CryptUnprotectData = WINFUNCTYPE(wintypes.BOOL, POINTER(DATA_BLOB), POINTER(wintypes.WCHAR), POINTER(DATA_BLOB), c_void_p, POINTER(CRYPTPROTECT_PROMPTSTRUCT), wintypes.DWORD, POINTER(DATA_BLOB))( ('CryptUnprotectData', _dll)) # Functions def encrypt(data, non_interactive=0): blobin = DATA_BLOB(cbData=len(data), pbData=cast(c_char_p(data), POINTER(wintypes.BYTE))) blobout = DATA_BLOB() if not CryptProtectData(byref(blobin), u('python-keyring-lib.win32crypto'), None, None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blobout)): raise OSError("Can't encrypt") encrypted = create_string_buffer(blobout.cbData) memmove(encrypted, blobout.pbData, blobout.cbData) windll.kernel32.LocalFree(blobout.pbData) return encrypted.raw def decrypt(encrypted, non_interactive=0): blobin = DATA_BLOB(cbData=len(encrypted), pbData=cast(c_char_p(encrypted), POINTER(wintypes.BYTE))) blobout = DATA_BLOB() if not CryptUnprotectData(byref(blobin), u('python-keyring-lib.win32crypto'), None, None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blobout)): raise OSError("Can't decrypt") data = create_string_buffer(blobout.cbData) memmove(data, blobout.pbData, blobout.cbData) windll.kernel32.LocalFree(blobout.pbData) return data.raw keyring-3.5/keyring/backends/multi.py0000666000000000000000000000421312273742000016456 0ustar rootrootimport itertools from keyring.util import properties from keyring.backend import KeyringBackend from keyring import errors class MultipartKeyringWrapper(KeyringBackend): """A wrapper around an existing keyring that breaks the password into smaller parts to handle implementations that have limits on the maximum length of passwords i.e. Windows Vault """ def __init__(self, keyring, max_password_size=512): self._keyring = keyring self._max_password_size = max_password_size @properties.ClassProperty @classmethod def priority(cls): return 0 def get_password(self, service, username): """Get password of the username for the service """ init_part = self._keyring.get_password(service, username) if init_part: parts = [init_part,] i = 1 while True: next_part = self._keyring.get_password( service, '%s{{part_%d}}' %(username, i)) if next_part: parts.append(next_part) i += 1 else: break return ''.join(parts) return None def set_password(self, service, username, password): """Set password for the username of the service """ segments = range(0, len(password), self._max_password_size) password_parts = [ password[i:i + self._max_password_size] for i in segments] for i, password_part in enumerate(password_parts): curr_username = username if i > 0: curr_username += '{{part_%d}}' %i self._keyring.set_password(service, curr_username, password_part) def delete_password(self, service, username): self._keyring.delete_password(service, username) count = itertools.count(1) while True: part_name = '%(username)s{{part_%(index)d}}' % dict( index = next(count), **vars()) try: self._keyring.delete_password(service, part_name) except errors.PasswordDeleteError: break keyring-3.5/keyring/backends/SecretService.py0000666000000000000000000000537212277420362020111 0ustar rootrootimport logging from keyring.util import properties from keyring.util import XDG from keyring.backend import KeyringBackend from keyring.errors import (InitError, PasswordDeleteError, ExceptionRaisedContext) try: import secretstorage import secretstorage.exceptions as exceptions except ImportError: pass log = logging.getLogger(__name__) class Keyring(KeyringBackend): """Secret Service Keyring""" @properties.ClassProperty @classmethod @XDG.Preference('Gnome') def priority(cls): with ExceptionRaisedContext() as exc: secretstorage.__name__ if exc: raise RuntimeError("SecretStorage required") if not hasattr(secretstorage, 'get_default_collection'): raise RuntimeError("SecretStorage 1.0 or newer required") try: bus = secretstorage.dbus_init() list(secretstorage.get_all_collections(bus)) except exceptions.SecretServiceNotAvailableException as e: raise RuntimeError( "Unable to initialize SecretService: %s" % e) return 5 def get_default_collection(self): bus = secretstorage.dbus_init() try: collection = secretstorage.get_default_collection(bus) except exceptions.SecretStorageException as e: raise InitError("Failed to create the collection: %s." % e) if collection.is_locked(): collection.unlock() if collection.is_locked(): # User dismissed the prompt raise InitError("Failed to unlock the collection!") return collection def get_password(self, service, username): """Get password of the username for the service """ collection = self.get_default_collection() items = collection.search_items( {"username": username, "service": service}) for item in items: return item.get_secret().decode('utf-8') def set_password(self, service, username, password): """Set password for the username of the service """ collection = self.get_default_collection() attributes = { "application": "python-keyring", "service": service, "username": username } label = "Password for '%s' on '%s'" % (username, service) collection.create_item(label, attributes, password, replace=True) def delete_password(self, service, username): """Delete the stored password (only the first one) """ collection = self.get_default_collection() items = collection.search_items( {"username": username, "service": service}) for item in items: return item.delete() raise PasswordDeleteError("No such password!") keyring-3.5/keyring/backends/file.py0000666000000000000000000002307112273742000016246 0ustar rootrootfrom __future__ import with_statement import os import getpass import base64 import sys import json import abc from ..py27compat import configparser import keyring.util.platform_ from keyring.errors import PasswordDeleteError from keyring.backend import KeyringBackend from keyring.util import properties from keyring.util.escape import escape as escape_for_ini class FileBacked(object): @abc.abstractproperty def filename(self): """ The filename used to store the passwords. """ @properties.NonDataProperty def file_path(self): """ The path to the file where passwords are stored. This property may be overridden by the subclass or at the instance level. """ return os.path.join(keyring.util.platform_.data_root(), self.filename) class BaseKeyring(FileBacked, KeyringBackend): """ BaseKeyring is a file-based implementation of keyring. This keyring stores the password directly in the file and provides methods which may be overridden by subclasses to support encryption and decryption. The encrypted payload is stored in base64 format. """ @abc.abstractmethod def encrypt(self, password): """ Given a password (byte string), return an encrypted byte string. """ @abc.abstractmethod def decrypt(self, password_encrypted): """ Given a password encrypted by a previous call to `encrypt`, return the original byte string. """ def get_password(self, service, username): """ Read the password from the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # load the passwords from the file config = configparser.RawConfigParser() if os.path.exists(self.file_path): config.read(self.file_path) # fetch the password try: password_base64 = config.get(service, username).encode() # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = self.decrypt(password_encrypted).decode('utf-8') except (configparser.NoOptionError, configparser.NoSectionError): password = None return password def set_password(self, service, username, password): """Write the password in the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # encrypt the password password_encrypted = self.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted).decode() # ensure the file exists self._ensure_file_path() # load the keyring from the disk config = configparser.RawConfigParser() config.read(self.file_path) # update the keyring with the password if not config.has_section(service): config.add_section(service) config.set(service, username, password_base64) # save the keyring back to the file with open(self.file_path, 'w') as config_file: config.write(config_file) def _ensure_file_path(self): """ Ensure the storage path exists. If it doesn't, create it with "go-rwx" permissions. """ storage_root = os.path.dirname(self.file_path) if storage_root and not os.path.isdir(storage_root): os.makedirs(storage_root) if not os.path.isfile(self.file_path): # create the file without group/world permissions with open(self.file_path, 'w'): pass user_read_write = 0o600 os.chmod(self.file_path, user_read_write) def delete_password(self, service, username): """Delete the password for the username of the service. """ service = escape_for_ini(service) username = escape_for_ini(username) config = configparser.RawConfigParser() if os.path.exists(self.file_path): config.read(self.file_path) try: if not config.remove_option(service, username): raise PasswordDeleteError("Password not found") except configparser.NoSectionError: raise PasswordDeleteError("Password not found") # update the file with open(self.file_path, 'w') as config_file: config.write(config_file) class PlaintextKeyring(BaseKeyring): """Simple File Keyring with no encryption""" priority = .5 "Applicable for all platforms, but not recommended" filename = 'keyring_pass.cfg' def encrypt(self, password): """Directly return the password itself. """ return password def decrypt(self, password_encrypted): """Directly return encrypted password. """ return password_encrypted class Encrypted(object): """ PyCrypto-backed Encryption support """ block_size = 32 def _create_cipher(self, password, salt, IV): """ Create the cipher object to encrypt or decrypt a payload. """ from Crypto.Protocol.KDF import PBKDF2 from Crypto.Cipher import AES pw = PBKDF2(password, salt, dkLen=self.block_size) return AES.new(pw[:self.block_size], AES.MODE_CFB, IV) def _get_new_password(self): while True: password = getpass.getpass( "Please set a password for your new keyring: ") confirm = getpass.getpass('Please confirm the password: ') if password != confirm: sys.stderr.write("Error: Your passwords didn't match\n") continue if '' == password.strip(): # forbid the blank password sys.stderr.write("Error: blank passwords aren't allowed.\n") continue return password class EncryptedKeyring(Encrypted, BaseKeyring): """PyCrypto File Keyring""" filename = 'crypted_pass.cfg' pw_prefix = 'pw:'.encode() @properties.ClassProperty @classmethod def priority(self): "Applicable for all platforms, but not recommended." try: __import__('Crypto.Cipher.AES') __import__('Crypto.Protocol.KDF') __import__('Crypto.Random') except ImportError: raise RuntimeError("PyCrypto required") if not json: raise RuntimeError("JSON implementation such as simplejson " "required.") return .6 @properties.NonDataProperty def keyring_key(self): # _unlock or _init_file will set the key or raise an exception if self._check_file(): self._unlock() else: self._init_file() return self.keyring_key def _init_file(self): """ Initialize a new password file and set the reference password. """ self.keyring_key = self._get_new_password() # set a reference password, used to check that the password provided # matches for subsequent checks. self.set_password('keyring-setting', 'password reference', 'password reference value') def _check_file(self): """ Check if the file exists and has the expected password reference. """ if not os.path.exists(self.file_path): return False self._migrate() config = configparser.RawConfigParser() config.read(self.file_path) try: config.get( escape_for_ini('keyring-setting'), escape_for_ini('password reference'), ) except (configparser.NoSectionError, configparser.NoOptionError): return False return True def _unlock(self): """ Unlock this keyring by getting the password for the keyring from the user. """ self.keyring_key = getpass.getpass( 'Please enter password for encrypted keyring: ') try: ref_pw = self.get_password('keyring-setting', 'password reference') assert ref_pw == 'password reference value' except AssertionError: self._lock() raise ValueError("Incorrect Password") def _lock(self): """ Remove the keyring key from this instance. """ del self.keyring_key def encrypt(self, password): from Crypto.Random import get_random_bytes salt = get_random_bytes(self.block_size) from Crypto.Cipher import AES IV = get_random_bytes(AES.block_size) cipher = self._create_cipher(self.keyring_key, salt, IV) password_encrypted = cipher.encrypt(self.pw_prefix + password) # Serialize the salt, IV, and encrypted password in a secure format data = dict( salt=salt, IV=IV, password_encrypted=password_encrypted, ) for key in data: data[key] = base64.encodestring(data[key]).decode() return json.dumps(data).encode() def decrypt(self, password_encrypted): # unpack the encrypted payload data = json.loads(password_encrypted.decode()) for key in data: data[key] = base64.decodestring(data[key].encode()) cipher = self._create_cipher(self.keyring_key, data['salt'], data['IV']) plaintext = cipher.decrypt(data['password_encrypted']) assert plaintext.startswith(self.pw_prefix) return plaintext[3:] def _migrate(self, keyring_password=None): """ Convert older keyrings to the current format. """ keyring-3.5/keyring/backends/keyczar.py0000666000000000000000000000560512273742000017002 0ustar rootrootfrom __future__ import absolute_import import os import abc try: from keyczar import keyczar except ImportError: pass from keyring.backend import Crypter from keyring import errors def has_keyczar(): with errors.ExceptionRaisedContext() as exc: keyczar.__name__ return not bool(exc) class BaseCrypter(Crypter): """Base Keyczar keyset encryption and decryption. The keyset initialisation is deferred until required. """ @abc.abstractproperty def keyset_location(self): """Location for the main keyset that may be encrypted or not""" pass @abc.abstractproperty def encrypting_keyset_location(self): """Location for the encrypting keyset. Use None to indicate that the main keyset is not encrypted """ pass @property def crypter(self): """The actual keyczar crypter""" if not hasattr(self, '_crypter'): # initialise the Keyczar keysets if not self.keyset_location: raise ValueError('No encrypted keyset location!') reader = keyczar.readers.CreateReader(self.keyset_location) if self.encrypting_keyset_location: encrypting_keyczar = keyczar.Crypter.Read( self.encrypting_keyset_location) reader = keyczar.readers.EncryptedReader(reader, encrypting_keyczar) self._crypter = keyczar.Crypter(reader) return self._crypter def encrypt(self, value): """Encrypt the value. """ if not value: return '' return self.crypter.Encrypt(value) def decrypt(self, value): """Decrypt the value. """ if not value: return '' return self.crypter.Decrypt(value) class Crypter(BaseCrypter): """A Keyczar crypter using locations specified in the constructor """ def __init__(self, keyset_location, encrypting_keyset_location=None): self._keyset_location = keyset_location self._encrypting_keyset_location = encrypting_keyset_location @property def keyset_location(self): return self._keyset_location @property def encrypting_keyset_location(self): return self._encrypting_keyset_location class EnvironCrypter(BaseCrypter): """A Keyczar crypter using locations specified by environment vars """ KEYSET_ENV_VAR = 'KEYRING_KEYCZAR_ENCRYPTED_LOCATION' ENC_KEYSET_ENV_VAR = 'KEYRING_KEYCZAR_ENCRYPTING_LOCATION' @property def keyset_location(self): val = os.environ.get(self.KEYSET_ENV_VAR) if not val: raise ValueError('%s environment value not set' % self.KEYSET_ENV_VAR) return val @property def encrypting_keyset_location(self): return os.environ.get(self.ENC_KEYSET_ENV_VAR) keyring-3.5/keyring/backends/Google.py0000666000000000000000000003010312273742000016535 0ustar rootrootfrom __future__ import absolute_import import os import sys import copy import codecs import base64 import io try: import gdata.docs.service except ImportError: pass from . import keyczar from keyring import errors from keyring import credentials from keyring.py27compat import input, pickle from keyring.backend import KeyringBackend from keyring.util import properties from keyring.errors import ExceptionRaisedContext class EnvironCredential(credentials.EnvironCredential): """Retrieve credentials from specifically named environment variables """ def __init__(self): super(EnvironCredential, self).__init__('GOOGLE_KEYRING_USER', 'GOOGLE_KEYRING_PASSWORD') class DocsKeyring(KeyringBackend): """Backend that stores keyring on Google Docs. Note that login and any other initialisation is deferred until it is actually required to allow this keyring class to be added to the global _all_keyring list. """ keyring_title = 'GoogleKeyring' # status enums OK = 1 FAIL = 0 CONFLICT = -1 def __init__(self, credential, source, crypter, collection=None, client=None, can_create=True, input_getter=input ): self.credential = credential self.crypter = crypter self.source = source self._collection = collection self.can_create = can_create self.input_getter = input_getter self._keyring_dict = None if not client: self._client = gdata.docs.service.DocsService() else: self._client = client self._client.source = source self._client.ssl = True self._login_reqd = True @properties.ClassProperty @classmethod def priority(cls): if not cls._has_gdata(): raise RuntimeError("Requires gdata") if not keyczar.has_keyczar(): raise RuntimeError("Requires keyczar") return 3 @classmethod def _has_gdata(cls): with ExceptionRaisedContext() as exc: gdata.__name__ return not bool(exc) def get_password(self, service, username): """Get password of the username for the service """ result = self._get_entry(self._keyring, service, username) if result: result = self._decrypt(result) return result def set_password(self, service, username, password): """Set password for the username of the service """ password = self._encrypt(password or '') keyring_working_copy = copy.deepcopy(self._keyring) service_entries = keyring_working_copy.get(service) if not service_entries: service_entries = {} keyring_working_copy[service] = service_entries service_entries[username] = password save_result = self._save_keyring(keyring_working_copy) if save_result == self.OK: self._keyring_dict = keyring_working_copy return elif save_result == self.CONFLICT: # check if we can avoid updating self.docs_entry, keyring_dict = self._read() existing_pwd = self._get_entry(self._keyring, service, username) conflicting_pwd = self._get_entry(keyring_dict, service, username) if conflicting_pwd == password: # if someone else updated it to the same value then we are done self._keyring_dict = keyring_working_copy return elif conflicting_pwd is None or conflicting_pwd == existing_pwd: # if doesn't already exist or is unchanged then update it new_service_entries = keyring_dict.get(service, {}) new_service_entries[username] = password keyring_dict[service] = new_service_entries save_result = self._save_keyring(keyring_dict) if save_result == self.OK: self._keyring_dict = keyring_dict return else: raise errors.PasswordSetError( 'Failed write after conflict detected') else: raise errors.PasswordSetError( 'Conflict detected, service:%s and username:%s was '\ 'set to a different value by someone else' %(service, username)) raise errors.PasswordSetError('Could not save keyring') def delete_password(self, service, username): return self._del_entry(self._keyring, service, username) @property def client(self): if not self._client.GetClientLoginToken(): try: self._client.ClientLogin(self.credential.username, self.credential.password, self._client.source) except gdata.service.CaptchaRequired: sys.stdout.write('Please visit ' + self._client.captcha_url) answer = self.input_getter('Answer to the challenge? ') self._client.email = self.credential.username self._client.password = self.credential.password self._client.ClientLogin( self.credential.username, self.credential.password, self._client.source, captcha_token=self._client.captcha_token, captcha_response=answer) except gdata.service.BadAuthentication: raise errors.InitError('Users credential were unrecognized') except gdata.service.Error: raise errors.InitError('Login Error') return self._client @property def collection(self): return self._collection or self.credential.username.split('@')[0] @property def _keyring(self): if self._keyring_dict is None: self.docs_entry, self._keyring_dict = self._read() return self._keyring_dict def _get_entry(self, keyring_dict, service, username): result = None service_entries = keyring_dict.get(service) if service_entries: result = service_entries.get(username) return result def _del_entry(self, keyring_dict, service, username): service_entries = keyring_dict.get(service) if not service_entries: raise errors.PasswordDeleteError("No matching service") try: del service_entries[username] except KeyError: raise errors.PasswordDeleteError("Not found") if not service_entries: del keyring_dict[service] def _decrypt(self, value): if not value: return '' return self.crypter.decrypt(value) def _encrypt(self, value): if not value: return '' return self.crypter.encrypt(value) def _get_doc_title(self): return '%s' %self.keyring_title def _read(self): from gdata.docs.service import DocumentQuery title_query = DocumentQuery(categories=[self.collection]) title_query['title'] = self._get_doc_title() title_query['title-exact'] = 'true' docs = self.client.QueryDocumentListFeed(title_query.ToUri()) if not docs.entry: if self.can_create: docs_entry = None keyring_dict = {} else: raise errors.InitError( '%s not found in %s and create not permitted' %(self._get_doc_title(), self.collection)) else: docs_entry = docs.entry[0] file_contents = '' try: url = docs_entry.content.src url += '&exportFormat=txt' server_response = self.client.request('GET', url) if server_response.status != 200: raise errors.InitError( 'Could not read existing Google Docs keyring') file_contents = server_response.read() if file_contents.startswith(codecs.BOM_UTF8): file_contents = file_contents[len(codecs.BOM_UTF8):] keyring_dict = pickle.loads(base64.urlsafe_b64decode( file_contents.decode('string-escape'))) except pickle.UnpicklingError as ex: raise errors.InitError( 'Could not unpickle existing Google Docs keyring', ex) except TypeError as ex: raise errors.InitError( 'Could not decode existing Google Docs keyring', ex) return docs_entry, keyring_dict def _save_keyring(self, keyring_dict): """Helper to actually write the keyring to Google""" import gdata result = self.OK file_contents = base64.urlsafe_b64encode(pickle.dumps(keyring_dict)) try: if self.docs_entry: extra_headers = {'Content-Type': 'text/plain', 'Content-Length': len(file_contents)} self.docs_entry = self.client.Put( file_contents, self.docs_entry.GetEditMediaLink().href, extra_headers=extra_headers ) else: from gdata.docs.service import DocumentQuery # check for existence of folder, create if required folder_query = DocumentQuery(categories=['folder']) folder_query['title'] = self.collection folder_query['title-exact'] = 'true' docs = self.client.QueryDocumentListFeed(folder_query.ToUri()) if docs.entry: folder_entry = docs.entry[0] else: folder_entry = self.client.CreateFolder(self.collection) file_handle = io.BytesIO(file_contents) media_source = gdata.MediaSource( file_handle=file_handle, content_type='text/plain', content_length=len(file_contents), file_name='temp') self.docs_entry = self.client.Upload( media_source, self._get_doc_title(), folder_or_uri=folder_entry ) except gdata.service.RequestError as ex: try: if ex.message['reason'].lower().find('conflict') != -1: result = self.CONFLICT else: # Google docs has a bug when updating a shared document # using PUT from any account other that the owner. # It returns an error 400 "Sorry, there was an error saving the file. Please try again" # *despite* actually updating the document! # Workaround by re-reading to see if it actually updated if ex.message['body'].find( 'Sorry, there was an error saving the file') != -1: new_docs_entry, new_keyring_dict = self._read() if new_keyring_dict == keyring_dict: result = self.OK else: result = self.FAIL else: result = self.FAIL except: result = self.FAIL return result class KeyczarDocsKeyring(DocsKeyring): """Google Docs keyring using keyczar initialized from environment variables """ def __init__(self): crypter = keyczar.EnvironCrypter() credential = EnvironCredential() source = os.environ.get('GOOGLE_KEYRING_SOURCE') super(KeyczarDocsKeyring, self).__init__( credential, source, crypter) def supported(self): """Return if this keyring supports current environment: -1: not applicable 0: suitable 1: recommended """ try: from keyczar import keyczar return super(KeyczarDocsKeyring, self).supported() except ImportError: return -1 keyring-3.5/keyring/backends/pyfs.py0000666000000000000000000002222112273742000016304 0ustar rootrootimport os import base64 from ..py27compat import configparser import keyring.util.platform_ from keyring import errors from keyring.util.escape import escape as escape_for_ini from keyring.util import properties from keyring.backend import KeyringBackend, NullCrypter from . import keyczar try: import fs.opener import fs.osfs import fs.errors import fs.path import fs.remote except ImportError: pass def has_pyfs(): """ Does this environment have pyfs installed? Should return False even when Mercurial's Demand Import allowed import of fs.*. """ with errors.ExceptionRaisedContext() as exc: fs.__name__ return not bool(exc) class BasicKeyring(KeyringBackend): """BasicKeyring is a Pyfilesystem-based implementation of keyring. It stores the password directly in the file, and supports encryption and decryption. The encrypted password is stored in base64 format. Being based on Pyfilesystem the file can be local or network-based and served by any of the filesystems supported by Pyfilesystem including Amazon S3, FTP, WebDAV, memory and more. """ _filename = 'keyring_pyf_pass.cfg' def __init__(self, crypter, filename=None, can_create=True, cache_timeout=None): super(BasicKeyring, self).__init__() self._crypter = crypter self._filename = (filename or os.path.join(keyring.util.platform_.data_root(), self.__class__._filename)) self._can_create = can_create self._cache_timeout = cache_timeout @properties.NonDataProperty def file_path(self): """ The path to the file where passwords are stored. This property may be overridden by the subclass or at the instance level. """ return os.path.join(keyring.util.platform_.data_root(), self.filename) @property def filename(self): """The filename used to store the passwords. """ return self._filename def encrypt(self, password): """Encrypt the password. """ if not password or not self._crypter: return password or '' return self._crypter.encrypt(password) def decrypt(self, password_encrypted): """Decrypt the password. """ if not password_encrypted or not self._crypter: return password_encrypted or '' return self._crypter.decrypt(password_encrypted) def _open(self, mode='rb'): """Open the password file in the specified mode """ open_file = None writeable = 'w' in mode or 'a' in mode or '+' in mode try: # NOTE: currently the MemOpener does not split off any filename # which causes errors on close() # so we add a dummy name and open it separately if (self.filename.startswith('mem://') or self.filename.startswith('ram://')): open_file = fs.opener.fsopendir(self.filename).open('kr.cfg', mode) else: if not hasattr(self, '_pyfs'): # reuse the pyfilesystem and path self._pyfs, self._path = fs.opener.opener.parse(self.filename, writeable=writeable) # cache if permitted if self._cache_timeout is not None: self._pyfs = fs.remote.CacheFS( self._pyfs, cache_timeout=self._cache_timeout) open_file = self._pyfs.open(self._path, mode) except fs.errors.ResourceNotFoundError: if self._can_create: segments = fs.opener.opener.split_segments(self.filename) if segments: # this seems broken, but pyfilesystem uses it, so we must fs_name, credentials, url1, url2, path = segments.groups() assert fs_name, 'Should be a remote filesystem' host = '' # allow for domain:port if ':' in url2: split_url2 = url2.split('/', 1) if len(split_url2) > 1: url2 = split_url2[1] else: url2 = '' host = split_url2[0] pyfs = fs.opener.opener.opendir('%s://%s' %(fs_name, host)) # cache if permitted if self._cache_timeout is not None: pyfs = fs.remote.CacheFS( pyfs, cache_timeout=self._cache_timeout) # NOTE: fs.path.split does not function in the same way os os.path.split... at least under windows url2_path, url2_filename = os.path.split(url2) if url2_path and not pyfs.exists(url2_path): pyfs.makedir(url2_path, recursive=True) else: # assume local filesystem full_url = fs.opener._expand_syspath(self.filename) # NOTE: fs.path.split does not function in the same way os os.path.split... at least under windows url2_path, url2 = os.path.split(full_url) pyfs = fs.osfs.OSFS(url2_path) try: # reuse the pyfilesystem and path self._pyfs = pyfs self._path = url2 return pyfs.open(url2, mode) except fs.errors.ResourceNotFoundError: if writeable: raise else: pass # NOTE: ignore read errors as the underlying caller can fail safely if writeable: raise else: pass return open_file @property def config(self): """load the passwords from the config file """ if not hasattr(self, '_config'): raw_config = configparser.RawConfigParser() f = self._open() if f: raw_config.readfp(f) f.close() self._config = raw_config return self._config def get_password(self, service, username): """Read the password from the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # fetch the password try: password_base64 = self.config.get(service, username).encode() # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = self.decrypt(password_encrypted).decode('utf-8') except (configparser.NoOptionError, configparser.NoSectionError): password = None return password def set_password(self, service, username, password): """Write the password in the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # encrypt the password password = password or '' password_encrypted = self.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted).decode() # write the modification if not self.config.has_section(service): self.config.add_section(service) self.config.set(service, username, password_base64) config_file = self._open('w') self.config.write(config_file) config_file.close() def delete_password(self, service, username): service = escape_for_ini(service) username = escape_for_ini(username) try: self.config.remove_option(service, username) except configparser.NoSectionError: raise errors.PasswordDeleteError('Password not found') config_file = self._open('w') self.config.write(config_file) config_file.close() @properties.ClassProperty @classmethod def priority(cls): if not has_pyfs(): raise RuntimeError("pyfs required") return 2 class PlaintextKeyring(BasicKeyring): """Unencrypted Pyfilesystem Keyring """ def __init__(self, filename=None, can_create=True, cache_timeout=None): super(PlaintextKeyring, self).__init__( NullCrypter(), filename=filename, can_create=can_create, cache_timeout=cache_timeout) class EncryptedKeyring(BasicKeyring): """Encrypted Pyfilesystem Keyring """ _filename = 'crypted_pyf_pass.cfg' def __init__(self, crypter, filename=None, can_create=True, cache_timeout=None): super(EncryptedKeyring, self).__init__( crypter, filename=filename, can_create=can_create, cache_timeout=cache_timeout) class KeyczarKeyring(EncryptedKeyring): """Encrypted Pyfilesystem Keyring using Keyczar keysets specified in environment vars """ def __init__(self): super(KeyczarKeyring, self).__init__( keyczar.EnvironCrypter()) keyring-3.5/keyring/backends/kwallet.py0000666000000000000000000000716712277420364017014 0ustar rootrootimport os from keyring.py27compat import unicode_str from keyring.backend import KeyringBackend from keyring.errors import PasswordDeleteError from keyring.errors import PasswordSetError, ExceptionRaisedContext from keyring.util import properties from keyring.util import XDG try: from PyKDE4.kdeui import KWallet from PyQt4 import QtGui except ImportError: pass kwallet = None def open_kwallet(kwallet_module=None, qt_module=None): # If we specified the kwallet_module and/or qt_module, surely we won't need # the cached kwallet object... if kwallet_module is None and qt_module is None: global kwallet if not kwallet is None: return kwallet # Allow for the injection of module-like objects for testing purposes. if kwallet_module is None: kwallet_module = KWallet.Wallet if qt_module is None: qt_module = QtGui # KDE wants us to instantiate an application object. app = None if qt_module.qApp.instance() == None: app = qt_module.QApplication([]) try: window = qt_module.QWidget() kwallet = kwallet_module.openWallet( kwallet_module.NetworkWallet(), window.winId(), kwallet_module.Synchronous) if kwallet is not None: if not kwallet.hasFolder('Python'): kwallet.createFolder('Python') kwallet.setFolder('Python') return kwallet finally: if app: app.exit() class Keyring(KeyringBackend): """KDE KWallet""" @properties.ClassProperty @classmethod @XDG.Preference('KDE') def priority(cls): with ExceptionRaisedContext() as exc: KWallet.__name__ if exc: raise RuntimeError("KDE libraries not available") # Infer if KDE environment is active based on environment vars. # TODO: Does PyKDE provide a better indicator? kde_session_keys = ( 'KDE_SESSION_ID', # most environments 'KDE_FULL_SESSION', # openSUSE ) if not set(os.environ).intersection(kde_session_keys): return 0 return 5 def get_password(self, service, username): """Get password of the username for the service """ key = username + '@' + service network = KWallet.Wallet.NetworkWallet() wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. return None if wallet.keyDoesNotExist(network, 'Python', key): return None result = wallet.readPassword(key)[1] # The string will be a PyQt4.QtCore.QString, so turn it into a unicode # object. return unicode_str(result) def set_password(self, service, username, password): """Set password for the username of the service """ wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordSetError("Cancelled by user") wallet.writePassword(username+'@'+service, password) def delete_password(self, service, username): """Delete the password for the username of the service. """ key = username + '@' + service wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordDeleteError("Cancelled by user") if wallet.keyDoesNotExist(wallet.walletName(), 'Python', key): raise PasswordDeleteError("Password not found") wallet.removeEntry(key) keyring-3.5/keyring/backends/__init__.py0000666000000000000000000000000212242130154017050 0ustar rootroot# keyring-3.5/keyring/backends/Gnome.py0000666000000000000000000001111112277420152016371 0ustar rootrootimport os try: from gi import Repository if Repository.get_default().enumerate_versions('GnomeKeyring'): from gi.repository import GnomeKeyring except ImportError: pass from keyring.backend import KeyringBackend from keyring.errors import PasswordSetError, PasswordDeleteError from keyring.util import properties from keyring.util import XDG from keyring.py27compat import unicode_str class Keyring(KeyringBackend): """Gnome Keyring""" # Name of the keyring to store the passwords in. # Use None for the default keyring. KEYRING_NAME = None requisite_vars = [ 'GNOME_KEYRING_CONTROL', 'DISPLAY', 'DBUS_SESSION_BUS_ADDRESS', ] @properties.ClassProperty @classmethod @XDG.Preference('Gnome') def priority(cls): if 'GnomeKeyring' not in globals(): raise RuntimeError("GnomeKeyring module required") if not cls.has_requisite_vars(): raise RuntimeError("Requisite environment vars are not present") return int(cls.has_requisite_vars()) @classmethod def has_requisite_vars(cls): """ Return True if the requisite environment vars are present in the environment. """ return set(cls.requisite_vars).issubset(os.environ) def _find_passwords(self, service, username, deleting=False): """Get password of the username for the service """ passwords = [] service = self._safe_string(service) username = self._safe_string(username) for attrs_tuple in (('username', 'service'), ('user', 'domain')): attrs = GnomeKeyring.Attribute.list_new() GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[0], username) GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[1], service) result, items = GnomeKeyring.find_items_sync( GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) if result == GnomeKeyring.Result.OK: passwords += items elif deleting: if result == GnomeKeyring.Result.CANCELLED: raise PasswordDeleteError("Cancelled by user") elif result != GnomeKeyring.Result.NO_MATCH: raise PasswordDeleteError(result.value_name) return passwords def get_password(self, service, username): """Get password of the username for the service """ items = self._find_passwords(service, username) if not items: return None secret = items[0].secret return secret if isinstance(secret, unicode_str) else secret.decode('utf-8') def set_password(self, service, username, password): """Set password for the username of the service """ service = self._safe_string(service) username = self._safe_string(username) password = self._safe_string(password) attrs = GnomeKeyring.Attribute.list_new() GnomeKeyring.Attribute.list_append_string(attrs, 'username', username) GnomeKeyring.Attribute.list_append_string(attrs, 'service', service) GnomeKeyring.Attribute.list_append_string(attrs, 'application', 'python-keyring') result = GnomeKeyring.item_create_sync( self.KEYRING_NAME, GnomeKeyring.ItemType.NETWORK_PASSWORD, "Password for '%s' on '%s'" % (username, service), attrs, password, True)[0] if result == GnomeKeyring.Result.CANCELLED: # The user pressed "Cancel" when prompted to unlock their keyring. raise PasswordSetError("Cancelled by user") elif result != GnomeKeyring.Result.OK: raise PasswordSetError(result.value_name) def delete_password(self, service, username): """Delete the password for the username of the service. """ items = self._find_passwords(service, username, deleting=True) if not items: raise PasswordDeleteError("Password not found") for current in items: result = GnomeKeyring.item_delete_sync(current.keyring, current.item_id) if result == GnomeKeyring.Result.CANCELLED: raise PasswordDeleteError("Cancelled by user") elif result != GnomeKeyring.Result.OK: raise PasswordDeleteError(result.value_name) def _safe_string(self, source, encoding='utf-8'): """Convert unicode to string as gnomekeyring barfs on unicode""" if not isinstance(source, str): return source.encode(encoding) return str(source) keyring-3.5/keyring/backends/OS_X.py0000666000000000000000000000777212273742000016151 0ustar rootrootimport platform import subprocess import re import binascii from keyring.backend import KeyringBackend from keyring.errors import PasswordSetError from keyring.errors import PasswordDeleteError from keyring.util import properties from keyring.py27compat import unicode_str class SecurityCommand(unicode_str): """ A string suitable for passing as the 'command' parameter to the OS X 'security' command. """ def __new__(cls, cmd, store='generic'): cmd = '%(cmd)s-%(store)s-password' % vars() return super(SecurityCommand, cls).__new__(cls, cmd) class Keyring(KeyringBackend): """Mac OS X Keychain""" # regex for extracting password from security call password_regex = re.compile("""password:\s*(?:0x(?P[0-9A-F]+)\s*)?""" """(?:"(?P.*)")?""") store = 'generic' @properties.ClassProperty @classmethod def priority(cls): """ Preferred for all OS X environments. """ if platform.system() != 'Darwin': raise RuntimeError("OS X required") return 5 def set_password(self, service, username, password): if username is None: username = '' set_error = PasswordSetError("Can't store password in keychain") try: # set up the call for security. cmd = [ 'security', SecurityCommand('add', self.store), '-a', username, '-s', service, '-w', password, '-U', ] call = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdoutdata, stderrdata = call.communicate() code = call.returncode # check return code. if code is not 0: raise set_error except: raise set_error def get_password(self, service, username): if username is None: username = '' try: # set up the call to security. cmd = [ 'security', SecurityCommand('find', self.store), '-g', '-a', username, '-s', service, ] call = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdoutdata, stderrdata = call.communicate() code = call.returncode if code is not 0: raise OSError("Can't fetch password from system") output = stderrdata.decode() # check for empty password. if output == 'password: \n': return '' # search for special password pattern. matches = Keyring.password_regex.search(output) if matches: group_dict = matches.groupdict() hex = group_dict.get('hex') pw = group_dict.get('pw') if hex: # it's a weird hex password, decode it. return unicode_str(binascii.unhexlify(hex), 'utf-8') else: # it's a normal password, send it back. return pw # nothing was found, it doesn't exist. except: pass def delete_password(self, service, username): del_error = PasswordDeleteError("Can't delete password in keychain") if username is None: username = '' try: cmd = [ 'security', SecurityCommand('delete', self.store), '-a', username, '-s', service, ] # set up the call for security. call = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) stdoutdata, stderrdata = call.communicate() code = call.returncode # check return code. if code is not 0: raise del_error except: raise del_error keyring-3.5/keyring/backends/Windows.py0000666000000000000000000001716712273742000016772 0ustar rootrootimport sys import base64 import platform import keyring.util.escape from keyring.py27compat import unicode_str from keyring.util import properties from keyring.backend import KeyringBackend from keyring.errors import PasswordDeleteError, ExceptionRaisedContext from . import file try: import pywintypes import win32cred except ImportError: pass try: import winreg except ImportError: try: # Python 2 compatibility import _winreg as winreg except ImportError: pass try: from . import _win_crypto except ImportError: pass def has_pywin32(): """ Does this environment have pywin32? Should return False even when Mercurial's Demand Import allowed import of win32cred. """ with ExceptionRaisedContext() as exc: win32cred.__name__ return not bool(exc) def has_wincrypto(): """ Does this environment have wincrypto? Should return False even when Mercurial's Demand Import allowed import of _win_crypto, so accesses an attribute of the module. """ with ExceptionRaisedContext() as exc: _win_crypto.__name__ return not bool(exc) class EncryptedKeyring(file.BaseKeyring): """ A File-based keyring secured by Windows Crypto API. """ @properties.ClassProperty @classmethod def priority(self): """ Preferred over file.EncryptedKeyring but not other, more sophisticated Windows backends. """ if not platform.system() == 'Windows': raise RuntimeError("Requires Windows") return .8 filename = 'wincrypto_pass.cfg' def encrypt(self, password): """Encrypt the password using the CryptAPI. """ return _win_crypto.encrypt(password) def decrypt(self, password_encrypted): """Decrypt the password using the CryptAPI. """ return _win_crypto.decrypt(password_encrypted) class WinVaultKeyring(KeyringBackend): """ WinVaultKeyring stores encrypted passwords using the Windows Credential Manager. Requires pywin32 This backend does some gymnastics to simulate multi-user support, which WinVault doesn't support natively. See https://bitbucket.org/kang/python-keyring-lib/issue/47/winvaultkeyring-only-ever-returns-last#comment-731977 for details on the implementation, but here's the gist: Passwords are stored under the service name unless there is a collision (another password with the same service name but different user name), in which case the previous password is moved into a compound name: {username}@{service} """ @properties.ClassProperty @classmethod def priority(cls): """ If available, the preferred backend on Windows. """ if not has_pywin32(): raise RuntimeError("Requires Windows and pywin32") return 5 @staticmethod def _compound_name(username, service): return keyring.util.escape.u('%(username)s@%(service)s') % vars() def get_password(self, service, username): # first attempt to get the password under the service name res = self._get_password(service) if not res or res['UserName'] != username: # It wasn't found so attempt to get it with the compound name res = self._get_password(self._compound_name(username, service)) if not res: return None blob = res['CredentialBlob'] return blob.decode('utf-16') def _get_password(self, target): try: res = win32cred.CredRead( Type=win32cred.CRED_TYPE_GENERIC, TargetName=target, ) except pywintypes.error: e = sys.exc_info()[1] if e.winerror == 1168 and e.funcname == 'CredRead': # not found return None raise return res def set_password(self, service, username, password): existing_pw = self._get_password(service) if existing_pw: # resave the existing password using a compound target existing_username = existing_pw['UserName'] target = self._compound_name(existing_username, service) self._set_password(target, existing_username, existing_pw['CredentialBlob'].decode('utf-16')) self._set_password(service, username, unicode_str(password)) def _set_password(self, target, username, password): credential = dict(Type=win32cred.CRED_TYPE_GENERIC, TargetName=target, UserName=username, CredentialBlob=password, Comment="Stored using python-keyring", Persist=win32cred.CRED_PERSIST_ENTERPRISE) win32cred.CredWrite(credential, 0) def delete_password(self, service, username): compound = self._compound_name(username, service) deleted = False for target in service, compound: existing_pw = self._get_password(target) if existing_pw and existing_pw['UserName'] == username: deleted = True self._delete_password(target) if not deleted: raise PasswordDeleteError(service) def _delete_password(self, target): win32cred.CredDelete( Type=win32cred.CRED_TYPE_GENERIC, TargetName=target, ) class RegistryKeyring(KeyringBackend): """ RegistryKeyring is a keyring which use Windows CryptAPI to encrypt the user's passwords and store them under registry keys """ @properties.ClassProperty @classmethod def priority(self): """ Preferred on Windows when pywin32 isn't installed """ if platform.system() != 'Windows': raise RuntimeError("Requires Windows") if not has_wincrypto(): raise RuntimeError("Requires ctypes") return 2 def get_password(self, service, username): """Get password of the username for the service """ try: # fetch the password key = r'Software\%s\Keyring' % service hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key) password_saved = winreg.QueryValueEx(hkey, username)[0] password_base64 = password_saved.encode('ascii') # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = _win_crypto.decrypt(password_encrypted).decode('utf-8') except EnvironmentError: password = None return password def set_password(self, service, username, password): """Write the password to the registry """ # encrypt the password password_encrypted = _win_crypto.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted) # encode again to unicode password_saved = password_base64.decode('ascii') # store the password key_name = r'Software\%s\Keyring' % service hkey = winreg.CreateKey(winreg.HKEY_CURRENT_USER, key_name) winreg.SetValueEx(hkey, username, 0, winreg.REG_SZ, password_saved) def delete_password(self, service, username): """Delete the password for the username of the service. """ try: key_name = r'Software\%s\Keyring' % service hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0, winreg.KEY_ALL_ACCESS) winreg.DeleteValue(hkey, username) except WindowsError: e = sys.exc_info()[1] raise PasswordDeleteError(e) keyring-3.5/keyring/getpassbackend.py0000666000000000000000000000050112273742000016524 0ustar rootroot"""Specific support for getpass.""" import getpass import keyring.core def get_password(prompt='Password: ', stream=None, service_name='Python', username=None): if username is None: username = getpass.getuser() return keyring.core.get_password(service_name, username) keyring-3.5/keyring/tests/0000775000000000000000000000000012300426570014342 5ustar rootrootkeyring-3.5/keyring/tests/py30compat.py0000666000000000000000000000034312246200106016707 0ustar rootroot""" Compatibility support for Python 3.0. Remove when Python 3.0 support is no longer required. """ import sys if sys.version_info < (2,7) or sys.version_info[:2] == (3,0): import unittest2 as unittest else: import unittest keyring-3.5/keyring/tests/backends/0000775000000000000000000000000012300426570016114 5ustar rootrootkeyring-3.5/keyring/tests/backends/test_kwallet.py0000666000000000000000000000420112273742000021165 0ustar rootrootfrom ..py30compat import unittest from keyring.backends import kwallet from ..test_backend import BackendBasicTests def is_qt4_supported(): try: __import__('PyQt4.QtGui') except ImportError: return False return True @unittest.skipUnless(kwallet.Keyring.viable, "Need KWallet") class KDEKWalletTestCase(BackendBasicTests, unittest.TestCase): def init_keyring(self): return kwallet.Keyring() class UnOpenableKWallet(object): """A module-like object used to test KDE wallet fall-back.""" Synchronous = None def openWallet(self, *args): return None def NetworkWallet(self): return None class FauxQtGui(object): """A fake module-like object used in testing the open_kwallet function.""" class qApp: @staticmethod def instance(): pass class QApplication(object): def __init__(self, *args): pass def exit(self): pass class QWidget(object): def __init__(self, *args): pass def winId(self): pass class KDEWalletCanceledTestCase(unittest.TestCase): def test_user_canceled(self): # If the user cancels either the "enter your password to unlock the # keyring" dialog or clicks "deny" on the "can this application access # the wallet" dialog then openWallet() will return None. The # open_wallet() function should handle that eventuality by returning # None to signify that the KWallet backend is not available. self.assertEqual( kwallet.open_kwallet(UnOpenableKWallet(), FauxQtGui()), None) @unittest.skipUnless(kwallet.Keyring.viable and is_qt4_supported(), "Need KWallet and Qt4") class KDEKWalletInQApplication(unittest.TestCase): def test_QApplication(self): try: from PyKDE4.kdeui import KWallet from PyQt4.QtGui import QApplication except: return app = QApplication([]) wallet = kwallet.open_kwallet() self.assertIsInstance(wallet, KWallet.Wallet) app.exit() keyring-3.5/keyring/tests/backends/test_pyfs.py0000666000000000000000000001061312273742000020507 0ustar rootrootimport os import tempfile from ..py30compat import unittest import keyring.backend from keyring.backends import pyfs from ..test_backend import BackendBasicTests, random_string class ReverseCrypter(keyring.backend.Crypter): """Very silly crypter class""" def encrypt(self, value): return value[::-1] def decrypt(self, value): return value[::-1] class PyfilesystemKeyringTests(BackendBasicTests): """Base class for Pyfilesystem tests""" def setUp(self): super(PyfilesystemKeyringTests, self).setUp() self.keyring = self.init_keyring() def tearDown(self): del self.keyring def test_encrypt_decrypt(self): password = random_string(20) encrypted = self.keyring.encrypt(password) self.assertEqual(password, self.keyring.decrypt(encrypted)) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedMemoryPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with no encryption""" keyring_filename = 'mem://unencrypted' def init_keyring(self): return keyring.backends.pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedMemoryPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with no encryption""" keyring_filename = 'mem://some/sub/dir/unencrypted' def init_keyring(self): return keyring.backends.pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedLocalPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with no encryption""" keyring_filename = '%s/keyring.cfg' %tempfile.mkdtemp() def init_keyring(self): return keyring.backends.pyfs.PlaintextKeyring( filename=self.keyring_filename) def test_handles_preexisting_keyring(self): from fs.opener import opener fs, path = opener.parse(self.keyring_filename, writeable=True) keyring_file = fs.open(path, 'wb') keyring_file.write( """[svc1] user1 = cHdkMQ== """) keyring_file.close() pyf_keyring = keyring.backends.pyfs.PlaintextKeyring( filename=self.keyring_filename) self.assertEquals('pwd1', pyf_keyring.get_password('svc1', 'user1')) def tearDown(self): del self.keyring if os.path.exists(self.keyring_filename): os.remove(self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedLocalPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with no encryption""" keyring_dir = os.path.join(tempfile.mkdtemp(), 'more', 'sub', 'dirs') keyring_filename = os.path.join(keyring_dir, 'keyring.cfg') def init_keyring(self): if not os.path.exists(self.keyring_dir): os.makedirs(self.keyring_dir) return keyring.backends.pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedMemoryPyfilesystemKeyringTestCase(PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with encryption""" def init_keyring(self): return keyring.backends.pyfs.EncryptedKeyring( ReverseCrypter(), filename='mem://encrypted/keyring.cfg') @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedLocalPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with encryption""" def init_keyring(self): return keyring.backends.pyfs.EncryptedKeyring( ReverseCrypter(), filename='temp://keyring.cfg') @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedLocalPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with encryption""" def init_keyring(self): return keyring.backends.pyfs.EncryptedKeyring( ReverseCrypter(), filename='temp://a/sub/dir/hierarchy/keyring.cfg') keyring-3.5/keyring/tests/backends/test_keyczar.py0000666000000000000000000000627212273742000021204 0ustar rootrootimport os from ..py30compat import unittest from keyring.backends import keyczar from .. import mocks def is_keyczar_supported(): return hasattr(keyczar, 'keyczar') @unittest.skipUnless(is_keyczar_supported(), "Need Keyczar") class KeyczarCrypterTestCase(unittest.TestCase): """Test the keyczar crypter""" def setUp(self): self._orig_keyczar = keyczar.keyczar keyczar.keyczar = mocks.MockKeyczar() def tearDown(self): keyczar.keyczar = self._orig_keyczar if keyczar.EnvironCrypter.KEYSET_ENV_VAR in os.environ: del os.environ[keyczar.EnvironCrypter.KEYSET_ENV_VAR] if keyczar.EnvironCrypter.ENC_KEYSET_ENV_VAR in os.environ: del os.environ[keyczar.EnvironCrypter.ENC_KEYSET_ENV_VAR] def testKeyczarCrypterWithUnencryptedReader(self): """ """ location = 'bar://baz' kz_crypter = keyczar.Crypter(location) self.assertEquals(location, kz_crypter.keyset_location) self.assertIsNone(kz_crypter.encrypting_keyset_location) self.assertIsInstance(kz_crypter.crypter, mocks.MockKeyczarCrypter) self.assertIsInstance(kz_crypter.crypter.reader, mocks.MockKeyczarReader) self.assertEquals(location, kz_crypter.crypter.reader.location) def testKeyczarCrypterWithEncryptedReader(self): """ """ location = 'foo://baz' encrypting_location = 'castle://aaargh' kz_crypter = keyczar.Crypter(location, encrypting_location) self.assertEquals(location, kz_crypter.keyset_location) self.assertEquals(encrypting_location, kz_crypter.encrypting_keyset_location) self.assertIsInstance(kz_crypter.crypter, mocks.MockKeyczarCrypter) self.assertIsInstance(kz_crypter.crypter.reader, mocks.MockKeyczarEncryptedReader) self.assertEquals(location, kz_crypter.crypter.reader._reader.location) self.assertEquals(encrypting_location, kz_crypter.crypter.reader._crypter.reader.location) def testKeyczarCrypterEncryptDecryptHandlesEmptyNone(self): location = 'castle://aargh' kz_crypter = keyczar.Crypter(location) self.assertEquals('', kz_crypter.encrypt('')) self.assertEquals('', kz_crypter.encrypt(None)) self.assertEquals('', kz_crypter.decrypt('')) self.assertEquals('', kz_crypter.decrypt(None)) def testEnvironCrypterReadsCorrectValues(self): location = 'foo://baz' encrypting_location = 'castle://aaargh' kz_crypter = keyczar.EnvironCrypter() os.environ[kz_crypter.KEYSET_ENV_VAR] = location self.assertEqual(location, kz_crypter.keyset_location) self.assertIsNone(kz_crypter.encrypting_keyset_location) os.environ[kz_crypter.ENC_KEYSET_ENV_VAR] = encrypting_location self.assertEqual(encrypting_location, kz_crypter.encrypting_keyset_location) def testEnvironCrypterThrowsExceptionOnMissingValues(self): kz_crypter = keyczar.EnvironCrypter() with self.assertRaises(ValueError): kz_crypter.keyset_location self.assertIsNone(kz_crypter.encrypting_keyset_location) keyring-3.5/keyring/tests/backends/test_file.py0000666000000000000000000000312212276520014020444 0ustar rootrootimport os import tempfile import sys import errno from ..py30compat import unittest from ..test_backend import BackendBasicTests from ..util import random_string from keyring.backends import file class FileKeyringTests(BackendBasicTests): def setUp(self): super(FileKeyringTests, self).setUp() self.keyring = self.init_keyring() self.keyring.file_path = self.tmp_keyring_file = tempfile.mktemp() def tearDown(self): try: os.unlink(self.tmp_keyring_file) except (OSError,): e = sys.exc_info()[1] if e.errno != errno.ENOENT: # No such file or directory raise def test_encrypt_decrypt(self): password = random_string(20) # keyring.encrypt expects bytes password = password.encode('utf-8') encrypted = self.keyring.encrypt(password) self.assertEqual(password, self.keyring.decrypt(encrypted)) class UncryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase): def init_keyring(self): return file.PlaintextKeyring() @unittest.skipIf(sys.platform == 'win32', "Group/World permissions aren't meaningful on Windows") def test_keyring_not_created_world_writable(self): """ Ensure that when keyring creates the file that it's not overly- permissive. """ self.keyring.set_password('system', 'user', 'password') self.assertTrue(os.path.exists(self.keyring.file_path)) group_other_perms = os.stat(self.keyring.file_path).st_mode & 0o077 self.assertEqual(group_other_perms, 0) keyring-3.5/keyring/tests/backends/test_OS_X.py0000666000000000000000000000147312273742000020342 0ustar rootrootimport sys from ..test_backend import BackendBasicTests from ..py30compat import unittest from keyring.backends import OS_X def is_osx_keychain_supported(): return sys.platform in ('mac','darwin') @unittest.skipUnless(is_osx_keychain_supported(), "Need OS X") class OSXKeychainTestCase(BackendBasicTests, unittest.TestCase): def init_keyring(self): return OS_X.Keyring() @unittest.expectedFailure def test_delete_present(self): """Not implemented""" super(OSXKeychainTestCase, self).test_delete_present() class SecurityCommandTestCase(unittest.TestCase): def test_SecurityCommand(self): self.assertEqual(OS_X.SecurityCommand('get'), 'get-generic-password') self.assertEqual(OS_X.SecurityCommand('set', 'internet'), 'set-internet-password') keyring-3.5/keyring/tests/backends/test_Google.py0000666000000000000000000003617712273742000020757 0ustar rootrootimport codecs import base64 from ..py30compat import unittest from ..test_backend import BackendBasicTests from keyring.backends import Google from keyring.credentials import SimpleCredential from keyring.backend import NullCrypter from keyring import errors from keyring.py27compat import input, pickle from .. import mocks def is_gdata_supported(): try: __import__('gdata.service') except ImportError: return False return True def init_google_docs_keyring(client, can_create=True, input_getter=input): credentials = SimpleCredential('foo', 'bar') return Google.DocsKeyring(credentials, 'test_src', NullCrypter(), client=client, can_create=can_create, input_getter=input_getter ) @unittest.skipUnless(is_gdata_supported(), "Need Google Docs (gdata)") class GoogleDocsKeyringTestCase(BackendBasicTests, unittest.TestCase): """Run all the standard tests on a new keyring""" def init_keyring(self): client = mocks.MockDocumentService() client.SetClientLoginToken('foo') return init_google_docs_keyring(client) @unittest.skipUnless(is_gdata_supported(), "Need Google Docs (gdata)") class GoogleDocsKeyringInteractionTestCase(unittest.TestCase): """Additional tests for Google Doc interactions""" def _init_client(self, set_token=True): client = mocks.MockDocumentService() if set_token: client.SetClientLoginToken('interaction') return client def _init_keyring(self, client): self.keyring = init_google_docs_keyring(client) def _init_listfeed(self): listfeed = mocks.MockListFeed() listfeed._entry = [mocks.MockDocumentListEntry(), mocks.MockDocumentListEntry() ] return listfeed def _encode_data(self, data): return base64.urlsafe_b64encode(pickle.dumps(data)) def test_handles_auth_failure(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.BadAuthentication self._init_keyring(client) with self.assertRaises(errors.InitError): self.keyring.client def test_handles_auth_error(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.Error self._init_keyring(client) with self.assertRaises(errors.InitError): self.keyring.client def test_handles_login_captcha(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.CaptchaRequired client.captcha_url = 'a_captcha_url' client.captcha_token = 'token' self.get_input_called = False def _get_input(prompt): self.get_input_called = True delattr(client, '_login_err') return 'Foo' self.keyring = init_google_docs_keyring(client, input_getter=_get_input) self.keyring.client self.assertTrue(self.get_input_called, 'Should have got input') def test_retrieves_existing_keyring_with_and_without_bom(self): client = self._init_client() dummy_entries = dict(section1=dict(user1='pwd1')) no_utf8_bom_entries = self._encode_data(dummy_entries) client._request_response = dict(status=200, data=no_utf8_bom_entries) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual(self.keyring.get_password('section1', 'user1'), 'pwd1') utf8_bom_entries = codecs.BOM_UTF8 + no_utf8_bom_entries client._request_response = dict(status=200, data=utf8_bom_entries) self._init_keyring(client) self.assertEqual(self.keyring.get_password('section1', 'user1'), 'pwd1') def test_handles_retrieve_failure(self): client = self._init_client() client._listfeed = self._init_listfeed() client._request_response = dict(status=400, reason='Data centre explosion') self._init_keyring(client) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_handles_corrupt_retrieve(self): client = self._init_client() dummy_entries = dict(section1=dict(user1='pwd1')) client._request_response = dict(status=200, data='broken' + self._encode_data(dummy_entries)) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_no_create_if_requested(self): client = self._init_client() self.keyring = init_google_docs_keyring(client, can_create=False) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_no_set_if_create_folder_fails_on_new_keyring(self): import gdata client = self._init_client() client._create_folder_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'service-a', 'user-A', 'password-A') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set after write fail') def test_no_set_if_write_fails_on_new_keyring(self): import gdata client = self._init_client() client._upload_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'service-a', 'user-A', 'password-A') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set after write fail') def test_no_set_if_write_fails_on_existing_keyring(self): import gdata client = self._init_client() dummy_entries = dict(sectionB=dict(user9='pwd9')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = gdata.service.RequestError client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual(self.keyring.get_password('sectionB', 'user9'), 'pwd9', 'Correct password should be set in existing keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionB', 'user9', 'Not the same pwd') self.assertEqual(self.keyring.get_password('sectionB', 'user9'), 'pwd9', 'Password should be unchanged after write fail') def test_writes_correct_data_to_google_docs(self): client = self._init_client() dummy_entries = dict(sectionWriteChk=dict(userWriteChk='pwd')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._listfeed = self._init_listfeed() self._init_keyring(client) self.keyring.set_password('sectionWriteChk', 'userWritechk', 'new_pwd') self.assertIsNotNone(client._put_data, 'Should have written data') self.assertEquals( 'new_pwd', client._put_data.get('sectionWriteChk').get('userWritechk'), 'Did not write updated password!') def test_handles_write_conflict_on_different_service(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictA=dict( userwriteConflictA='pwdwriteConflictA')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [(gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictA', 'userwriteConflictA'), 'pwdwriteConflictA', 'Correct password should be set in existing keyring') dummy_entries['diffSection'] = dict(foo='bar') client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) new_pwd = 'Not the same pwd' self.keyring.set_password('sectionWriteConflictA', 'userwriteConflictA', new_pwd) self.assertEquals(self.keyring.get_password('sectionWriteConflictA', 'userwriteConflictA'), new_pwd ) self.assertEqual(1, client._put_count, 'Write not called after conflict resolution') def test_handles_write_conflict_on_same_service_and_username(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictB=dict( userwriteConflictB='pwdwriteConflictB')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = (gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictB', 'userwriteConflictB'), 'pwdwriteConflictB', 'Correct password should be set in existing keyring') conflicting_dummy_entries = dict(sectionWriteConflictB=dict( userwriteConflictB='pwdwriteConflictC')) client._request_response = dict(status=200, data=self._encode_data(conflicting_dummy_entries)) self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionWriteConflictB', 'userwriteConflictB', 'new_pwd') def test_handles_write_conflict_with_identical_change(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictC=dict( userwriteConflictC='pwdwriteConflictC')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [(gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictC', 'userwriteConflictC'), 'pwdwriteConflictC', 'Correct password should be set in existing keyring') new_pwd = 'Not the same pwd' conflicting_dummy_entries = dict(sectionWriteConflictC=dict( userwriteConflictC=new_pwd)) client._request_response = dict(status=200, data=self._encode_data(conflicting_dummy_entries)) self.keyring.set_password('sectionWriteConflictC', 'userwriteConflictC', new_pwd) self.assertEquals(self.keyring.get_password('sectionWriteConflictC', 'userwriteConflictC'), new_pwd ) def test_handles_broken_google_put_when_non_owner_update_fails(self): """Google Docs has a bug when putting to a non-owner see GoogleDocsKeyring._save_keyring() """ import gdata client = self._init_client() dummy_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [( gdata.service.RequestError, { 'status': '400', 'body': 'Sorry, there was an error saving the file. Please try again.', 'reason': 'Bad Request'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) new_pwd = 'newPwdBrokenPut' correct_read_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(correct_read_entries)) self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionBrokenPut', 'userBrokenPut', new_pwd) def test_handles_broken_google_put_when_non_owner_update(self): """Google Docs has a bug when putting to a non-owner see GoogleDocsKeyring._save_keyring() """ import gdata client = self._init_client() dummy_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [( gdata.service.RequestError, { 'status': '400', 'body': 'Sorry, there was an error saving the file. Please try again.', 'reason': 'Bad Request'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) new_pwd = 'newPwdBrokenPut' correct_read_entries = dict(sectionBrokenPut=dict( userBrokenPut=new_pwd)) client._request_response = dict(status=200, data=self._encode_data(correct_read_entries)) self.keyring.set_password('sectionBrokenPut', 'userBrokenPut', new_pwd) self.assertEquals(self.keyring.get_password('sectionBrokenPut', 'userBrokenPut'), new_pwd) def test_uses_existing_folder(self): import gdata client = self._init_client() # should not happen client._create_folder_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') client._listfeed = self._init_listfeed() self.keyring.set_password('service-a', 'user-A', 'password-A') self.assertIsNotNone(client._upload_data, 'Should have written data') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), 'password-A', 'Correct password should be set') keyring-3.5/keyring/tests/backends/__init__.py0000666000000000000000000000000012246200106020207 0ustar rootrootkeyring-3.5/keyring/tests/backends/test_SecretService.py0000666000000000000000000000156212273742000022277 0ustar rootrootfrom ..py30compat import unittest from ..test_backend import BackendBasicTests from keyring.backends import SecretService from .. import util @unittest.skipUnless(SecretService.Keyring.viable, "SecretStorage package is needed for SecretServiceKeyring") class SecretServiceKeyringTestCase(BackendBasicTests, unittest.TestCase): __test__ = True def init_keyring(self): print("Testing SecretServiceKeyring; the following " "password prompts are for this keyring") return SecretService.Keyring() class SecretServiceKeyringUnitTests(unittest.TestCase): def test_supported_no_secretstorage(self): """ SecretService Keyring is not viable if secretstorage can't be imported. """ with util.NoNoneDictMutator(SecretService.__dict__, secretstorage=None): self.assertFalse(SecretService.Keyring.viable) keyring-3.5/keyring/tests/backends/test_Gnome.py0000666000000000000000000000365512273742000020603 0ustar rootrootimport types import sys from ..py30compat import unittest from ..test_backend import BackendBasicTests from ..util import Environ, NoNoneDictMutator from keyring.backends import Gnome def ImportBlesser(*names, **changes): """A context manager to temporarily make it possible to import a module""" for name in names: changes[name] = types.ModuleType(name) return NoNoneDictMutator(sys.modules, **changes) @unittest.skipUnless(Gnome.Keyring.viable, "Need GnomeKeyring") class GnomeKeyringTestCase(BackendBasicTests, unittest.TestCase): def environ(self): return dict(GNOME_KEYRING_CONTROL='1', DISPLAY='1', DBUS_SESSION_BUS_ADDRESS='1') def init_keyring(self): k = Gnome.Keyring() # Store passwords in the session (in-memory) keyring for the tests. This # is going to be automatically cleared when the user logoff. k.KEYRING_NAME = 'session' return k def test_supported(self): with ImportBlesser('gi.repository'): with Environ(**self.environ()): self.assertTrue(Gnome.Keyring.viable) def test_supported_no_module(self): with NoNoneDictMutator(Gnome.__dict__, GnomeKeyring=None): with Environ(**self.environ()): self.assertFalse(Gnome.Keyring.viable) def test_supported_no_keyring(self): environ = self.environ() environ['GNOME_KEYRING_CONTROL'] = None with Environ(**environ): self.assertFalse(Gnome.Keyring.viable) def test_supported_no_display(self): environ = self.environ() environ['DISPLAY'] = None with Environ(**environ): self.assertFalse(Gnome.Keyring.viable) def test_supported_no_session(self): environ = self.environ() environ['DBUS_SESSION_BUS_ADDRESS'] = None with Environ(**environ): self.assertFalse(Gnome.Keyring.viable) keyring-3.5/keyring/tests/backends/test_crypto.py0000666000000000000000000000150512273742000021046 0ustar rootrootimport mock from ..py30compat import unittest from .test_file import FileKeyringTests from keyring.backends import file def is_crypto_supported(): try: __import__('Crypto.Cipher.AES') __import__('Crypto.Protocol.KDF') __import__('Crypto.Random') except ImportError: return False return True @unittest.skipUnless(is_crypto_supported(), "Need Crypto module") class CryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase): def setUp(self): super(self.__class__, self).setUp() fake_getpass = mock.Mock(return_value='abcdef') self.patcher = mock.patch('getpass.getpass', fake_getpass) self.patcher.start() def tearDown(self): self.patcher.stop() def init_keyring(self): return file.EncryptedKeyring() keyring-3.5/keyring/tests/backends/test_Windows.py0000666000000000000000000000367412273742000021171 0ustar rootrootfrom __future__ import print_function import sys from ..py30compat import unittest import keyring.backends.Windows from ..test_backend import BackendBasicTests from .test_file import FileKeyringTests def is_win32_crypto_supported(): try: __import__('keyring.backends._win_crypto') except ImportError: return False return sys.platform in ['win32'] and sys.getwindowsversion()[-2] == 2 def is_winvault_supported(): try: __import__('win32cred') has_pywin32 = True except ImportError: has_pywin32 = False return ( sys.platform in ['win32'] and sys.getwindowsversion().major >= 6 and has_pywin32 ) @unittest.skipUnless(is_win32_crypto_supported(), "Need Windows") class Win32CryptoKeyringTestCase(FileKeyringTests, unittest.TestCase): def init_keyring(self): return keyring.backends.Windows.EncryptedKeyring() @unittest.skipUnless(is_winvault_supported(), "Need WinVault") class WinVaultKeyringTestCase(BackendBasicTests, unittest.TestCase): def tearDown(self): # clean up any credentials created for cred in self.credentials_created: try: self.keyring.delete_password(*cred) except Exception as e: print(e, file=sys.stderr) def init_keyring(self): return keyring.backends.Windows.WinVaultKeyring() @unittest.skipUnless(keyring.backends.Windows.RegistryKeyring.viable and sys.version_info > (3,), "RegistryKeyring not viable") class RegistryKeyringTestCase(BackendBasicTests, unittest.TestCase): def tearDown(self): # clean up any credentials created for cred in self.credentials_created: try: self.keyring.delete_password(*cred) except Exception as e: print(e, file=sys.stderr) def init_keyring(self): return keyring.backends.Windows.RegistryKeyring() keyring-3.5/keyring/tests/backends/test_multi.py0000666000000000000000000000411012246200106020647 0ustar rootrootfrom ..py30compat import unittest from keyring.backend import KeyringBackend from keyring.backends import multi import keyring.errors class MultipartKeyringWrapperTestCase(unittest.TestCase): """Test the wrapper that breaks passwords into smaller chunks""" class MockKeyring(KeyringBackend): priority = 1 def __init__(self): self.passwords = {} def get_password(self, service, username): return self.passwords.get(service+username) def set_password(self, service, username, password): self.passwords[service+username] = password def delete_password(self, service, username): try: del self.passwords[service+username] except KeyError: raise keyring.errors.PasswordDeleteError('not found') def testViablePassThru(self): kr = multi.MultipartKeyringWrapper(self.MockKeyring()) self.assertTrue(kr.viable) def testMissingPassword(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr) self.assertIsNone(kr.get_password('s1', 'u1')) def testSmallPasswordSetInSinglePart(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr) kr.set_password('s1', 'u1', 'p1') self.assertEquals(wrapped_kr.passwords, {'s1u1':'p1'}) # should be able to read it back self.assertEquals(kr.get_password('s1', 'u1'), 'p1') def testLargePasswordSetInMultipleParts(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr, max_password_size=2) kr.set_password('s2', 'u2', '0123456') self.assertEquals(wrapped_kr.passwords, {'s2u2':'01', 's2u2{{part_1}}':'23', 's2u2{{part_2}}':'45', "s2u2{{part_3}}":'6'}) # should be able to read it back self.assertEquals(kr.get_password('s2', 'u2'), '0123456') keyring-3.5/keyring/tests/test_backend.py0000666000000000000000000001165712273742000017354 0ustar rootroot# -*- coding: utf-8 -*- """ test_backend.py Test case for keyring basic function created by Kang Zhang 2009-07-14 """ from __future__ import with_statement import string from keyring.util import escape from .util import random_string from keyring import errors DIFFICULT_CHARS = string.whitespace + string.punctuation # unicode only characters # Sourced from The Quick Brown Fox... Pangrams # http://www.columbia.edu/~fdc/utf8/ UNICODE_CHARS = escape.u( """זהכיףסתםלשמועאיךתנצחקרפדעץטובבגן""" """ξεσκεπάζωτηνψυχοφθόραβδελυγμία""" """Съешьжеещёэтихмягкихфранцузскихбулокдавыпейчаю""" """Жълтатадюлябешещастливачепухъткойтоцъфназамръзнакатогьон""" ) # ensure no-ascii chars slip by - watch your editor! assert min(ord(char) for char in UNICODE_CHARS) > 127 class BackendBasicTests(object): """Test for the keyring's basic functions. password_set and password_get """ def setUp(self): self.keyring = self.init_keyring() self.credentials_created = set() def tearDown(self): for item in self.credentials_created: self.keyring.delete_password(*item) def set_password(self, service, username, password): # set the password and save the result so the test runner can clean # up after if necessary. self.keyring.set_password(service, username, password) self.credentials_created.add((service, username)) def check_set_get(self, service, username, password): keyring = self.keyring # for the non-existent password self.assertEqual(keyring.get_password(service, username), None) # common usage self.set_password(service, username, password) self.assertEqual(keyring.get_password(service, username), password) # for the empty password self.set_password(service, username, "") self.assertEqual(keyring.get_password(service, username), "") def test_password_set_get(self): password = random_string(20) username = random_string(20) service = random_string(20) self.check_set_get(service, username, password) def test_difficult_chars(self): password = random_string(20, DIFFICULT_CHARS) username = random_string(20, DIFFICULT_CHARS) service = random_string(20, DIFFICULT_CHARS) self.check_set_get(service, username, password) def test_delete_present(self): password = random_string(20, DIFFICULT_CHARS) username = random_string(20, DIFFICULT_CHARS) service = random_string(20, DIFFICULT_CHARS) self.keyring.set_password(service, username, password) self.keyring.delete_password(service, username) self.assertIsNone(self.keyring.get_password(service, username)) def test_delete_not_present(self): username = random_string(20, DIFFICULT_CHARS) service = random_string(20, DIFFICULT_CHARS) self.assertRaises(errors.PasswordDeleteError, self.keyring.delete_password, service, username) def test_delete_one_in_group(self): username1 = random_string(20, DIFFICULT_CHARS) username2 = random_string(20, DIFFICULT_CHARS) password = random_string(20, DIFFICULT_CHARS) service = random_string(20, DIFFICULT_CHARS) self.keyring.set_password(service, username1, password) self.set_password(service, username2, password) self.keyring.delete_password(service, username1) self.assertEqual(self.keyring.get_password( service, username2), password) def test_unicode_chars(self): password = random_string(20, UNICODE_CHARS) username = random_string(20, UNICODE_CHARS) service = random_string(20, UNICODE_CHARS) self.check_set_get(service, username, password) def test_unicode_and_ascii_chars(self): source = (random_string(10, UNICODE_CHARS) + random_string(10) + random_string(10, DIFFICULT_CHARS)) password = random_string(20, source) username = random_string(20, source) service = random_string(20, source) self.check_set_get(service, username, password) def test_different_user(self): """ Issue #47 reports that WinVault isn't storing passwords for multiple users. This test exercises that test for each of the backends. """ keyring = self.keyring self.set_password('service1', 'user1', 'password1') self.set_password('service1', 'user2', 'password2') self.assertEqual(keyring.get_password('service1', 'user1'), 'password1') self.assertEqual(keyring.get_password('service1', 'user2'), 'password2') self.set_password('service2', 'user3', 'password3') self.assertEqual(keyring.get_password('service1', 'user1'), 'password1') keyring-3.5/keyring/tests/mocks.py0000666000000000000000000001472112273742000016035 0ustar rootroot""" mocks.py Various mock objects for testing """ import base64 from keyring.py27compat import pickle, unicode_str try: from StringIO import StringIO except ImportError: # For Python 3 from io import StringIO class MockAtom(object): """ Mocks an atom in the GData service. """ def __init__(self, value): self.text = value class MockEntry(object): """ Mocks and entry returned from the GData service. """ def __init__(self, title, ID): self.title = MockAtom(title) self.id = MockAtom('http://mock.example.com/%s' % ID) self.ID = ID # simpler lookup for key value def GetEditMediaLink(self): return MockLink() class MockHTTPClient(object): """ Mocks the functionality of an http client. """ def request(*args, **kwargs): pass class MockGDataService(object): """ Provides the common functionality of a Google Service. """ http_client = MockHTTPClient() def __init__(self, email=None, password=None, account_type='HOSTED_OR_GOOGLE', service=None, auth_service_url=None, source=None, server=None, additional_headers=None, handler=None, tokens=None, http_client=None, token_store=None): """ Create the Service with the default parameters. """ self.email = email self.password = password self.account_type = account_type self.service = service self.auth_service_url = auth_service_url self.server = server self.login_token = None def GetClientLoginToken(self): return self.login_token def SetClientLoginToken(self, token): self.login_token = token def ClientLogin(self, username, password, account_type=None, service=None, auth_service_url=None, source=None, captcha_token=None, captcha_response=None): """ Client side login to the service. """ if hasattr(self, '_login_err'): raise self._login_err() class MockDocumentService(MockGDataService): """ Implements the minimum functionality of the Google Document service. """ def Upload(self, media_source, title, folder_or_uri=None, label=None): """ Upload a document. """ if hasattr(self, '_upload_err'): raise self._upload_err() if not hasattr(self, '_upload_count'): self._upload_count = 0 # save the data for asserting against self._upload_data = dict(media_source=media_source, title=title, folder_or_uri=folder_or_uri, label=label) self._upload_count += 1 return MockEntry(title, 'mockentry%3A' + title) def QueryDocumentListFeed(self, uri): if hasattr(self, '_listfeed'): return self._listfeed return MockListFeed() def CreateFolder(self, title, folder_or_uri=None): if hasattr(self, '_create_folder_err'): raise self._create_folder_err() if hasattr(self, '_create_folder'): return self._create_folder return MockListEntry() def Put(self, data, uri, extra_headers=None, url_params=None, escape_params=True, redirects_remaining=3, media_source=None, converter=None): self._put_data = None if not hasattr(self, '_put_count'): self._put_count = 0 if hasattr(self, '_put_err'): # allow for a list of errors if type(self._put_err) == list: put_err = self._put_err.pop(0) if not len(self._put_err): delattr(self, '_put_err') else: put_err = self._put_err if type(put_err) == tuple: raise put_err[0](put_err[1]) else: raise put_err() # save the data for asserting against assert isinstance(data, str) or isinstance(data, unicode_str), \ 'Should be a string' self._put_data = pickle.loads(base64.urlsafe_b64decode(data)) self._put_count += 1 return MockEntry('', 'mockentry%3A' + '') def Export(self, entry_or_id_or_url, file_path, gid=None, extra_params=None): if hasattr(self, '_export_err'): raise self._export_err() if hasattr(self, '_export_data'): export_file = open(file_path, 'wb') export_file.write(self._export_data) export_file.close() def request(self, data, uri): if hasattr(self, '_request_err'): if type(self._request_err) == tuple: raise self._request_err[0](self._request_err[1]) else: raise self._request_err() if hasattr(self, '_request_response'): return MockHttpResponse(self._request_response) class MockHttpResponse(StringIO, object): def __init__(self, response_dict): super(MockHttpResponse, self).__init__(response_dict.get('data', '')) self.status = response_dict.get('status', 200) self.reason = response_dict.get('reason', '') class MockListFeed(object): @property def entry(self): if hasattr(self, '_entry'): return self._entry return [] class MockListEntry(object): pass class MockLink(object): @property def href(self): return '' class MockContent(object): @property def src(self): return 'src' class MockDocumentListEntry(object): @property def content(self): return MockContent() def GetEditMediaLink(self): return MockLink() class MockKeyczarReader(object): def __init__(self, location): self.location = location class MockKeyczarEncryptedReader(object): def __init__(self, reader, crypter): self._reader = reader self._crypter = crypter class MockKeyczarReaders(object): @staticmethod def CreateReader(location): return MockKeyczarReader(location) @staticmethod def EncryptedReader(reader, crypter): return MockKeyczarEncryptedReader(reader, crypter) class MockKeyczarCrypter(object): def __init__(self, reader): self.reader = reader @staticmethod def Read(location): return MockKeyczarCrypter(MockKeyczarReader(location)) class MockKeyczar(object): @property def readers(self): return MockKeyczarReaders @property def Crypter(self): return MockKeyczarCrypter keyring-3.5/keyring/tests/test_core.py0000666000000000000000000001064212273742000016706 0ustar rootroot""" test_core.py Created by Kang Zhang on 2009-08-09 """ from __future__ import with_statement import os import tempfile import shutil from keyring.tests.py30compat import unittest import mock import keyring.backend import keyring.core from keyring import errors PASSWORD_TEXT = "This is password" PASSWORD_TEXT_2 = "This is password2" KEYRINGRC = "keyringrc.cfg" class TestKeyring(keyring.backend.KeyringBackend): """A faked keyring for test. """ def __init__(self): self.passwords = {} def supported(self): return 0 def get_password(self, service, username): return PASSWORD_TEXT def set_password(self, service, username, password): self.passwords[(service, username)] = password return 0 def delete_password(self, service, username): try: del self.passwords[(service, username)] except KeyError: raise errors.PasswordDeleteError("not set") class TestKeyring2(TestKeyring): """Another faked keyring for test. """ def get_password(self, service, username): return PASSWORD_TEXT_2 class CoreTestCase(unittest.TestCase): mock_global_backend = mock.patch('keyring.core._keyring_backend') @mock_global_backend def test_set_password(self, backend): """ set_password on the default keyring is called. """ keyring.core.set_password("test", "user", "passtest") backend.set_password.assert_called_once_with('test', 'user', 'passtest') @mock_global_backend def test_get_password(self, backend): """ set_password on the default keyring is called. """ result = keyring.core.get_password("test", "user") backend.get_password.assert_called_once_with('test', 'user') self.assertIsNotNone(result) @mock_global_backend def test_delete_password(self, backend): keyring.core.delete_password("test", "user") backend.delete_password.assert_called_once_with('test', 'user') def test_set_keyring_in_runtime(self): """Test the function of set keyring in runtime. """ keyring.core.set_keyring(TestKeyring()) keyring.core.set_password("test", "user", "password") self.assertEqual(keyring.core.get_password("test", "user"), PASSWORD_TEXT) def test_set_keyring_in_config(self): """Test setting the keyring by config file. """ # create the config file config_file = open(KEYRINGRC, 'w') config_file.writelines([ "[backend]\n", # the path for the user created keyring "keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)), # the name of the keyring class "default-keyring=test_core.TestKeyring2\n", ]) config_file.close() # init the keyring lib, the lib will automaticlly load the # config file and load the user defined module keyring.core.init_backend() keyring.core.set_password("test", "user", "password") self.assertEqual(keyring.core.get_password("test", "user"), PASSWORD_TEXT_2) os.remove(KEYRINGRC) def test_load_config(self): tempdir = tempfile.mkdtemp() old_location = os.getcwd() os.chdir(tempdir) personal_cfg = os.path.join(os.path.expanduser("~"), "keyringrc.cfg") if os.path.exists(personal_cfg): os.rename(personal_cfg, personal_cfg + '.old') personal_renamed = True else: personal_renamed = False # loading with an empty environment keyring.core.load_config() # loading with a file that doesn't have a backend section cfg = os.path.join(tempdir, "keyringrc.cfg") f = open(cfg, 'w') f.write('[keyring]') f.close() keyring.core.load_config() # loading with a file that doesn't have a default-keyring value cfg = os.path.join(tempdir, "keyringrc.cfg") f = open(cfg, 'w') f.write('[backend]') f.close() keyring.core.load_config() os.chdir(old_location) shutil.rmtree(tempdir) if personal_renamed: os.rename(personal_cfg + '.old', personal_cfg) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(CoreTestCase)) return suite if __name__ == "__main__": unittest.main(defaultTest="test_suite") keyring-3.5/keyring/tests/test_XDG.py0000666000000000000000000000052612277420134016405 0ustar rootrootfrom mock import patch from keyring.util import XDG class TestPreference: @XDG.Preference('Unity') def one(self): return 1 @patch.dict('os.environ', XDG_CURRENT_DESKTOP='KDE') def test_mismatch(self): assert self.one() == 1 @patch.dict('os.environ', XDG_CURRENT_DESKTOP='Unity') def test_match(self): assert self.one() == 1.5 keyring-3.5/keyring/tests/util.py0000666000000000000000000000360412246200106015670 0ustar rootrootimport contextlib import os import sys import random import string class ImportKiller(object): "Context manager to make an import of a given name or names fail." def __init__(self, *names): self.names = names def find_module(self, fullname, path=None): if fullname in self.names: return self def load_module(self, fullname): assert fullname in self.names raise ImportError(fullname) def __enter__(self): self.original = {} for name in self.names: self.original[name] = sys.modules.pop(name, None) sys.meta_path.insert(0, self) def __exit__(self, *args): sys.meta_path.remove(self) for key, value in self.original.items(): if value is not None: sys.modules[key] = value @contextlib.contextmanager def NoNoneDictMutator(destination, **changes): """Helper context manager to make and unmake changes to a dict. A None is not a valid value for the destination, and so means that the associated name should be removed.""" original = {} for key, value in changes.items(): original[key] = destination.get(key) if value is None: if key in destination: del destination[key] else: destination[key] = value yield for key, value in original.items(): if value is None: if key in destination: del destination[key] else: destination[key] = value def Environ(**changes): """A context manager to temporarily change the os.environ""" return NoNoneDictMutator(os.environ, **changes) ALPHABET = string.ascii_letters + string.digits def random_string(k, source = ALPHABET): """Generate a random string with length k """ result = '' for i in range(0, k): result += random.choice(source) return result keyring-3.5/keyring/tests/__init__.py0000666000000000000000000000012712246200106016447 0ustar rootrootimport logging import sys logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) keyring-3.5/keyring/tests/test_util.py0000666000000000000000000000243112273742000016730 0ustar rootroot# -*- coding: utf-8 -*- """ Test for simple escape/unescape routine """ from .py30compat import unittest from keyring.util import escape class EscapeTestCase(unittest.TestCase): def check_escape_unescape(self, initial): escaped = escape.escape(initial) for c in escaped: self.assertIn(c, escape.LEGAL_CHARS + '_') unescaped = escape.unescape(escaped) self.assertEqual(initial, unescaped) def test_escape_unescape(self): self.check_escape_unescape("aaaa") self.check_escape_unescape("aaaa bbbb cccc") self.check_escape_unescape(escape.u("Zażółć gęślą jaźń")) self.check_escape_unescape("(((P{{{{'''---; ;; '\"|%^") def test_low_byte(self): """ Ensure that encoding low bytes (ordinal less than hex F) encode as as three bytes to avoid ambiguity. For example '\n' (hex A) should encode as '_0A' and not '_A', the latter of which isn't matched by the inverse operation. """ self.check_escape_unescape('\n') self.check_escape_unescape('\x000') def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(EscapeTestCase)) return suite if __name__ == "__main__": unittest.main(defaultTest="test_suite") keyring-3.5/keyring/tests/test_cli.py0000666000000000000000000001140212273742000016520 0ustar rootroot""" Test case to access the keyring from the command line """ import os.path from keyring.tests.py30compat import unittest import keyring.backend from keyring import cli from keyring import errors class FakeKeyring(keyring.backend.KeyringBackend): PASSWORD = "GABUZOMEUH" def supported(self): return 1 def set_password(self, service, username, password): pass def get_password(self, service, username): return self.PASSWORD def delete_password(self, service, username): pass class SimpleKeyring(keyring.backend.KeyringBackend): """A very simple keyring""" def __init__(self): self.pwd = {} def supported(self): return 1 def set_password(self, service, username, password): self.pwd[(service, username)] = password def get_password(self, service, username): try: return self.pwd[(service, username)] except KeyError: return None def delete_password(self, service, username): try: del self.pwd[(service, username)] except KeyError: raise errors.PasswordDeleteError("No key") class CommandLineTestCase(unittest.TestCase): def setUp(self): self.old_keyring = keyring.get_keyring() self.cli = cli.CommandLineTool() self.cli.input_password = self.return_password self.cli.output_password = self.save_password self.cli.parser.error = self.mock_error self.cli.parser.print_help = lambda: None keyring.set_keyring(SimpleKeyring()) self.password = "" self.password_returned = None self.last_error = None def tearDown(self): keyring.set_keyring(self.old_keyring) def return_password(self, *args, **kwargs): return self.password def save_password(self, password): self.password_returned = password def mock_error(self, error): self.last_error = error raise SystemExit() def test_wrong_arguments(self): self.assertEqual(1, self.cli.run([])) self.assertRaises(SystemExit, self.cli.run, ["get"]) self.assertRaises(SystemExit, self.cli.run, ["get", "foo"]) self.assertRaises(SystemExit, self.cli.run, ["get", "foo", "bar", "baz"]) self.assertRaises(SystemExit, self.cli.run, ["set"]) self.assertRaises(SystemExit, self.cli.run, ["set", "foo"]) self.assertRaises(SystemExit, self.cli.run, ["set", "foo", "bar", "baz"]) self.assertRaises(SystemExit, self.cli.run, ["foo", "bar", "baz"]) def test_get_unexistent_password(self): self.assertEqual(1, self.cli.run(["get", "foo", "bar"])) self.assertEqual(None, self.password_returned) def test_set_and_get_password(self): self.password = "plop" self.assertEqual(0, self.cli.run(["set", "foo", "bar"])) self.assertEqual(0, self.cli.run(["get", "foo", "bar"])) self.assertEqual("plop", self.password_returned) def test_load_builtin_backend(self): self.assertEqual(1, self.cli.run([ "get", "-b", "keyring.backends.file.PlaintextKeyring", "foo", "bar"])) backend = keyring.get_keyring() self.assertIsInstance(backend, keyring.backends.file.PlaintextKeyring) def test_load_specific_backend_with_path(self): keyring_path = os.path.join(os.path.dirname(keyring.__file__), 'tests') self.assertEqual(0, self.cli.run(["get", "-b", "test_cli.FakeKeyring", "-p", keyring_path, "foo", "bar"])) # Somehow, this doesn't work, because the full dotted name of the class # is not the same as the one expected :( #self.assertIsInstance(keyring.get_keyring(), FakeKeyring) self.assertEqual(FakeKeyring.PASSWORD, self.password_returned) def test_load_wrong_keyrings(self): self.assertRaises(SystemExit, self.cli.run, ["get", "foo", "bar", "-b", "blablabla" # ImportError ]) self.assertRaises(SystemExit, self.cli.run, ["get", "foo", "bar", "-b", "os.path.blabla" # AttributeError ]) self.assertRaises(SystemExit, self.cli.run, ["get", "foo", "bar", "-b", "__builtin__.str" # TypeError ]) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(CommandLineTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest="test_suite") keyring-3.5/keyring/errors.py0000666000000000000000000000223412273742000015067 0ustar rootrootimport sys class PasswordSetError(Exception): """Raised when the password can't be set. """ class PasswordDeleteError(Exception): """Raised when the password can't be deleted. """ class InitError(Exception): """Raised when the keyring could not be initialised """ class ExceptionRaisedContext(object): """ An exception-trapping context that indicates whether an exception was raised. """ def __init__(self, ExpectedException=Exception): self.ExpectedException = ExpectedException self.exc_info = None def __enter__(self): self.exc_info = object.__new__(ExceptionInfo) return self.exc_info def __exit__(self, *exc_info): self.exc_info.__init__(*exc_info) return self.exc_info.type and issubclass( self.exc_info.type, self.ExpectedException) class ExceptionInfo(object): def __init__(self, *info): if not info: info = sys.exc_info() self.type, self.value, self.traceback = info def __bool__(self): """ Return True if an exception occurred """ return bool(self.type) __nonzero__ = __bool__ keyring-3.5/keyring/py27compat.py0000666000000000000000000000163712273742000015566 0ustar rootroot""" Compatibility support for Python 2.7. Remove when Python 2.7 support is no longer required. """ try: import configparser except ImportError: import ConfigParser as configparser try: input = raw_input except NameError: input = input try: unicode_str = unicode except NameError: unicode_str = str try: import cPickle as pickle except ImportError: import pickle try: from itertools import ifilter as filter except ImportError: filter = filter # Taken from six.py def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) for slots_var in orig_vars.get('__slots__', ()): orig_vars.pop(slots_var) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper keyring-3.5/keyring/core.py0000666000000000000000000001234012277422456014520 0ustar rootroot""" core.py Created by Kang Zhang on 2009-07-09 """ import os import sys import warnings import logging from .py27compat import configparser from keyring import logger from keyring import backend from keyring.util import platform_ as platform log = logging.getLogger(__name__) _keyring_backend = None def set_keyring(keyring): """Set current keyring backend. """ global _keyring_backend if not isinstance(keyring, backend.KeyringBackend): raise TypeError("The keyring must be a subclass of KeyringBackend") _keyring_backend = keyring def get_keyring(): """Get current keyring backend. """ return _keyring_backend def get_password(service_name, username): """Get password from the specified service. """ return _keyring_backend.get_password(service_name, username) def set_password(service_name, username, password): """Set password for the user in the specified service. """ _keyring_backend.set_password(service_name, username, password) def delete_password(service_name, username): """Delete the password for the user in the specified service. """ _keyring_backend.delete_password(service_name, username) def init_backend(): """ Load a keyring specified in the config file or infer the best available. """ _load_library_extensions() set_keyring(load_config() or _get_best_keyring()) def _get_best_keyring(): """ Return the best keyring backend for the given environment based on priority. """ keyrings = backend.get_all_keyring() # rank by priority keyrings.sort(key = lambda x: -x.priority) # get the most recommended one return keyrings[0] def load_keyring(keyring_path, keyring_name): """ Load the specified keyring by name (a fully-qualified name to the keyring, such as 'keyring.backends.file.PlaintextKeyring') `keyring_path` is an additional, optional search path and may be None. **deprecated** In the future, keyring_path must be None. """ module_name, sep, class_name = keyring_name.rpartition('.') if keyring_path is not None and keyring_path not in sys.path: warnings.warn("keyring_path is deprecated and should always be None", DeprecationWarning) sys.path.insert(0, keyring_path) __import__(module_name) module = sys.modules[module_name] class_ = getattr(module, class_name) # invoke the priority to ensure it is viable, or raise a RuntimeError class_.priority return class_() def load_config(): """Load a keyring using the config file. The config file can be in the current working directory, or in the user's home directory. """ keyring = None filename = 'keyringrc.cfg' local_path = os.path.join(os.getcwd(), filename) config_path = os.path.join(platform.config_root(), filename) # search from current working directory and the data root keyring_cfg_candidates = [local_path, config_path] # initialize the keyring_config with the first detected config file keyring_cfg = None for path in keyring_cfg_candidates: keyring_cfg = path if os.path.exists(path): break if os.path.exists(keyring_cfg): config = configparser.RawConfigParser() config.read(keyring_cfg) _load_keyring_path(config) # load the keyring class name, and then load this keyring try: if config.has_section("backend"): keyring_name = config.get("backend", "default-keyring").strip() else: raise configparser.NoOptionError('backend', 'default-keyring') keyring = load_keyring(None, keyring_name) except (configparser.NoOptionError, ImportError): logger.warning("Keyring config file contains incorrect values.\n" + "Config file: %s" % keyring_cfg) return keyring def _load_keyring_path(config): "load the keyring-path option (if present)" try: path = config.get("backend", "keyring-path").strip() sys.path.insert(0, path) except (configparser.NoOptionError, configparser.NoSectionError): pass def _load_library_extensions(): """ Locate all setuptools entry points by the name 'keyring backends' and initialize them. Any third-party library may register an entry point by adding the following to their setup.py:: entry_points = { 'keyring backends': [ 'plugin name = mylib.mymodule:initialize_func', ], }, `plugin name` can be anything. `initialize_func` is optional and will be invoked by keyring on startup. Most plugins will simply provide or import a KeyringBackend in `mymodule`. """ group = 'keyring backends' try: pkg_resources = __import__('pkg_resources') except ImportError: return entry_points = pkg_resources.iter_entry_points(group=group) for ep in entry_points: try: log.info('Loading keyring backends from %s', ep.name) init_func = ep.load() if callable(init_func): init_func() except Exception as exc: log.exception("Error initializing plugin %s (%s).", ep, exc) # init the _keyring_backend init_backend() keyring-3.5/keyring/credentials.py0000666000000000000000000000252212273742000016050 0ustar rootrootimport os import abc from keyring.py27compat import add_metaclass @add_metaclass(abc.ABCMeta) class Credential(object): """Abstract class to manage credentials """ @abc.abstractproperty def username(self): return None @abc.abstractproperty def password(self): return None class SimpleCredential(Credential): """Simple credentials implementation """ def __init__(self, username, password): self._username = username self._password = password @property def username(self): return self._username @property def password(self): return self._password class EnvironCredential(Credential): """Source credentials from environment variables. Actual sourcing is deferred until requested. """ def __init__(self, user_env_var, pwd_env_var): self.user_env_var = user_env_var self.pwd_env_var = pwd_env_var def _get_env(self, env_var): """Helper to read an environment variable """ value = os.environ.get(env_var) if not value: raise ValueError('Missing environment variable:%s' %env_var) return value @property def username(self): return self._get_env(self.user_env_var) @property def password(self): return self._get_env(self.pwd_env_var) keyring-3.5/keyring/util/0000775000000000000000000000000012300426570014155 5ustar rootrootkeyring-3.5/keyring/util/escape.py0000666000000000000000000000273112246200106015766 0ustar rootroot""" escape/unescape routines available for backends which need alphanumeric usernames, services, or other values """ import re import string import sys # True if we are running on Python 3. # taken from six.py PY3 = sys.version_info[0] == 3 # allow use of unicode literals if PY3: def u(s): return s def _unichr(c): return chr(c) else: def u(s): return unicode(s, "unicode_escape") def _unichr(c): return unichr(c) LEGAL_CHARS = ( getattr(string, 'letters', None) # Python 2 or getattr(string, 'ascii_letters') # Python 3 ) + string.digits ESCAPE_FMT = "_%02X" def _escape_char(c): "Single char escape. Return the char, escaped if not already legal" if isinstance(c, int): c = _unichr(c) return c if c in LEGAL_CHARS else ESCAPE_FMT % ord(c) def escape(value): """ Escapes given string so the result consists of alphanumeric chars and underscore only. """ return "".join(_escape_char(c) for c in value.encode('utf-8')) def _unescape_code(regex_match): ordinal = int(regex_match.group('code'), 16) if sys.version_info >= (3,): return bytes([ordinal]) return chr(ordinal) def unescape(value): """ Inverse of escape. """ re_esc = re.compile( # the pattern must be bytes to operate on bytes ESCAPE_FMT.replace('%02X', '(?P[0-9A-F]{2})').encode('ascii') ) return re_esc.sub(_unescape_code, value.encode('ascii')).decode('utf-8') keyring-3.5/keyring/util/XDG.py0000666000000000000000000000105212277417612015163 0ustar rootrootimport os import functools class Preference: """ A decorator wrapping a 'priority' classmethod """ def __init__(self, name): """ Create a decorator giving preference to XDG_CURRENT_DESKTOP of 'name' """ self.name = name def decorate(self, func): self.func = func @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) * self.multiplier return wrapper __call__ = decorate @property def multiplier(self): matches = os.environ.get('XDG_CURRENT_DESKTOP') == self.name return 1.5 if matches else 1 keyring-3.5/keyring/util/properties.py0000666000000000000000000000247112273742000016727 0ustar rootrootfrom collections import Callable class ClassProperty(property): """ An implementation of a property callable on a class. Used to decorate a classmethod but to then treat it like a property. Example: >>> class MyClass: ... @ClassProperty ... @classmethod ... def skillz(cls): ... return cls.__name__.startswith('My') >>> MyClass.skillz True >>> class YourClass(MyClass): pass >>> YourClass.skillz False """ def __get__(self, cls, owner): return self.fget.__get__(None, owner)() # borrowed from jaraco.util.dictlib class NonDataProperty(object): """Much like the property builtin, but only implements __get__, making it a non-data property, and can be subsequently reset. See http://users.rcn.com/python/download/Descriptor.htm for more information. >>> class X(object): ... @NonDataProperty ... def foo(self): ... return 3 >>> x = X() >>> x.foo 3 >>> x.foo = 4 >>> x.foo 4 """ def __init__(self, fget): assert fget is not None, "fget cannot be none" assert isinstance(fget, Callable), "fget must be callable" self.fget = fget def __get__(self, obj, objtype=None): if obj is None: return self return self.fget(obj) keyring-3.5/keyring/util/platform_.py0000666000000000000000000000237512277422606016534 0ustar rootrootfrom __future__ import absolute_import import os import platform def _settings_root_XP(): return os.path.join(os.environ['USERPROFILE'], 'Local Settings') def _settings_root_Vista(): return os.environ.get('LOCALAPPDATA', os.environ.get('ProgramData', '.')) def _data_root_Windows(): release, version, csd, ptype = platform.win32_ver() root = _settings_root_XP() if release == 'XP' else _settings_root_Vista() return os.path.join(root, 'Python Keyring') def _data_root_Linux(): """ Use freedesktop.org Base Dir Specfication to determine storage location. """ fallback = os.path.expanduser('~/.local/share') root = os.environ.get('XDG_DATA_HOME', None) or fallback return os.path.join(root, 'python_keyring') _config_root_Windows = _data_root_Windows def _config_root_Linux(): """ Use freedesktop.org Base Dir Specfication to determine config location. """ fallback = os.path.expanduser('~/.local/share') key = 'XDG_DATA_HOME' # TODO: use XDG_CONFIG_HOME, ref #99. root = os.environ.get(key, None) or fallback return os.path.join(root, 'python_keyring') # by default, use Unix convention data_root = globals().get('_data_root_' + platform.system(), _data_root_Linux) config_root = globals().get('_config_root' + platform.system(), _config_root_Linux) keyring-3.5/keyring/util/__init__.py0000666000000000000000000000176012246200106016266 0ustar rootroottry: import functools except ImportError: # functools not available until Python 2.5 pass def once(func): """ Decorate func so it's only ever called the first time. This decorator can ensure that an expensive or non-idempotent function will not be expensive on subsequent calls and is idempotent. >>> func = once(lambda a: a+3) >>> func(3) 6 >>> func(9) 6 >>> func('12') 6 """ def wrapper(*args, **kwargs): if not hasattr(func, 'always_returns'): func.always_returns = func(*args, **kwargs) return func.always_returns if 'functools' in globals(): wrapper = functools.wraps(func)(wrapper) return wrapper def suppress_exceptions(callables, exceptions=Exception): """ yield the results of calling each element of callables, suppressing any indicated exceptions. """ for callable in callables: try: yield callable() except exceptions: pass keyring-3.5/keyring/__init__.py0000666000000000000000000000050612273742000015312 0ustar rootroot""" __init__.py Created by Kang Zhang on 2009-07-09 """ from __future__ import absolute_import import logging logger = logging.getLogger('keyring') from .core import (set_keyring, get_keyring, set_password, get_password, delete_password) from .getpassbackend import get_password as get_pass_get_password keyring-3.5/keyring/http.py0000666000000000000000000000233412246177330014543 0ustar rootroot""" urllib2.HTTPPasswordMgr object using the keyring, for use with the urllib2.HTTPBasicAuthHandler. usage: import urllib2 handlers = [urllib2.HTTPBasicAuthHandler(PasswordMgr())] urllib2.install_opener(handlers) urllib2.urlopen(...) This will prompt for a password if one is required and isn't already in the keyring. Then, it adds it to the keyring for subsequent use. """ import keyring import getpass class PasswordMgr(object): def get_username(self, realm, authuri): return getpass.getuser() def add_password(self, realm, authuri, password): user = self.get_username(realm, authuri) keyring.set_password(realm, user, password) def find_user_password(self, realm, authuri): user = self.get_username(realm, authuri) password = keyring.get_password(realm, user) if password is None: prompt = 'password for %(user)s@%(realm)s for '\ '%(authuri)s: ' % vars() password = getpass.getpass(prompt) keyring.set_password(realm, user, password) return user, password def clear_password(self, realm, authuri): user = self.get_username(realm, authuri) keyring.delete_password(realm, user) keyring-3.5/keyring/cli.py0000666000000000000000000000636412273742000014332 0ustar rootroot#!/usr/bin/env python """Simple command line interface to get/set password from a keyring""" from __future__ import print_function import getpass from optparse import OptionParser import sys import keyring import keyring.core class CommandLineTool(object): def __init__(self): self.parser = OptionParser( usage="%prog [get|set|del] SERVICE USERNAME") self.parser.add_option("-p", "--keyring-path", dest="keyring_path", default=None, help="Path to the keyring backend") self.parser.add_option("-b", "--keyring-backend", dest="keyring_backend", default=None, help="Name of the keyring backend") def run(self, argv): opts, args = self.parser.parse_args(argv) try: kind, service, username = args except ValueError: if len(args) == 0: # Be nice with the user if he just tries to launch the tool self.parser.print_help() return 1 else: self.parser.error("Wrong number of arguments") if opts.keyring_backend is not None: try: backend = keyring.core.load_keyring(opts.keyring_path, opts.keyring_backend) keyring.set_keyring(backend) except (Exception,): # Tons of things can go wrong here: # ImportError when using "fjkljfljkl" # AttributeError when using "os.path.bar" # TypeError when using "__builtins__.str" # So, we play on the safe side, and catch everything. e = sys.exc_info()[1] self.parser.error("Unable to load specified keyring: %s" % e) if kind == 'get': password = keyring.get_password(service, username) if password is None: return 1 self.output_password(password) return 0 elif kind == 'set': password = self.input_password("Password for '%s' in '%s': " % (username, service)) keyring.set_password(service, username, password) return 0 elif kind == 'del': password = self.input_password("Deleting password for '%s' in '%s': " % (username, service)) keyring.delete_password(service, username) return 0 else: self.parser.error("You can only 'get', 'del' or 'set' a password.") pass def input_password(self, prompt): """Ask for a password to the user. This mostly exists to ease the testing process. """ return getpass.getpass(prompt) def output_password(self, password): """Output the password to the user. This mostly exists to ease the testing process. """ print(password, file=sys.stdout) def main(argv=None): """Main command line interface.""" if argv is None: argv = sys.argv[1:] cli = CommandLineTool() return cli.run(argv) if __name__ == '__main__': sys.exit(main()) keyring-3.5/setup.py0000666000000000000000000000624712277352766013277 0ustar rootroot#!/usr/bin/env python # encoding: utf-8 """ setup.py Setup the Keyring Lib for Python. """ import sys import codecs try: import setuptools setup_mod = setuptools "where to find setup()" except ImportError: import distutils.core setup_mod = distutils.core def load(filename): """ Read a text file and decode it. """ f = codecs.open(filename, encoding='utf-8') try: result = f.read() finally: f.close() if not encodes_as_ascii(result): # see https://bitbucket.org/kang/python-keyring-lib/issue/55 raise ValueError("distutils requires ASCII") return result def encodes_as_ascii(string): try: string.encode('ascii') except UnicodeEncodeError: return False return True test_requirements = [ 'pytest', 'gdata', 'python-keyczar', 'fs', 'mock', 'pycrypto', ] "dependencies for running tests" if sys.version_info < (2, 7) or ( sys.version_info >= (3, 0) and sys.version_info < (3, 1)): # Require unittest2 for Python which doesn't contain the new unittest # module (appears in Python 2.7 and Python 3.5) test_requirements.append('unittest2') if sys.version_info >= (3, 0): # the fs lib doesn't currently install on Python 3. Omit it for now. # See http://code.google.com/p/pyfilesystem/issues/detail?id=135 test_requirements.remove('fs') # gdata doesn't currently install on Python 3. Omit it also. # http://code.google.com/p/gdata-python-client/issues/detail?id=229 test_requirements.remove('gdata') # keyczar doesn't currently install on Python 3. Omit it also. # http://code.google.com/p/keyczar/issues/detail?id=125 test_requirements.remove('python-keyczar') # only request pytest_runner when command-line indicates invocation pytest_runner = ['pytest-runner'] if 'ptr' in sys.argv else [] setup_params = dict( name = 'keyring', version = "3.5", description = "Store and access your passwords safely.", url = "http://bitbucket.org/kang/python-keyring-lib", keywords = "keyring Keychain GnomeKeyring Kwallet password storage", author = "Kang Zhang", author_email = "jobo.zh@gmail.com", maintainer = 'Jason R. Coombs', maintainer_email = 'jaraco@jaraco.com', license = "PSF and MIT", long_description = load('README.rst') + load('CHANGES.rst'), classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "License :: OSI Approved :: Python Software Foundation License", "License :: OSI Approved :: MIT License", ], platforms = ["Many"], packages = ['keyring', 'keyring.tests', 'keyring.util', 'keyring.backends', 'keyring.tests.backends'], extras_require = {'test': test_requirements}, tests_require = test_requirements, setup_requires = [ ] + pytest_runner, entry_points = { 'console_scripts': [ 'keyring=keyring.cli:main', ], }, ) if __name__ == '__main__': setup_mod.setup(**setup_params) keyring-3.5/release.py0000666000000000000000000000033612246200106013520 0ustar rootroot""" Keyring is released using 'jaraco.packaging.release'. To make a release, install jaraco.packaging and run 'python -m jaraco.packaging.release' """ test_info = "Travis-CI tests: http://travis-ci.org/#!/jaraco/keyring" keyring-3.5/CONTRIBUTORS.txt0000666000000000000000000000061212246200106014221 0ustar rootroot------------ Contributors ------------ * Kang Zhang * Tarek Ziade * Marcin Kasperski * Steve Borho * Michael Gruenewald * Jason R. Coombs * Benji York * Jonathan Ballet * Gary Poster * Patrick Mezard * Jérôme Laheurte * Jake Basile * Jelmer Vernooij * André Sintzoff * Thomas Kluyver * Sebastian Ramacher * Robert Leftwich * Kyle Stark * Daniel Holth * Manuel de la Peña * Dmitry Shachnev