keyring-18.0.1/0000775000372000037200000000000013445723360014122 5ustar travistravis00000000000000keyring-18.0.1/.flake80000664000372000037200000000026213445723333015275 0ustar travistravis00000000000000[flake8] ignore = # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 W504 keyring-18.0.1/.gitignore0000664000372000037200000000134113445723333016111 0ustar travistravis00000000000000# Created by https://www.gitignore.io ### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ keyring-18.0.1/.readthedocs.yml0000664000372000037200000000011213445723333017202 0ustar travistravis00000000000000python: version: 3 extra_requirements: - docs pip_install: true keyring-18.0.1/.travis-macos0000664000372000037200000000024413445723333016533 0ustar travistravis00000000000000# Perform the manual steps on osx to install python and activate an environment brew update brew upgrade python export PATH=/usr/local/opt/python/libexec/bin:$PATH keyring-18.0.1/.travis.yml0000664000372000037200000000164313445723333016237 0ustar travistravis00000000000000dist: xenial sudo: false language: python python: - 2.7 - 3.6 - &latest_py3 3.7 jobs: fast_finish: true include: - os: osx language: generic python: *latest_py3 - stage: deploy if: tag IS present python: *latest_py3 before_script: skip env: - TWINE_USERNAME=jaraco # TWINE_PASSWORD - secure: VXM9F3HLDPprEY3JRlX6eH+yngucMHrqpUMeo4zgnFOc5yJUs7RP3uJ87O9IHllOUc7VR+nu/ye7HJk9y8Ux8X53S5UiH/s3/7rHPWP5IyCZBuDMZ3ouSxYKCWyMd6h/tkmVjNJIkV/0ZUhTJlwaODKwqegZ0twEIoRQnVIDTcs= - TOX_TESTENV_PASSENV="TWINE_USERNAME TWINE_PASSWORD" script: tox -e release cache: pip before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source .travis-macos; fi install: - pip install tox tox-venv before_script: # Disable IPv6. Ref travis-ci/travis-ci#8361 - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi script: tox keyring-18.0.1/CHANGES.rst0000664000372000037200000007266213445723333015741 0ustar travistravis0000000000000018.0.1 ------ * #386: ExceptionInfo no longer retains a reference to the traceback. 18.0.0 ------ * #375: On macOS, the backend now raises a ``KeyringLocked`` when access to the keyring is denied (on get or set) instead of ``PasswordSetError`` or ``KeyringError``. Any API users may need to account for this change, probably by catching the parent ``KeyringError``. Additionally, the error message from the underying error is now included in any errors that occur. 17.1.1 ------ * #368: Update packaging technique to avoid 0.0.0 releases. 17.1.0 ------ * #366: When calling ``keyring.core.init_backend``, if any limit function is supplied, it is saved and later honored by the ``ChainerBackend`` as well. 17.0.0 ------ * #345: Remove application attribute from stored passwords using SecretService, addressing regression introduced in 10.5.0 (#292). Impacted Linux keyrings will once again prompt for a password for "Python program". 16.1.1 ------ * #362: Fix error on import due to circular imports on Python 3.4. 16.1.0 ------ * Refactor ChainerBackend, introduced in 16.0 to function as any other backend, activating when relevant. 16.0.2 ------ * #319: In Windows backend, trap all exceptions when attempting to import pywin32. 16.0.1 ------ * #357: Once again allow all positive, non-zero priority keyrings to participate. 16.0.0 ------ * #323: Fix race condition in delete_password on Windows. * #352: All suitable backends (priority 1 and greater) are allowed to participate. 15.2.0 ------ * #350: Added new API for ``get_credentials``, for backends that can resolve both a username and password for a service. 15.1.0 ------ * #340: Add the Null keyring, disabled by default. * #340: Added ``--disable`` option to command-line interface. * #340: Now honor a ``PYTHON_KEYRING_BACKEND`` environment variable to select a backend. Environments may set to ``keyring.backends.null.Keyring`` to disable keyring. 15.0.0 ------ Removed deprecated ``keyring.util.escape`` module. Fixed warning about using deprecated Abstract Base Classes from collections module. 14.0.0 ------ Removed ``getpassbackend`` module and alias in ``keyring.get_pass_get_password``. Instead, just use:: keyring.get_password(getpass.getuser(), 'Python') 13.2.1 ------ * #335: Fix regression in command line client. 13.2.0 ------ * Keyring command-line interface now reads the password directly from stdin if stdin is connected to a pipe. 13.1.0 ------ * #329: Improve output of ``keyring --list-backends``. 13.0.0 ------ * #327: In kwallet backend, if the collection or item is locked, a ``KeyringLocked`` exception is raised. Clients expecting a None response from ``get_password`` under this condition will need to catch this exception. Additionally, an ``InitError`` is now raised if the connection cannot be established to the DBus. * #298: In kwallet backend, when checking an existing handle, verify that it is still valid or create a new connection. 12.2.1 ------ * Fixed issue in SecretService. Ref #226. 12.2.0 ------ * #322: Fix AttributeError when ``escape.__builtins__`` is a dict. * Deprecated ``keyring.util.escape`` module. If you use this module or encounter the warning (on the latest release of your packages), please `file a ticket `_. 12.1.0 ------ * Unpin SecretStorage on Python 3.5+. Requires that Setuptools 17.1 be used. Note that the special handling will be unnecessary once Pip 9 can be assumed (as it will exclude SecretStorage 3 in non-viable environments). 12.0.2 ------ * Pin SecretStorage to 2.x. 12.0.1 ------ * #314: No changes except to rebuild. 12.0.0 ------ * #310: Keyring now loads all backends through entry points. For most users, this release will be fully compatible. Some users may experience compatibility issues if entrypoints is not installed (as declared) or the metadata on which entrypoints relies is unavailable. For that reason, the package is released with a major version bump. 11.1.0 ------ * #312: Use ``entrypoints`` instead of pkg_resources to avoid performance hit loading pkg_resources. Adds a dependency on ``entrypoints``. 11.0.0 ------ * #294: No longer expose ``keyring.__version__`` (added in 8.1) to avoid performance hit loading pkg_resources. 10.6.0 ------ * #299: Keyring exceptions are now derived from a base ``keyring.errors.KeyringError``. 10.5.1 ------ * #296: Prevent AttributeError on import when Debian has created broken dbus installs. 10.5.0 ------ * #287: Added ``--list-backends`` option to command-line interface. * Removed ``logger`` from ``keyring``. See #291 for related request. * #292: Set the appid for SecretService & KWallet to something meaningful. 10.4.0 ------ * #279: In Kwallet, pass mainloop to SessionBus. * #278: Unpin pywin32-ctypes, but blacklist known incompatible versions. 10.3.3 ------ * #278: Pin to pywin32-ctypes 0.0.1 to avoid apparent breakage introduced in 0.1.0. 10.3.2 ------ * #267: More leniently unescape lowercased characters as they get re-cased by ConfigParser. 10.3.1 ------ * #266: Use private compatibity model rather than six to avoid the dependency. 10.3 ---- * #264: Implement devpi hook for supplying a password when logging in with `devpi `_ client. * #260: For macOS, added initial API support for internet passwords. 10.2 ---- * #259: Allow to set a custom application attribute for SecretService backend. 10.1 ---- * #253: Backends now expose a '.name' attribute suitable for identifying each backend to users. 10.0.2 ----- * #247: Restored console script. 10.0.1 ------ * Update readme to reflect test recommendations. 10.0 ---- * Drop support for Python 3.2. * Test suite now uses tox instead of pytest-runner. Test requirements are now defined in tests/requirements.txt. 9.3.1 ----- * Link to the new Gitter chat room is now in the readme. * Issue #235: ``kwallet`` backend now returns string objects instead of ``dbus.String`` objects, for less surprising reprs. * Minor doc fixes. 9.3 --- * Issue #161: In SecretService backend, unlock individual entries. 9.2.1 ----- * Issue #230: Don't rely on dbus-python and instead defer to SecretStorage to describe the installation requirements. 9.2 --- * Issue #231 via #233: On Linux, ``secretstorage`` is now a declared dependency, allowing recommended keyring to work simply after installation. 9.1 --- * Issue #83 via #229: ``kwallet`` backend now stores the service name as a folder name in the backend rather than storing all passwords in a Python folder. 9.0 --- * Issue #217: Once again, the OS X backend uses the Framework API for invoking the Keychain service. As a result, applications utilizing this API will be authorized per application, rather than relying on the authorization of the 'security' application. Consequently, users will be prompted to authorize the system Python executable and also new Python executables, such as those created by virtualenv. #260: No longer does the keyring honor the ``store`` attribute on the keyring. Only application passwords are accessible. 8.7 --- * Changelog now links to issues and provides dates of releases. 8.6 --- * Issue #217: Add warning in OS Keyring when 'store' is set to 'internet' to determine if this feature is used in the wild. 8.5.1 ----- * Pull Request #216: Kwallet backend now has lower priority than the preferred SecretService backend, now that the desktop check is no longer in place. 8.5 --- * Issue #168: Now prefer KF5 Kwallet to KF4. Users relying on KF4 must use prior releases. 8.4 --- * Pull Request #209: Better error message when no backend is available (indicating keyrings.alt as a quick workaround). * Pull Request #208: Fix pywin32-ctypes package name in requirements. 8.3 --- * Issue #207: Library now requires win32ctypes on Windows systems, which will be installed automatically by Setuptools 0.7 or Pip 6 (or later). * Actually removed QtKwallet, which was meant to be dropped in 8.0 but somehow remained. 8.2 --- * Update readme to include how-to use with Linux non-graphical environments. 8.1 --- * Issue #197: Add ``__version__`` attribute to keyring module. 8.0 --- * Issue #117: Removed all but the preferred keyring backends for each of the major desktop platforms: - keyring.backends.kwallet.DBusKeyring - keyring.backends.OS_X.Keyring - keyring.backends.SecretService.Keyring - keyring.backends.Windows.WinVaultKeyring All other keyrings have been moved to a new package, `keyrings.alt `_ and backward-compatibility aliases removed. To retain availability of these less preferred keyrings, include that package in your installation (install both keyring and keyrings.alt). As these keyrings have moved, any keyrings indicated explicitly in configuration will need to be updated to replace "keyring.backends." with "keyrings.alt.". For example, "keyring.backends.file.PlaintextKeyring" becomes "keyrings.alt.file.PlaintextKeyring". 7.3.1 ----- * Issue #194: Redirect away from docs until they have something more than the changelog. Users seeking the changelog will want to follow the `direct link `_. 7.3 --- * Issue #117: Added support for filtering which backends are acceptable. To limit to only loading recommended keyrings (those with priority >= 1), call:: keyring.core.init_backend(limit=keyring.core.recommended) 7.2 --- * Pull Request #190: OS X backend now exposes a ``keychain`` attribute, which if set will be used by ``get_password`` when retrieving passwords. Useful in environments such as when running under cron where the default keychain is not the same as the default keychain in a login session. Example usage:: keyring.get_keyring().keychain = '/path/to/login.keychain' pw = keyring.get_password(...) 7.1 --- * Issue #186: Removed preference for keyrings based on ``XDG_CURRENT_DESKTOP`` as these values are to varied to be a reliable indicator of which keyring implementation might be preferable. 7.0.2 ----- * Issue #187: Restore ``Keyring`` name in ``kwallet`` backend. Users of keyring 6.1 or later should prefer an explicit reference to DBusKeyring or QtKeyring instead. 7.0.1 ----- * Issue #183 and Issue #185: Gnome keyring no longer relies on environment variables, but instead relies on the GnomeKeyring library to determine viability. 7.0 --- * Issue #99: Keyring now expects the config file to be located in the XDG_CONFIG_HOME rather than XDG_DATA_HOME and will fail to start if the config is found in the old location but not the new. On systems where the two locations are distinct, simply copy or symlink the config to remain compatible with older versions or move the file to work only with 7.0 and later. * Replaced Pull Request #182 with a conditional SessionBus construction, based on subsequent discussion. 6.1.1 ----- * Pull Request #182: Prevent DBus from indicating as a viable backend when no viable X DISPLAY variable is present. 6.1 --- * Pull Request #174: Add DBus backend for KWallet, preferred to Qt backend. Theoretically, it should be auto-detected based on available libraries and interchangeable with the Qt backend. 6.0 --- * Drop support for Python 2.6. 5.7.1 ----- * Updated project metadata to match Github hosting and generally refreshed the metadata structure to match practices with other projects. 5.7 --- * Issue #177: Resolve default keyring name on Gnome using the API. * Issue #145: Add workaround for password exposure through process status for most passwords containing simple characters. 5.6 --- * Allow keyring to be invoked from command-line with ``python -m keyring``. 5.5.1 ----- * Issue #156: Fixed test failures in ``pyfs`` keyring related to 0.5 release. 5.5 --- * Pull Request #176: Use recommended mechanism for checking GnomeKeyring version. 5.4 --- * Prefer setuptools_scm to hgtools. 5.3 --- * Prefer hgtools to setuptools_scm due to `setuptools_scm #21 `_. 5.2 --- * Prefer setuptools_scm to hgtools. 5.1 --- * Host project at Github (`repo `_). 5.0 --- * Version numbering is now derived from the code repository tags via `hgtools `_. * Build and install now requires setuptools. 4.1.1 ----- * The entry point group must look like a module name, so the group is now "keyring.backends". 4.1 --- * Added preliminary support for loading keyring backends through ``setuptools entry points``, specifically "keyring backends". 4.0 --- * Removed ``keyring_path`` parameter from ``load_keyring``. See release notes for 3.0.3 for more details. * Issue #22: Removed support for loading the config from the current directory. The config file must now be located in the platform-specific config location. 3.8 --- * Issue #22: Deprecated loading of config from current directory. Support for loading the config in this manner will be removed in a future version. * Issue #131: Keyring now will prefer `pywin32-ctypes `_ to pywin32 if available. 3.7 --- * Gnome keyring no longer relies on the GNOME_KEYRING_CONTROL environment variable. * Issue #140: Restore compatibility for older versions of PyWin32. 3.6 --- * `Pull Request #1 (github) `_: Add support for packages that wish to bundle keyring by using relative imports throughout. 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 compatibility. 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 compatibility 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). * 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-18.0.1/LICENSE0000664000372000037200000000203213445723333015124 0ustar travistravis00000000000000Copyright Jason R. Coombs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. keyring-18.0.1/PKG-INFO0000664000372000037200000004334413445723360015227 0ustar travistravis00000000000000Metadata-Version: 2.1 Name: keyring Version: 18.0.1 Summary: Store and access your passwords safely. Home-page: https://github.com/jaraco/keyring Author: Kang Zhang Author-email: jobo.zh@gmail.com Maintainer: Jason R. Coombs Maintainer-email: jaraco@jaraco.com License: UNKNOWN Description: .. image:: https://img.shields.io/pypi/v/keyring.svg :target: https://pypi.org/project/keyring .. image:: https://img.shields.io/pypi/pyversions/keyring.svg .. image:: https://img.shields.io/travis/jaraco/keyring/master.svg :target: https://travis-ci.org/jaraco/keyring .. image:: https://img.shields.io/appveyor/ci/jaraco/keyring/master.svg :target: https://ci.appveyor.com/project/jaraco/keyring/branch/master .. image:: https://readthedocs.org/projects/keyring/badge/?version=latest :target: https://keyring.readthedocs.io/en/latest/?badge=latest .. image:: https://tidelift.com/badges/github/jaraco/keyring :target: https://tidelift.com/subscription/pkg/pypi-keyring?utm_source=pypi-keyring&utm_medium=readme ======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides an 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. These recommended keyring backends are supported by the Python keyring lib: * macOS `Keychain `_ * Freedesktop `Secret Service `_ supports many DE including GNOME (requires `secretstorage `_) * KDE4 & KDE5 `KWallet `_ (requires `dbus `_) * `Windows Credential Locker `_ Other keyring implementations are available through `Third-Party Backends`_. ------------------------- Installation Instructions ------------------------- Install from Index ================== Install using your favorite installer. For example: $ pip install keyring Linux ----- On Linux, the KWallet backend relies on dbus-python_, which does not always install correctly when using pip (compilation is needed). So we recommend that dbus-python is installed as a system package. The same also applies to the Secret Storage backend under Python 2 (under Python 3 a different D-Bus implementation is used). .. _dbus-python: https://gitlab.freedesktop.org/dbus/dbus-python ------------- 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' Command-line Utility ==================== Keyring supplies a ``keyring`` command which is installed with the package. After installing keyring in most environments, the command should be available for setting, getting, and deleting passwords. For more information on usage, invoke with no arguments or with ``--help`` as so:: $ keyring --help $ keyring set system username Password for 'username' in 'system': $ keyring get system username password The command-line functionality is also exposed as an executable package, suitable for invoking from Python like so:: $ python -m keyring --help $ python -m keyring set system username Password for 'username' in 'system': $ python -m keyring get system username password -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends. The library 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 describes 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, set the **default-keyring** option to the full path of the class for that backend, such as ``keyring.backends.OS_X.Keyring``. If **keyring-path** is indicated, keyring will add that path to the Python module search path before loading the backend. For example, this config might be used to load the ``SimpleKeyring`` from the ``simplekeyring`` module in the ``./demo`` directory (not implemented):: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=demo Third-Party Backends ==================== In addition to the backends provided by the core keyring package for the most common and secure use cases, there are additional keyring backend implementations available for other use-cases. Simply install them to make them available: - `keyrings.cryptfile `_ - Encrypted text file storage. - `keyring_jeepney `__ - a pure Python backend using the secret service DBus API for desktop Linux. - `keyrings.alt `_ - "alternate", possibly-insecure backends, originally part of the core package, but available for opt-in. - `gsheet-keyring `_ - a backend that stores secrets in a Google Sheet. For use with `ipython-secrets `_. - `bitwarden-keyring `_ - a backend that stores secrets in the `BitWarden `_ password manager. Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. Every backend should derive from that base class and define a ``priority`` attribute and three functions: ``get_password()``, ``set_password()``, and ``delete_password()``. The ``get_credential()`` function may be defined if desired. See the ``backend`` module for more detail on the interface of this class. Keyring employs entry points to allow any third-party package to implement backends without any modification to the keyring itself. Those interested in creating new backends are encouraged to create new, third-party packages in the ``keyrings`` namespace, in a manner modeled by the `keyrings.alt package `_. See the ``setup.py`` file in that project for a hint on how to create the requisite entry points. Backends that prove essential may be considered for inclusion in the core library, although the ease of installing these third-party packages should mean that extensions may be readily available. If you've created an extension for Keyring, please submit a pull request to have your extension mentioned as an available extension. Set the keyring in runtime ========================== Keyring additionally allows programmatic configuration of the backend calling the api ``set_keyring()``. The indicated backend will subsequently be used to store and retrieve passwords. Here's an example demonstrating how to invoke ``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 """ priority = 1 def set_password(self, servicename, username, password): pass def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): pass # set the keyring for keyring lib keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print("password stored successfully") except keyring.errors.PasswordSetError: print("failed to store password") print("password", keyring.get_password("demo-service", "tarek")) Using Keyring on Ubuntu 16.04 ============================= The following is a complete transcript for installing keyring in a virtual environment on Ubuntu 16.04. No config file was used.:: $ sudo apt install python3-venv libdbus-glib-1-dev $ cd /tmp $ pyvenv py3 $ source py3/bin/activate $ pip install -U pip $ pip install secretstorage dbus-python $ pip install keyring $ python >>> import keyring >>> keyring.get_keyring() >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' Using Keyring on headless Linux systems ======================================= It is possible to use the SecretService backend on Linux systems without X11 server available (only D-Bus is required). To do that, you need the following: * Install the `GNOME Keyring`_ daemon. * Start a D-Bus session, e.g. run ``dbus-run-session -- sh`` and run the following commands inside that shell. * Run ``gnome-keyring-daemon`` with ``--unlock`` option. The description of that option says: Read a password from stdin, and use it to unlock the login keyring or create it if the login keyring does not exist. When that command is started, enter your password into stdin and press Ctrl+D (end of data). After that the daemon will fork into background (use ``--foreground`` option to prevent that). * Now you can use the SecretService backend of Keyring. Remember to run your application in the same D-Bus session as the daemon. .. _GNOME Keyring: https://wiki.gnome.org/Projects/GnomeKeyring ----------------------------------------------- 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 the active keyring. If the password does not exist, it will return None. * ``get_credential(service, username)``: Return a credential object stored in the active keyring. This object contains at least ``username`` and ``password`` attributes for the specified service, where the returned ``username`` may be different from the argument. * ``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. In all cases, the parameters (``service``, ``username``, ``password``) should be Unicode text. On Python 2, these parameters are accepted as simple ``str`` in the default encoding as they will be implicitly decoded to text. Some backends may accept ``bytes`` for these parameters, but such usage is discouraged. Exceptions ========== The keyring lib raises following exceptions: * ``keyring.errors.KeyringError``: Base Error class for all exceptions in keyring lib. * ``keyring.errors.InitError``: Raised when the keyring can't be initialized. * ``keyring.errors.PasswordSetError``: Raise when password can't be set in the keyring. * ``keyring.errors.PasswordDeleteError``: Raised when the password can't be deleted in the keyring. ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: https://github.com/jaraco/keyring/ * Bug Tracker: https://github.com/jaraco/keyring/issues/ * Mailing list: http://groups.google.com/group/python-keyring Security Contact ================ If you wish to report a security vulnerability, the public disclosure of which may exacerbate the risk, please `Contact Tidelift security `_, which will coordinate the fix and disclosure privately. Making Releases =============== This project makes use of automated releases via Travis-CI. The simple workflow is to tag a commit and push it to Github. If it passes tests on a late Python version, it will be automatically deployed to PyPI. Other things to consider when making a release: - first ensure that tests pass (preferably on Windows and Linux) - check that the changelog is current for the intended release Running Tests ============= Tests are `continuously run `_ using Travis-CI. To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Recommended technique is described below. Using tox --------- Keyring prefers use of `tox `_ to run tests. Simply install and invoke ``tox``. This technique is the one used by the Travis-CI script. ---------- Background ---------- 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/ .. image:: https://badges.gitter.im/jaraco/keyring.svg :alt: Join the chat at https://gitter.im/jaraco/keyring :target: https://gitter.im/jaraco/keyring?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7 Provides-Extra: docs Provides-Extra: testing keyring-18.0.1/README.rst0000664000372000037200000003342213445723333015615 0ustar travistravis00000000000000.. image:: https://img.shields.io/pypi/v/keyring.svg :target: https://pypi.org/project/keyring .. image:: https://img.shields.io/pypi/pyversions/keyring.svg .. image:: https://img.shields.io/travis/jaraco/keyring/master.svg :target: https://travis-ci.org/jaraco/keyring .. image:: https://img.shields.io/appveyor/ci/jaraco/keyring/master.svg :target: https://ci.appveyor.com/project/jaraco/keyring/branch/master .. image:: https://readthedocs.org/projects/keyring/badge/?version=latest :target: https://keyring.readthedocs.io/en/latest/?badge=latest .. image:: https://tidelift.com/badges/github/jaraco/keyring :target: https://tidelift.com/subscription/pkg/pypi-keyring?utm_source=pypi-keyring&utm_medium=readme ======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides an 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. These recommended keyring backends are supported by the Python keyring lib: * macOS `Keychain `_ * Freedesktop `Secret Service `_ supports many DE including GNOME (requires `secretstorage `_) * KDE4 & KDE5 `KWallet `_ (requires `dbus `_) * `Windows Credential Locker `_ Other keyring implementations are available through `Third-Party Backends`_. ------------------------- Installation Instructions ------------------------- Install from Index ================== Install using your favorite installer. For example: $ pip install keyring Linux ----- On Linux, the KWallet backend relies on dbus-python_, which does not always install correctly when using pip (compilation is needed). So we recommend that dbus-python is installed as a system package. The same also applies to the Secret Storage backend under Python 2 (under Python 3 a different D-Bus implementation is used). .. _dbus-python: https://gitlab.freedesktop.org/dbus/dbus-python ------------- 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' Command-line Utility ==================== Keyring supplies a ``keyring`` command which is installed with the package. After installing keyring in most environments, the command should be available for setting, getting, and deleting passwords. For more information on usage, invoke with no arguments or with ``--help`` as so:: $ keyring --help $ keyring set system username Password for 'username' in 'system': $ keyring get system username password The command-line functionality is also exposed as an executable package, suitable for invoking from Python like so:: $ python -m keyring --help $ python -m keyring set system username Password for 'username' in 'system': $ python -m keyring get system username password -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends. The library 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 describes 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, set the **default-keyring** option to the full path of the class for that backend, such as ``keyring.backends.OS_X.Keyring``. If **keyring-path** is indicated, keyring will add that path to the Python module search path before loading the backend. For example, this config might be used to load the ``SimpleKeyring`` from the ``simplekeyring`` module in the ``./demo`` directory (not implemented):: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=demo Third-Party Backends ==================== In addition to the backends provided by the core keyring package for the most common and secure use cases, there are additional keyring backend implementations available for other use-cases. Simply install them to make them available: - `keyrings.cryptfile `_ - Encrypted text file storage. - `keyring_jeepney `__ - a pure Python backend using the secret service DBus API for desktop Linux. - `keyrings.alt `_ - "alternate", possibly-insecure backends, originally part of the core package, but available for opt-in. - `gsheet-keyring `_ - a backend that stores secrets in a Google Sheet. For use with `ipython-secrets `_. - `bitwarden-keyring `_ - a backend that stores secrets in the `BitWarden `_ password manager. Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. Every backend should derive from that base class and define a ``priority`` attribute and three functions: ``get_password()``, ``set_password()``, and ``delete_password()``. The ``get_credential()`` function may be defined if desired. See the ``backend`` module for more detail on the interface of this class. Keyring employs entry points to allow any third-party package to implement backends without any modification to the keyring itself. Those interested in creating new backends are encouraged to create new, third-party packages in the ``keyrings`` namespace, in a manner modeled by the `keyrings.alt package `_. See the ``setup.py`` file in that project for a hint on how to create the requisite entry points. Backends that prove essential may be considered for inclusion in the core library, although the ease of installing these third-party packages should mean that extensions may be readily available. If you've created an extension for Keyring, please submit a pull request to have your extension mentioned as an available extension. Set the keyring in runtime ========================== Keyring additionally allows programmatic configuration of the backend calling the api ``set_keyring()``. The indicated backend will subsequently be used to store and retrieve passwords. Here's an example demonstrating how to invoke ``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 """ priority = 1 def set_password(self, servicename, username, password): pass def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): pass # set the keyring for keyring lib keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print("password stored successfully") except keyring.errors.PasswordSetError: print("failed to store password") print("password", keyring.get_password("demo-service", "tarek")) Using Keyring on Ubuntu 16.04 ============================= The following is a complete transcript for installing keyring in a virtual environment on Ubuntu 16.04. No config file was used.:: $ sudo apt install python3-venv libdbus-glib-1-dev $ cd /tmp $ pyvenv py3 $ source py3/bin/activate $ pip install -U pip $ pip install secretstorage dbus-python $ pip install keyring $ python >>> import keyring >>> keyring.get_keyring() >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' Using Keyring on headless Linux systems ======================================= It is possible to use the SecretService backend on Linux systems without X11 server available (only D-Bus is required). To do that, you need the following: * Install the `GNOME Keyring`_ daemon. * Start a D-Bus session, e.g. run ``dbus-run-session -- sh`` and run the following commands inside that shell. * Run ``gnome-keyring-daemon`` with ``--unlock`` option. The description of that option says: Read a password from stdin, and use it to unlock the login keyring or create it if the login keyring does not exist. When that command is started, enter your password into stdin and press Ctrl+D (end of data). After that the daemon will fork into background (use ``--foreground`` option to prevent that). * Now you can use the SecretService backend of Keyring. Remember to run your application in the same D-Bus session as the daemon. .. _GNOME Keyring: https://wiki.gnome.org/Projects/GnomeKeyring ----------------------------------------------- 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 the active keyring. If the password does not exist, it will return None. * ``get_credential(service, username)``: Return a credential object stored in the active keyring. This object contains at least ``username`` and ``password`` attributes for the specified service, where the returned ``username`` may be different from the argument. * ``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. In all cases, the parameters (``service``, ``username``, ``password``) should be Unicode text. On Python 2, these parameters are accepted as simple ``str`` in the default encoding as they will be implicitly decoded to text. Some backends may accept ``bytes`` for these parameters, but such usage is discouraged. Exceptions ========== The keyring lib raises following exceptions: * ``keyring.errors.KeyringError``: Base Error class for all exceptions in keyring lib. * ``keyring.errors.InitError``: Raised when the keyring can't be initialized. * ``keyring.errors.PasswordSetError``: Raise when password can't be set in the keyring. * ``keyring.errors.PasswordDeleteError``: Raised when the password can't be deleted in the keyring. ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: https://github.com/jaraco/keyring/ * Bug Tracker: https://github.com/jaraco/keyring/issues/ * Mailing list: http://groups.google.com/group/python-keyring Security Contact ================ If you wish to report a security vulnerability, the public disclosure of which may exacerbate the risk, please `Contact Tidelift security `_, which will coordinate the fix and disclosure privately. Making Releases =============== This project makes use of automated releases via Travis-CI. The simple workflow is to tag a commit and push it to Github. If it passes tests on a late Python version, it will be automatically deployed to PyPI. Other things to consider when making a release: - first ensure that tests pass (preferably on Windows and Linux) - check that the changelog is current for the intended release Running Tests ============= Tests are `continuously run `_ using Travis-CI. To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Recommended technique is described below. Using tox --------- Keyring prefers use of `tox `_ to run tests. Simply install and invoke ``tox``. This technique is the one used by the Travis-CI script. ---------- Background ---------- 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/ .. image:: https://badges.gitter.im/jaraco/keyring.svg :alt: Join the chat at https://gitter.im/jaraco/keyring :target: https://gitter.im/jaraco/keyring?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge keyring-18.0.1/appveyor.yml0000664000372000037200000000070613445723333016515 0ustar travistravis00000000000000environment: APPVEYOR: true matrix: - PYTHON: "C:\\Python36-x64" - PYTHON: "C:\\Python27-x64" install: # symlink python from a directory with a space - "mklink /d \"C:\\Program Files\\Python\" %PYTHON%" - "SET PYTHON=\"C:\\Program Files\\Python\"" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" build: off cache: - '%LOCALAPPDATA%\pip\Cache' test_script: - "python -m pip install tox tox-venv" - "tox" version: '{build}' keyring-18.0.1/conftest.py0000664000372000037200000000032513445723333016321 0ustar travistravis00000000000000import platform collect_ignore = [ "hook-keyring.backend.py", ] if platform.system() != 'Darwin': collect_ignore.append('keyring/backends/_OS_X_API.py') collect_ignore.append('keyring/devpi_client.py') keyring-18.0.1/docs/0000775000372000037200000000000013445723360015052 5ustar travistravis00000000000000keyring-18.0.1/docs/_templates/0000775000372000037200000000000013445723360017207 5ustar travistravis00000000000000keyring-18.0.1/docs/_templates/tidelift-sidebar.html0000664000372000037200000000040413445723333023306 0ustar travistravis00000000000000

Professional support

Professionally-supported {{ project }} is available with the Tidelift Subscription.

keyring-18.0.1/docs/conf.py0000664000372000037200000000166213445723333016356 0ustar travistravis00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # flake8: noqa extensions = ["sphinx.ext.autodoc", "jaraco.packaging.sphinx", "rst.linker"] master_doc = "index" link_files = { "../CHANGES.rst": dict( using=dict(GH="https://github.com"), replace=[ dict( pattern=r"(Issue #|\B#)(?P\d+)", url="{package_url}/issues/{issue}", ), dict( pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", ), dict( pattern=r"PEP[- ](?P\d+)", url="https://www.python.org/dev/peps/pep-{pep_number:0>4}/", ), ], ) } # Custom sidebar templates, maps document names to template names. html_theme = 'alabaster' templates_path = ['_templates'] html_sidebars = {'index': 'tidelift-sidebar.html'} keyring-18.0.1/docs/history.rst0000664000372000037200000000012113445723333017277 0ustar travistravis00000000000000:tocdepth: 2 .. _changes: History ******* .. include:: ../CHANGES (links).rst keyring-18.0.1/docs/index.rst0000664000372000037200000000047413445723333016720 0ustar travistravis00000000000000Welcome to keyring documentation! ================================= .. include:: ../README.rst .. toctree:: :maxdepth: 1 history .. automodule:: keyring :members: :undoc-members: :show-inheritance: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` keyring-18.0.1/hook-keyring.backend.py0000664000372000037200000000025013445723333020465 0ustar travistravis00000000000000# Used by pyinstaller to expose hidden imports import entrypoints hiddenimports = [ ep.module_name for ep in entrypoints.get_group_all('keyring.backends') ] keyring-18.0.1/keyring/0000775000372000037200000000000013445723360015572 5ustar travistravis00000000000000keyring-18.0.1/keyring/__init__.py0000664000372000037200000000042513445723333017704 0ustar travistravis00000000000000from __future__ import absolute_import from .core import ( set_keyring, get_keyring, set_password, get_password, delete_password, get_credential) __all__ = ( 'set_keyring', 'get_keyring', 'set_password', 'get_password', 'delete_password', 'get_credential', ) keyring-18.0.1/keyring/__main__.py0000664000372000037200000000010613445723333017661 0ustar travistravis00000000000000if __name__ == '__main__': from keyring import cli cli.main() keyring-18.0.1/keyring/backend.py0000664000372000037200000001366713445723333017550 0ustar travistravis00000000000000""" Keyring implementation support """ from __future__ import absolute_import import abc import logging import operator import entrypoints from . import credentials, errors, util from .util import properties from .py27compat import add_metaclass, filter __metaclass__ = type log = logging.getLogger(__name__) by_priority = operator.attrgetter('priority') _limit = None 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: """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) @classmethod def get_viable_backends(cls): """ Return all subclasses deemed viable. """ return filter(operator.attrgetter('viable'), cls._classes) @properties.ClassProperty @classmethod def name(cls): """ The keyring name, suitable for display. The name is derived from module and class name. """ parent, sep, mod_name = cls.__module__.rpartition('.') mod_name = mod_name.replace('_', ' ') return ' '.join([mod_name, cls.__name__]) def __str__(self): keyring_class = type(self) return ("%s.%s (priority: %g)" % (keyring_class.__module__, keyring_class.__name__, keyring_class.priority)) @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. If the backend cannot store passwords, raise NotImplementedError. """ 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. If the backend cannot store passwords, raise NotImplementedError. """ raise errors.PasswordDeleteError("reason") # for backward-compatibility, don't require a backend to implement # get_credential # @abc.abstractmethod def get_credential(self, service, username): """Gets the username and password for the service. Returns a Credential instance. The *username* argument is optional and may be omitted by the caller or ignored by the backend. Callers must use the returned username. """ # The default implementation requires a username here. if username is not None: password = self.get_password(service, username) if password is not None: return credentials.SimpleCredential( username, password, ) return None class Crypter: """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 def _load_plugins(): """ 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, and is only used to display the name of the plugin at initialization time. `initialize_func` is optional, but will be invoked if callable. """ group = 'keyring.backends' entry_points = entrypoints.get_group_all(group=group) for ep in entry_points: try: log.info('Loading %s', ep.name) init_func = ep.load() if callable(init_func): init_func() except Exception: log.exception("Error initializing plugin %s." % ep) @util.once def get_all_keyring(): """ Return a list of all implemented keyrings that can be constructed without parameters. """ _load_plugins() viable_classes = KeyringBackend.get_viable_backends() rings = util.suppress_exceptions(viable_classes, exceptions=TypeError) return list(rings) keyring-18.0.1/keyring/backends/0000775000372000037200000000000013445723360017344 5ustar travistravis00000000000000keyring-18.0.1/keyring/backends/OS_X.py0000664000372000037200000000415213445723333020530 0ustar travistravis00000000000000import platform from ..backend import KeyringBackend from ..errors import PasswordSetError from ..errors import PasswordDeleteError from ..errors import KeyringLocked from ..errors import KeyringError from ..util import properties try: from . import _OS_X_API as api except Exception: pass class Keyring(KeyringBackend): """macOS Keychain""" keychain = None "Pathname to keychain filename, overriding default keychain." @properties.ClassProperty @classmethod def priority(cls): """ Preferred for all macOS environments. """ if platform.system() != 'Darwin': raise RuntimeError("macOS required") return 5 def set_password(self, service, username, password): if username is None: username = '' try: api.set_generic_password( self.keychain, service, username, password) except api.KeychainDenied as e: raise KeyringLocked("Can't store password on keychain: " "{}".format(e)) except api.Error as e: raise PasswordSetError("Can't store password on keychain: " "{}".format(e)) def get_password(self, service, username): if username is None: username = '' try: return api.find_generic_password(self.keychain, service, username) except api.NotFound: pass except api.KeychainDenied as e: raise KeyringLocked("Can't get password from keychain: " "{}".format(e)) except api.Error as e: raise KeyringError("Can't get password from keychain: " "{}".format(e)) def delete_password(self, service, username): if username is None: username = '' try: return api.delete_generic_password( self.keychain, service, username) except api.Error as e: raise PasswordDeleteError("Can't delete password in keychain: " "{}".format(e)) keyring-18.0.1/keyring/backends/SecretService.py0000664000372000037200000000656613445723333022501 0ustar travistravis00000000000000import logging from ..util import properties from ..backend import KeyringBackend from ..errors import (InitError, PasswordDeleteError, ExceptionRaisedContext, KeyringLocked) try: import secretstorage import secretstorage.exceptions as exceptions except ImportError: pass except AttributeError: # See https://github.com/jaraco/keyring/issues/296 pass log = logging.getLogger(__name__) class Keyring(KeyringBackend): """Secret Service Keyring""" appid = 'Python keyring library' @properties.ClassProperty @classmethod 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.SecretStorageException as e: raise RuntimeError( "Unable to initialize SecretService: %s" % e) return 5 def get_preferred_collection(self): """If self.preferred_collection contains a D-Bus path, the collection at that address is returned. Otherwise, the default collection is returned. """ bus = secretstorage.dbus_init() try: if hasattr(self, 'preferred_collection'): collection = secretstorage.Collection( bus, self.preferred_collection) else: 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 KeyringLocked("Failed to unlock the collection!") return collection def get_password(self, service, username): """Get password of the username for the service """ collection = self.get_preferred_collection() items = collection.search_items( {"username": username, "service": service}) for item in items: if hasattr(item, 'unlock'): item.unlock() if item.is_locked(): # User dismissed the prompt raise KeyringLocked('Failed to unlock the item!') 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_preferred_collection() attributes = { "application": self.appid, "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_preferred_collection() items = collection.search_items( {"username": username, "service": service}) for item in items: return item.delete() raise PasswordDeleteError("No such password!") keyring-18.0.1/keyring/backends/Windows.py0000664000372000037200000001276013445723333021356 0ustar travistravis00000000000000from __future__ import unicode_literals import functools from ..py27compat import text_type from ..util import properties from ..backend import KeyringBackend from ..credentials import SimpleCredential from ..errors import PasswordDeleteError, ExceptionRaisedContext with ExceptionRaisedContext() as missing_deps: try: # prefer pywin32-ctypes from win32ctypes.pywin32 import pywintypes from win32ctypes.pywin32 import win32cred # force demand import to raise ImportError win32cred.__name__ except ImportError: # fallback to pywin32 import pywintypes import win32cred # force demand import to raise ImportError win32cred.__name__ __metaclass__ = type 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 missing_deps: raise RuntimeError("Requires Windows and pywin32") return 5 @staticmethod def _compound_name(username, service): return '%(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 as e: e = OldPywinError.wrap(e) 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, text_type(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): try: win32cred.CredDelete( Type=win32cred.CRED_TYPE_GENERIC, TargetName=target, ) except pywintypes.error as e: e = OldPywinError.wrap(e) if e.winerror == 1168 and e.funcname == 'CredDelete': # not found return raise def get_credential(self, service, username): res = None # get the credentials associated with the provided username if username: res = self._get_password(self._compound_name(username, service)) # get any first password under the service name if not res: res = self._get_password(service) if not res: return None return SimpleCredential( res['UserName'], res['CredentialBlob'].decode('utf-16'), ) class OldPywinError: """ A compatibility wrapper for old PyWin32 errors, such as reported in https://bitbucket.org/kang/python-keyring-lib/issue/140/ """ def __init__(self, orig): self.orig = orig @property def funcname(self): return self.orig[1] @property def winerror(self): return self.orig[0] @classmethod def wrap(cls, orig_err): attr_check = functools.partial(hasattr, orig_err) is_old = not all(map(attr_check, ['funcname', 'winerror'])) return cls(orig_err) if is_old else orig_err keyring-18.0.1/keyring/backends/_OS_X_API.py0000664000372000037200000002264713445723333021371 0ustar travistravis00000000000000import contextlib import ctypes import struct from ctypes import c_void_p, c_uint16, c_uint32, c_int32, c_char_p, POINTER from keyring.py27compat import string_types, add_metaclass __metaclass__ = type sec_keychain_ref = sec_keychain_item_ref = c_void_p OS_status = c_int32 class error: item_not_found = -25300 keychain_denied = -128 sec_auth_failed = -25293 plist_missing = -67030 fw = '/System/Library/Frameworks/{name}.framework/Versions/A/{name}'.format _sec = ctypes.CDLL(fw(name='Security')) _core = ctypes.CDLL(fw(name='CoreServices')) SecKeychainOpen = _sec.SecKeychainOpen SecKeychainOpen.argtypes = ( c_char_p, POINTER(sec_keychain_ref), ) SecKeychainOpen.restype = OS_status SecKeychainCopyDefault = _sec.SecKeychainCopyDefault SecKeychainCopyDefault.argtypes = POINTER(sec_keychain_ref), SecKeychainCopyDefault.restype = OS_status class Error(Exception): @classmethod def raise_for_status(cls, status): if status == 0: return if status == error.item_not_found: raise NotFound(status, "Item not found") if status == error.keychain_denied: raise KeychainDenied(status, "Keychain Access Denied") if status == error.sec_auth_failed or status == error.plist_missing: raise SecAuthFailure(status, "Security Auth Failure: make sure " "python is signed with codesign util") raise cls(status, "Unknown Error") class NotFound(Error): pass class KeychainDenied(Error): pass class SecAuthFailure(Error): pass @contextlib.contextmanager def open(name): ref = sec_keychain_ref() if name is None: status = SecKeychainCopyDefault(ref) else: status = SecKeychainOpen(name.encode('utf-8'), ref) Error.raise_for_status(status) try: yield ref finally: _core.CFRelease(ref) SecKeychainFindGenericPassword = _sec.SecKeychainFindGenericPassword SecKeychainFindGenericPassword.argtypes = ( sec_keychain_ref, c_uint32, c_char_p, c_uint32, c_char_p, POINTER(c_uint32), # passwordLength POINTER(c_void_p), # passwordData POINTER(sec_keychain_item_ref), # itemRef ) SecKeychainFindGenericPassword.restype = OS_status def find_generic_password(kc_name, service, username): username = username.encode('utf-8') service = service.encode('utf-8') with open(kc_name) as keychain: length = c_uint32() data = c_void_p() status = SecKeychainFindGenericPassword( keychain, len(service), service, len(username), username, length, data, None, ) Error.raise_for_status(status) password = ctypes.create_string_buffer(length.value) ctypes.memmove(password, data.value, length.value) SecKeychainItemFreeContent(None, data) return password.raw.decode('utf-8') SecKeychainFindInternetPassword = _sec.SecKeychainFindInternetPassword SecKeychainFindInternetPassword.argtypes = ( sec_keychain_ref, # keychainOrArray c_uint32, # serverNameLength c_char_p, # serverName c_uint32, # securityDomainLength c_char_p, # securityDomain c_uint32, # accountNameLength c_char_p, # accountName c_uint32, # pathLength c_char_p, # path c_uint16, # port c_uint32, # SecProtocolType protocol, c_uint32, # SecAuthenticationType authenticationType, POINTER(c_uint32), # passwordLength POINTER(c_void_p), # passwordData POINTER(sec_keychain_item_ref), # itemRef ) SecKeychainFindInternetPassword.restype = OS_status class PackedAttributes(type): """ Take the attributes which use magic words to represent enumerated constants and generate the constants. """ def __new__(cls, name, bases, dict): dict.update( (key, cls.unpack(val)) for key, val in dict.items() if not key.startswith('_') ) return super(PackedAttributes, cls).__new__(cls, name, bases, dict) @staticmethod def unpack(word): r""" >>> PackedAttributes.unpack(0) 0 >>> PackedAttributes.unpack('\x00\x00\x00\x01') 1 >>> PackedAttributes.unpack('abcd') 1633837924 """ if not isinstance(word, string_types): return word val, = struct.unpack('!I', word.encode('ascii')) return val @add_metaclass(PackedAttributes) class SecProtocolType: kSecProtocolTypeHTTP = 'http' kSecProtocolTypeHTTPS = 'htps' kSecProtocolTypeFTP = 'ftp ' @add_metaclass(PackedAttributes) class SecAuthenticationType: """ >>> SecAuthenticationType.kSecAuthenticationTypeDefault 1684434036 """ kSecAuthenticationTypeDefault = 'dflt' kSecAuthenticationTypeAny = 0 def find_internet_password(kc_name, service, username): username = username.encode('utf-8') domain = None service = service.encode('utf-8') path = None port = 0 with open(kc_name) as keychain: length = c_uint32() data = c_void_p() status = SecKeychainFindInternetPassword( keychain, len(service), service, 0, domain, len(username), username, 0, path, port, SecProtocolType.kSecProtocolTypeHTTPS, SecAuthenticationType.kSecAuthenticationTypeAny, length, data, None, ) Error.raise_for_status(status) password = ctypes.create_string_buffer(length.value) ctypes.memmove(password, data.value, length.value) SecKeychainItemFreeContent(None, data) return password.raw.decode('utf-8') SecKeychainAddGenericPassword = _sec.SecKeychainAddGenericPassword SecKeychainAddGenericPassword.argtypes = ( sec_keychain_ref, c_uint32, c_char_p, c_uint32, c_char_p, c_uint32, c_char_p, POINTER(sec_keychain_item_ref), ) SecKeychainAddGenericPassword.restype = OS_status def set_generic_password(name, service, username, password): username = username.encode('utf-8') service = service.encode('utf-8') password = password.encode('utf-8') with open(name) as keychain: item = sec_keychain_item_ref() status = SecKeychainFindGenericPassword( keychain, len(service), service, len(username), username, None, None, item) if status: if status == error.item_not_found: status = SecKeychainAddGenericPassword( keychain, len(service), service, len(username), username, len(password), password, None) else: status = SecKeychainItemModifyAttributesAndData( item, None, len(password), password) _core.CFRelease(item) Error.raise_for_status(status) SecKeychainAddInternetPassword = _sec.SecKeychainAddInternetPassword SecKeychainAddInternetPassword.argtypes = ( sec_keychain_ref, # keychainOrArray c_uint32, # serverNameLength c_char_p, # serverName c_uint32, # securityDomainLength c_char_p, # securityDomain c_uint32, # accountNameLength c_char_p, # accountName c_uint32, # pathLength c_char_p, # path c_uint16, # port c_uint32, # SecProtocolType protocol, c_uint32, # SecAuthenticationType authenticationType, c_uint32, # passwordLength c_void_p, # passwordData POINTER(sec_keychain_item_ref), # itemRef ) SecKeychainAddInternetPassword.restype = OS_status def set_internet_password(name, service, username, password): username = username.encode('utf-8') domain = None service = service.encode('utf-8') password = password.encode('utf-8') path = None port = 0 with open(name) as keychain: # TODO: Use update or set technique as seen in set_generic_password status = SecKeychainAddInternetPassword( keychain, len(service), service, 0, domain, len(username), username, 0, path, port, SecProtocolType.kSecProtocolTypeHTTPS, SecAuthenticationType.kSecAuthenticationTypeAny, len(password), password, None, ) Error.raise_for_status(status) SecKeychainItemModifyAttributesAndData = ( _sec.SecKeychainItemModifyAttributesAndData) SecKeychainItemModifyAttributesAndData.argtypes = ( sec_keychain_item_ref, c_void_p, c_uint32, c_void_p, ) SecKeychainItemModifyAttributesAndData.restype = OS_status SecKeychainItemFreeContent = _sec.SecKeychainItemFreeContent SecKeychainItemFreeContent.argtypes = ( c_void_p, c_void_p, ) SecKeychainItemFreeContent.restype = OS_status SecKeychainItemDelete = _sec.SecKeychainItemDelete SecKeychainItemDelete.argtypes = sec_keychain_item_ref, SecKeychainItemDelete.restype = OS_status def delete_generic_password(name, service, username): username = username.encode('utf-8') service = service.encode('utf-8') with open(name) as keychain: length = c_uint32() data = c_void_p() item = sec_keychain_item_ref() status = SecKeychainFindGenericPassword( keychain, len(service), service, len(username), username, length, data, item, ) Error.raise_for_status(status) SecKeychainItemDelete(item) _core.CFRelease(item) keyring-18.0.1/keyring/backends/__init__.py0000664000372000037200000000000013445723333021443 0ustar travistravis00000000000000keyring-18.0.1/keyring/backends/chainer.py0000664000372000037200000000375013445723333021334 0ustar travistravis00000000000000""" Keyring Chainer - iterates over other viable backends to discover passwords in each. """ from __future__ import absolute_import from .. import backend from ..util import properties class ChainerBackend(backend.KeyringBackend): """ >>> ChainerBackend() """ # override viability as 'priority' cannot be determined # until other backends have been constructed viable = True @properties.ClassProperty @classmethod def priority(cls): """ High-priority if there are backends to chain, otherwise 0. """ return 10 * (len(cls.backends) > 1) @properties.ClassProperty @classmethod def backends(cls): """ Discover all keyrings for chaining. """ allowed = ( keyring for keyring in filter(backend._limit, backend.get_all_keyring()) if not isinstance(keyring, ChainerBackend) and keyring.priority > 0 ) return sorted(allowed, key=backend.by_priority, reverse=True) def get_password(self, service, username): for keyring in self.backends: password = keyring.get_password(service, username) if password is not None: return password def set_password(self, service, username, password): for keyring in self.backends: try: return keyring.set_password(service, username, password) except NotImplementedError: pass def delete_password(self, service, username): for keyring in self.backends: try: return keyring.delete_password(service, username) except NotImplementedError: pass def get_credential(self, service, username): for keyring in self.backends: credential = keyring.get_credential(service, username) if credential is not None: return credential keyring-18.0.1/keyring/backends/fail.py0000664000372000037200000000137513445723333020637 0ustar travistravis00000000000000from ..backend import KeyringBackend class Keyring(KeyringBackend): """ Keyring that raises error on every operation. >>> kr = Keyring() >>> kr.get_password('svc', 'user') Traceback (most recent call last): ... RuntimeError: ...No recommended backend... """ priority = 0 def get_password(self, service, username, password=None): msg = ( "No recommended backend was available. Install a recommended 3rd " "party backend package; or, install the keyrings.alt package if " "you want to use the non-recommended backends. See " "https://pypi.org/project/keyring for details." ) raise RuntimeError(msg) set_password = delete_password = get_password keyring-18.0.1/keyring/backends/kwallet.py0000664000372000037200000001132613445723334021365 0ustar travistravis00000000000000from __future__ import absolute_import import sys import os from ..backend import KeyringBackend from ..errors import PasswordDeleteError from ..errors import PasswordSetError, InitError, KeyringLocked from ..util import properties try: import dbus from dbus.mainloop.glib import DBusGMainLoop except ImportError: pass except AttributeError: # See https://github.com/jaraco/keyring/issues/296 pass class DBusKeyring(KeyringBackend): """ KDE KWallet 5 via D-Bus """ appid = os.path.basename(sys.argv[0]) or 'Python keyring library' wallet = None bus_name = 'org.kde.kwalletd5' object_path = '/modules/kwalletd5' @properties.ClassProperty @classmethod def priority(cls): if 'dbus' not in globals(): raise RuntimeError('python-dbus not installed') try: bus = dbus.SessionBus(mainloop=DBusGMainLoop()) except dbus.DBusException as exc: raise RuntimeError(exc.get_dbus_message()) try: bus.get_object(cls.bus_name, cls.object_path) except dbus.DBusException: tmpl = 'cannot connect to {bus_name}' msg = tmpl.format(bus_name=cls.bus_name) raise RuntimeError(msg) if "KDE" in os.getenv("XDG_CURRENT_DESKTOP", "").split(":"): return 5.1 return 4.9 def __init__(self, *arg, **kw): super(DBusKeyring, self).__init__(*arg, **kw) self.handle = -1 def _migrate(self, service): old_folder = 'Python' entry_list = [] if self.iface.hasFolder(self.handle, old_folder, self.appid): entry_list = self.iface.readPasswordList( self.handle, old_folder, '*@*', self.appid) for entry in entry_list.items(): key = entry[0] password = entry[1] username, service = key.rsplit('@', 1) ret = self.iface.writePassword( self.handle, service, username, password, self.appid) if ret == 0: self.iface.removeEntry( self.handle, old_folder, key, self.appid) entry_list = self.iface.readPasswordList( self.handle, old_folder, '*', self.appid) if not entry_list: self.iface.removeFolder(self.handle, old_folder, self.appid) def connected(self, service): if self.handle >= 0: if self.iface.isOpen(self.handle): return True bus = dbus.SessionBus(mainloop=DBusGMainLoop()) wId = 0 try: remote_obj = bus.get_object(self.bus_name, self.object_path) self.iface = dbus.Interface(remote_obj, 'org.kde.KWallet') self.handle = self.iface.open( self.iface.networkWallet(), wId, self.appid) except dbus.DBusException as e: raise InitError('Failed to open keyring: %s.' % e) if self.handle < 0: return False self._migrate(service) return True def get_password(self, service, username): """Get password of the username for the service """ if not self.connected(service): # the user pressed "cancel" when prompted to unlock their keyring. raise KeyringLocked("Failed to unlock the keyring!") if not self.iface.hasEntry(self.handle, service, username, self.appid): return None password = self.iface.readPassword( self.handle, service, username, self.appid) return str(password) def set_password(self, service, username, password): """Set password for the username of the service """ if not self.connected(service): # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordSetError("Cancelled by user") self.iface.writePassword( self.handle, service, username, password, self.appid) def delete_password(self, service, username): """Delete the password for the username of the service. """ if not self.connected(service): # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordDeleteError("Cancelled by user") if not self.iface.hasEntry(self.handle, service, username, self.appid): raise PasswordDeleteError("Password not found") self.iface.removeEntry(self.handle, service, username, self.appid) class DBusKeyringKWallet4(DBusKeyring): """ KDE KWallet 4 via D-Bus """ bus_name = 'org.kde.kwalletd' object_path = '/modules/kwalletd' @properties.ClassProperty @classmethod def priority(cls): return super(DBusKeyringKWallet4, cls).priority - 1 keyring-18.0.1/keyring/backends/null.py0000664000372000037200000000053013445723334020667 0ustar travistravis00000000000000from ..backend import KeyringBackend class Keyring(KeyringBackend): """ Keyring that return None on every operation. >>> kr = Keyring() >>> kr.get_password('svc', 'user') """ priority = -1 def get_password(self, service, username, password=None): pass set_password = delete_password = get_password keyring-18.0.1/keyring/cli.py0000775000372000037200000001021113445723334016712 0ustar travistravis00000000000000#!/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 from . import core from . import backend from . import set_keyring, get_password, set_password, delete_password __metaclass__ = type class CommandLineTool: 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") self.parser.add_option("--list-backends", action="store_true", help="List keyring backends and exit") self.parser.add_option("--disable", action="store_true", help="Disable keyring and exit") def run(self, argv): opts, args = self.parser.parse_args(argv) if opts.list_backends: for k in backend.get_all_keyring(): print(k) return if opts.disable: core.disable() return 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: if opts.keyring_path: sys.path.insert(0, opts.keyring_path) set_keyring(core.load_keyring(opts.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 = 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)) set_password(service, username, password) return 0 elif kind == 'del': password = self.input_password( "Deleting password for '%s' in '%s': " % (username, service), ) 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): """Retrieve password from input. """ return self.pass_from_pipe() or getpass.getpass(prompt) @classmethod def pass_from_pipe(cls): """Return password from pipe if not on TTY, else False. """ is_pipe = not sys.stdin.isatty() return is_pipe and cls.strip_last_newline(sys.stdin.read()) @staticmethod def strip_last_newline(str): """Strip one last newline, if present.""" return str[:-str.endswith('\n')] 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-18.0.1/keyring/core.py0000664000372000037200000001172413445723334017102 0ustar travistravis00000000000000""" Core API functions and initialization routines. """ import os import sys import logging from .py27compat import configparser, filter from .py33compat import max from . import backend from .util import platform_ as platform from .backends import fail 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 disable(): """ Configure the null keyring as the default. """ root = platform.config_root() try: os.makedirs(root) except OSError: pass filename = os.path.join(root, 'keyringrc.cfg') if os.path.exists(filename): msg = "Refusing to overwrite {filename}".format(**locals()) raise RuntimeError(msg) with open(filename, 'w') as file: file.write('[backend]\ndefault-keyring=keyring.backends.null.Keyring') 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 get_credential(service_name, username): """Get a Credential for the specified service. """ return _keyring_backend.get_credential(service_name, username) def recommended(backend): return backend.priority >= 1 def init_backend(limit=None): """ Load a keyring specified in the config file or infer the best available. Limit, if supplied, should be a callable taking a backend and returning True if that backend should be included for consideration. """ # save the limit for the chainer to honor backend._limit = limit # get all keyrings passing the limit filter keyrings = filter(limit, backend.get_all_keyring()) set_keyring( load_env() or load_config() or max(keyrings, default=fail.Keyring(), key=backend.by_priority) ) def _load_keyring_class(keyring_name): """ Load the keyring class indicated by name. These popular names are tested to ensure their presence. >>> popular_names = [ ... 'keyring.backends.Windows.WinVaultKeyring', ... 'keyring.backends.OS_X.Keyring', ... 'keyring.backends.kwallet.DBusKeyring', ... 'keyring.backends.SecretService.Keyring', ... ] >>> list(map(_load_keyring_class, popular_names)) [...] These legacy names are retained for compatibility. >>> legacy_names = [ ... ] >>> list(map(_load_keyring_class, legacy_names)) [...] """ module_name, sep, class_name = keyring_name.rpartition('.') __import__(module_name) module = sys.modules[module_name] return getattr(module, class_name) def load_keyring(keyring_name): """ Load the specified keyring by name (a fully-qualified name to the keyring, such as 'keyring.backends.file.PlaintextKeyring') """ class_ = _load_keyring_class(keyring_name) # invoke the priority to ensure it is viable, or raise a RuntimeError class_.priority return class_() def load_env(): """Load a keyring configured in the environment variable.""" try: return load_keyring(os.environ['PYTHON_KEYRING_BACKEND']) except KeyError: pass def load_config(): """Load a keyring using the config file in the config root.""" filename = 'keyringrc.cfg' keyring_cfg = os.path.join(platform.config_root(), filename) if not os.path.exists(keyring_cfg): return 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') except (configparser.NoOptionError, ImportError): logger = logging.getLogger('keyring') logger.warning("Keyring config file contains incorrect values.\n" + "Config file: %s" % keyring_cfg) return return load_keyring(keyring_name) 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 # init the _keyring_backend init_backend() keyring-18.0.1/keyring/credentials.py0000664000372000037200000000253513445723334020447 0ustar travistravis00000000000000import os import abc from .py27compat import add_metaclass __metaclass__ = type @add_metaclass(abc.ABCMeta) class Credential: """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-18.0.1/keyring/devpi_client.py0000664000372000037200000000030713445723334020612 0ustar travistravis00000000000000from pluggy import HookimplMarker import keyring hookimpl = HookimplMarker("devpiclient") @hookimpl() def devpiclient_get_password(url, username): return keyring.get_password(url, username) keyring-18.0.1/keyring/errors.py0000664000372000037200000000253713445723334017470 0ustar travistravis00000000000000import sys __metaclass__ = type class KeyringError(Exception): """Base class for exceptions in keyring """ class PasswordSetError(KeyringError): """Raised when the password can't be set. """ class PasswordDeleteError(KeyringError): """Raised when the password can't be deleted. """ class InitError(KeyringError): """Raised when the keyring could not be initialised """ class KeyringLocked(KeyringError): """Raised when the keyring could not be initialised """ class ExceptionRaisedContext: """ 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: def __init__(self, *info): if not info: info = sys.exc_info() self.type, self.value, _ = info def __bool__(self): """ Return True if an exception occurred """ return bool(self.type) __nonzero__ = __bool__ keyring-18.0.1/keyring/http.py0000664000372000037200000000236613445723334017133 0ustar travistravis00000000000000""" 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 getpass from . import get_password, delete_password, set_password __metaclass__ = type class PasswordMgr: def get_username(self, realm, authuri): return getpass.getuser() def add_password(self, realm, authuri, password): user = self.get_username(realm, authuri) set_password(realm, user, password) def find_user_password(self, realm, authuri): user = self.get_username(realm, authuri) password = get_password(realm, user) if password is None: prompt = 'password for %(user)s@%(realm)s for '\ '%(authuri)s: ' % vars() password = getpass.getpass(prompt) set_password(realm, user, password) return user, password def clear_password(self, realm, authuri): user = self.get_username(realm, authuri) delete_password(realm, user) keyring-18.0.1/keyring/py27compat.py0000664000372000037200000000207713445723334020160 0ustar travistravis00000000000000""" 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 # noqa try: input = raw_input except NameError: input = input try: text_type = unicode string_types = unicode, str except NameError: text_type = str string_types = str, try: import cPickle as pickle except ImportError: import pickle # noqa 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 try: import builtins except ImportError: import __builtin__ as builtins # noqa keyring-18.0.1/keyring/py32compat.py0000664000372000037200000000015313445723334020145 0ustar travistravis00000000000000try: from collections import abc except ImportError: import collections as abc __all__ = ['abc'] keyring-18.0.1/keyring/py33compat.py0000664000372000037200000000122413445723334020146 0ustar travistravis00000000000000""" Compatibility support for Python 3.3. Remove when Python 3.3 support is no longer required. """ from .py27compat import builtins def max(*args, **kwargs): """ Add support for 'default' kwarg. >>> max([], default='res') 'res' >>> max(default='res') Traceback (most recent call last): ... TypeError: ... >>> max('a', 'b', default='other') 'b' """ missing = object() default = kwargs.pop('default', missing) try: return builtins.max(*args, **kwargs) except ValueError as exc: if 'empty sequence' in str(exc) and default is not missing: return default raise keyring-18.0.1/keyring/tests/0000775000372000037200000000000013445723360016734 5ustar travistravis00000000000000keyring-18.0.1/keyring/tests/__init__.py0000664000372000037200000000012713445723334021046 0ustar travistravis00000000000000import logging import sys logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) keyring-18.0.1/keyring/tests/backends/0000775000372000037200000000000013445723360020506 5ustar travistravis00000000000000keyring-18.0.1/keyring/tests/backends/__init__.py0000664000372000037200000000000013445723334022606 0ustar travistravis00000000000000keyring-18.0.1/keyring/tests/backends/test_OS_X.py0000664000372000037200000000061613445723334022733 0ustar travistravis00000000000000import sys import unittest from ..test_backend import BackendBasicTests from keyring.backends import OS_X def is_osx_keychain_supported(): return sys.platform in ('mac', 'darwin') @unittest.skipUnless(is_osx_keychain_supported(), "Need macOS") class OSXKeychainTestCase(BackendBasicTests, unittest.TestCase): def init_keyring(self): return OS_X.Keyring() keyring-18.0.1/keyring/tests/backends/test_SecretService.py0000664000372000037200000000200213445723334024660 0ustar travistravis00000000000000import 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") keyring = SecretService.Keyring() keyring.preferred_collection = ( '/org/freedesktop/secrets/collection/session' ) return 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-18.0.1/keyring/tests/backends/test_Windows.py0000664000372000037200000000161013445723334023550 0ustar travistravis00000000000000from __future__ import print_function import sys import unittest import pytest import keyring.backends.Windows from ..test_backend import BackendBasicTests @unittest.skipUnless(keyring.backends.Windows.WinVaultKeyring.viable, "Needs Windows") 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() @pytest.mark.skipif('sys.platform != "win32"') def test_winvault_always_viable(): """ The WinVault backend should always be viable on Windows. """ assert keyring.backends.Windows.WinVaultKeyring.viable keyring-18.0.1/keyring/tests/backends/test_chainer.py0000664000372000037200000000204613445723334023533 0ustar travistravis00000000000000import pytest import keyring.backends.chainer from keyring import backend @pytest.fixture def two_keyrings(monkeypatch): def get_two(): class Keyring1(backend.KeyringBackend): priority = 1 def get_password(self, system, user): return 'ring1-{system}-{user}'.format(**locals()) def set_password(self, system, user, password): pass class Keyring2(backend.KeyringBackend): priority = 2 def get_password(self, system, user): return 'ring2-{system}-{user}'.format(**locals()) def set_password(self, system, user, password): raise NotImplementedError() return Keyring1(), Keyring2() monkeypatch.setattr('keyring.backend.get_all_keyring', get_two) class TestChainer: def test_chainer_gets_from_highest_priority(self, two_keyrings): chainer = keyring.backends.chainer.ChainerBackend() pw = chainer.get_password('alpha', 'bravo') assert pw == 'ring2-alpha-bravo' keyring-18.0.1/keyring/tests/backends/test_kwallet.py0000664000372000037200000000602713445723334023570 0ustar travistravis00000000000000import unittest from keyring.backends import kwallet from ..test_backend import BackendBasicTests @unittest.skipUnless(kwallet.DBusKeyring.viable, "KWallet5 unavailable") class DBusKWalletTestCase(BackendBasicTests, unittest.TestCase): # Remove '@' from service name as this is not supported in service names # '@' will cause troubles during migration of kwallet entries DIFFICULT_CHARS = BackendBasicTests.DIFFICULT_CHARS.replace('@', '') def init_keyring(self): return kwallet.DBusKeyring() def tearDown(self): for item in self.credentials_created: # Suppress errors, as only one pre/post migration item will be # present try: self.keyring.delete_password(*item) except BaseException: pass # TODO Remove empty folders created during tests def set_password(self, service, username, password, old_format=False): # set the password and save the result so the test runner can clean # up after if necessary. self.credentials_created.add((service, username)) if old_format: username = username + '@' + service service = 'Python' super( DBusKWalletTestCase, self).set_password( service, username, password) 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, True) # re-init keyring to force migration self.keyring = keyring = self.init_keyring() ret_password = keyring.get_password(service, username) self.assertEqual( ret_password, password, "Incorrect password for username: '%s' " "on service: '%s'. '%s' != '%s'" % (service, username, ret_password, password)) # for the empty password self.set_password(service, username, "", True) # re-init keyring to force migration self.keyring = keyring = self.init_keyring() ret_password = keyring.get_password(service, username) self.assertEqual( ret_password, "", "Incorrect password for username: '%s' " "on service: '%s'. '%s' != '%s'" % (service, username, ret_password, "")) ret_password = keyring.get_password('Python', username + '@' + service) self.assertEqual( ret_password, None, "Not 'None' password returned for username: '%s' " "on service: '%s'. '%s' != '%s'. Passwords from old " "folder should be deleted during migration." % (service, username, ret_password, None)) @unittest.skipUnless(kwallet.DBusKeyringKWallet4.viable, "KWallet4 unavailable") class DBusKWallet4TestCase(DBusKWalletTestCase): def init_keyring(self): return kwallet.DBusKeyringKWallet4() keyring-18.0.1/keyring/tests/test_backend.py0000664000372000037200000001322113445723334021734 0ustar travistravis00000000000000# coding: utf-8 """ Common test functionality for backends. """ from __future__ import unicode_literals import string import pytest from .util import random_string from keyring import errors __metaclass__ = type # unicode only characters # Sourced from The Quick Brown Fox... Pangrams # http://www.columbia.edu/~fdc/utf8/ UNICODE_CHARS = ( "זהכיףסתםלשמועאיךתנצחקרפדעץטובבגן" "ξεσκεπάζωτηνψυχοφθόραβδελυγμία" "Съешьжеещёэтихмягкихфранцузскихбулокдавыпейчаю" "Жълтатадюлябешещастливачепухъткойтоцъфназамръзнакатогьон" ) # ensure no-ascii chars slip by - watch your editor! assert min(ord(char) for char in UNICODE_CHARS) > 127 def is_ascii_printable(s): return all(32 <= ord(c) < 127 for c in s) class BackendBasicTests: """Test for the keyring's basic functions. password_set and password_get """ DIFFICULT_CHARS = string.whitespace + string.punctuation 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 assert keyring.get_password(service, username) is None # common usage self.set_password(service, username, password) assert keyring.get_password(service, username) == password # for the empty password self.set_password(service, username, "") assert 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, self.DIFFICULT_CHARS) username = random_string(20, self.DIFFICULT_CHARS) service = random_string(20, self.DIFFICULT_CHARS) self.check_set_get(service, username, password) def test_delete_present(self): password = random_string(20, self.DIFFICULT_CHARS) username = random_string(20, self.DIFFICULT_CHARS) service = random_string(20, self.DIFFICULT_CHARS) self.keyring.set_password(service, username, password) self.keyring.delete_password(service, username) assert self.keyring.get_password(service, username) is None def test_delete_not_present(self): username = random_string(20, self.DIFFICULT_CHARS) service = random_string(20, self.DIFFICULT_CHARS) with pytest.raises(errors.PasswordDeleteError): self.keyring.delete_password(service, username) def test_delete_one_in_group(self): username1 = random_string(20, self.DIFFICULT_CHARS) username2 = random_string(20, self.DIFFICULT_CHARS) password = random_string(20, self.DIFFICULT_CHARS) service = random_string(20, self.DIFFICULT_CHARS) self.keyring.set_password(service, username1, password) self.set_password(service, username2, password) self.keyring.delete_password(service, username1) assert self.keyring.get_password(service, username2) == password def test_name_property(self): assert is_ascii_printable(self.keyring.name) 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, self.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') assert keyring.get_password('service1', 'user1') == 'password1' assert keyring.get_password('service1', 'user2') == 'password2' self.set_password('service2', 'user3', 'password3') assert keyring.get_password('service1', 'user1') == 'password1' def test_credential(self): keyring = self.keyring cred = keyring.get_credential('service', None) assert cred is None self.set_password('service1', 'user1', 'password1') self.set_password('service1', 'user2', 'password2') cred = keyring.get_credential('service1', None) assert cred is None or (cred.username, cred.password) in ( ('user1', 'password1'), ('user2', 'password2'), ) cred = keyring.get_credential('service1', 'user2') assert cred is not None assert (cred.username, cred.password) in ( ('user1', 'password1'), ('user2', 'password2'), ) keyring-18.0.1/keyring/tests/util.py0000664000372000037200000000363113445723334020267 0ustar travistravis00000000000000import contextlib import os import sys import random import string __metaclass__ = type class ImportKiller: "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-18.0.1/keyring/util/0000775000372000037200000000000013445723360016547 5ustar travistravis00000000000000keyring-18.0.1/keyring/util/__init__.py0000664000372000037200000000154213445723334020663 0ustar travistravis00000000000000import functools 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 return functools.wraps(func)(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-18.0.1/keyring/util/platform_.py0000664000372000037200000000431513445723334021110 0ustar travistravis00000000000000from __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 _check_old_config_root(): """ Prior versions of keyring would search for the config in XDG_DATA_HOME, but should probably have been searching for config in XDG_CONFIG_HOME. If the config exists in the former but not in the latter, raise a RuntimeError to force the change. """ # disable the check - once is enough and avoids infinite loop globals()['_check_old_config_root'] = lambda: None config_file_new = os.path.join(_config_root_Linux(), 'keyringrc.cfg') config_file_old = os.path.join(_data_root_Linux(), 'keyringrc.cfg') if os.path.isfile(config_file_old) and not os.path.isfile(config_file_new): msg = ("Keyring config exists only in the old location " "{config_file_old} and should be moved to {config_file_new} " "to work with this version of keyring.") raise RuntimeError(msg.format(**locals())) def _config_root_Linux(): """ Use freedesktop.org Base Dir Specfication to determine config location. """ _check_old_config_root() fallback = os.path.expanduser('~/.local/share') key = 'XDG_CONFIG_HOME' 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-18.0.1/keyring/util/properties.py0000664000372000037200000000250313445723334021316 0ustar travistravis00000000000000from ..py32compat import abc __metaclass__ = type 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: """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: ... @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, abc.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-18.0.1/keyring.egg-info/0000775000372000037200000000000013445723360017264 5ustar travistravis00000000000000keyring-18.0.1/keyring.egg-info/PKG-INFO0000664000372000037200000004334413445723357020377 0ustar travistravis00000000000000Metadata-Version: 2.1 Name: keyring Version: 18.0.1 Summary: Store and access your passwords safely. Home-page: https://github.com/jaraco/keyring Author: Kang Zhang Author-email: jobo.zh@gmail.com Maintainer: Jason R. Coombs Maintainer-email: jaraco@jaraco.com License: UNKNOWN Description: .. image:: https://img.shields.io/pypi/v/keyring.svg :target: https://pypi.org/project/keyring .. image:: https://img.shields.io/pypi/pyversions/keyring.svg .. image:: https://img.shields.io/travis/jaraco/keyring/master.svg :target: https://travis-ci.org/jaraco/keyring .. image:: https://img.shields.io/appveyor/ci/jaraco/keyring/master.svg :target: https://ci.appveyor.com/project/jaraco/keyring/branch/master .. image:: https://readthedocs.org/projects/keyring/badge/?version=latest :target: https://keyring.readthedocs.io/en/latest/?badge=latest .. image:: https://tidelift.com/badges/github/jaraco/keyring :target: https://tidelift.com/subscription/pkg/pypi-keyring?utm_source=pypi-keyring&utm_medium=readme ======================================= Installing and Using Python Keyring Lib ======================================= .. contents:: **Table of Contents** --------------------------- What is Python keyring lib? --------------------------- The Python keyring lib provides an 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. These recommended keyring backends are supported by the Python keyring lib: * macOS `Keychain `_ * Freedesktop `Secret Service `_ supports many DE including GNOME (requires `secretstorage `_) * KDE4 & KDE5 `KWallet `_ (requires `dbus `_) * `Windows Credential Locker `_ Other keyring implementations are available through `Third-Party Backends`_. ------------------------- Installation Instructions ------------------------- Install from Index ================== Install using your favorite installer. For example: $ pip install keyring Linux ----- On Linux, the KWallet backend relies on dbus-python_, which does not always install correctly when using pip (compilation is needed). So we recommend that dbus-python is installed as a system package. The same also applies to the Secret Storage backend under Python 2 (under Python 3 a different D-Bus implementation is used). .. _dbus-python: https://gitlab.freedesktop.org/dbus/dbus-python ------------- 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' Command-line Utility ==================== Keyring supplies a ``keyring`` command which is installed with the package. After installing keyring in most environments, the command should be available for setting, getting, and deleting passwords. For more information on usage, invoke with no arguments or with ``--help`` as so:: $ keyring --help $ keyring set system username Password for 'username' in 'system': $ keyring get system username password The command-line functionality is also exposed as an executable package, suitable for invoking from Python like so:: $ python -m keyring --help $ python -m keyring set system username Password for 'username' in 'system': $ python -m keyring get system username password -------------------------- Configure your keyring lib -------------------------- The python keyring lib contains implementations for several backends. The library 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 describes 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, set the **default-keyring** option to the full path of the class for that backend, such as ``keyring.backends.OS_X.Keyring``. If **keyring-path** is indicated, keyring will add that path to the Python module search path before loading the backend. For example, this config might be used to load the ``SimpleKeyring`` from the ``simplekeyring`` module in the ``./demo`` directory (not implemented):: [backend] default-keyring=simplekeyring.SimpleKeyring keyring-path=demo Third-Party Backends ==================== In addition to the backends provided by the core keyring package for the most common and secure use cases, there are additional keyring backend implementations available for other use-cases. Simply install them to make them available: - `keyrings.cryptfile `_ - Encrypted text file storage. - `keyring_jeepney `__ - a pure Python backend using the secret service DBus API for desktop Linux. - `keyrings.alt `_ - "alternate", possibly-insecure backends, originally part of the core package, but available for opt-in. - `gsheet-keyring `_ - a backend that stores secrets in a Google Sheet. For use with `ipython-secrets `_. - `bitwarden-keyring `_ - a backend that stores secrets in the `BitWarden `_ password manager. Write your own keyring backend ============================== The interface for the backend is defined by ``keyring.backend.KeyringBackend``. Every backend should derive from that base class and define a ``priority`` attribute and three functions: ``get_password()``, ``set_password()``, and ``delete_password()``. The ``get_credential()`` function may be defined if desired. See the ``backend`` module for more detail on the interface of this class. Keyring employs entry points to allow any third-party package to implement backends without any modification to the keyring itself. Those interested in creating new backends are encouraged to create new, third-party packages in the ``keyrings`` namespace, in a manner modeled by the `keyrings.alt package `_. See the ``setup.py`` file in that project for a hint on how to create the requisite entry points. Backends that prove essential may be considered for inclusion in the core library, although the ease of installing these third-party packages should mean that extensions may be readily available. If you've created an extension for Keyring, please submit a pull request to have your extension mentioned as an available extension. Set the keyring in runtime ========================== Keyring additionally allows programmatic configuration of the backend calling the api ``set_keyring()``. The indicated backend will subsequently be used to store and retrieve passwords. Here's an example demonstrating how to invoke ``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 """ priority = 1 def set_password(self, servicename, username, password): pass def get_password(self, servicename, username): return "password from TestKeyring" def delete_password(self, servicename, username, password): pass # set the keyring for keyring lib keyring.set_keyring(TestKeyring()) # invoke the keyring lib try: keyring.set_password("demo-service", "tarek", "passexample") print("password stored successfully") except keyring.errors.PasswordSetError: print("failed to store password") print("password", keyring.get_password("demo-service", "tarek")) Using Keyring on Ubuntu 16.04 ============================= The following is a complete transcript for installing keyring in a virtual environment on Ubuntu 16.04. No config file was used.:: $ sudo apt install python3-venv libdbus-glib-1-dev $ cd /tmp $ pyvenv py3 $ source py3/bin/activate $ pip install -U pip $ pip install secretstorage dbus-python $ pip install keyring $ python >>> import keyring >>> keyring.get_keyring() >>> keyring.set_password("system", "username", "password") >>> keyring.get_password("system", "username") 'password' Using Keyring on headless Linux systems ======================================= It is possible to use the SecretService backend on Linux systems without X11 server available (only D-Bus is required). To do that, you need the following: * Install the `GNOME Keyring`_ daemon. * Start a D-Bus session, e.g. run ``dbus-run-session -- sh`` and run the following commands inside that shell. * Run ``gnome-keyring-daemon`` with ``--unlock`` option. The description of that option says: Read a password from stdin, and use it to unlock the login keyring or create it if the login keyring does not exist. When that command is started, enter your password into stdin and press Ctrl+D (end of data). After that the daemon will fork into background (use ``--foreground`` option to prevent that). * Now you can use the SecretService backend of Keyring. Remember to run your application in the same D-Bus session as the daemon. .. _GNOME Keyring: https://wiki.gnome.org/Projects/GnomeKeyring ----------------------------------------------- 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 the active keyring. If the password does not exist, it will return None. * ``get_credential(service, username)``: Return a credential object stored in the active keyring. This object contains at least ``username`` and ``password`` attributes for the specified service, where the returned ``username`` may be different from the argument. * ``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. In all cases, the parameters (``service``, ``username``, ``password``) should be Unicode text. On Python 2, these parameters are accepted as simple ``str`` in the default encoding as they will be implicitly decoded to text. Some backends may accept ``bytes`` for these parameters, but such usage is discouraged. Exceptions ========== The keyring lib raises following exceptions: * ``keyring.errors.KeyringError``: Base Error class for all exceptions in keyring lib. * ``keyring.errors.InitError``: Raised when the keyring can't be initialized. * ``keyring.errors.PasswordSetError``: Raise when password can't be set in the keyring. * ``keyring.errors.PasswordDeleteError``: Raised when the password can't be deleted in the keyring. ------------ Get involved ------------ Python keyring lib is an open community project and highly welcomes new contributors. * Repository: https://github.com/jaraco/keyring/ * Bug Tracker: https://github.com/jaraco/keyring/issues/ * Mailing list: http://groups.google.com/group/python-keyring Security Contact ================ If you wish to report a security vulnerability, the public disclosure of which may exacerbate the risk, please `Contact Tidelift security `_, which will coordinate the fix and disclosure privately. Making Releases =============== This project makes use of automated releases via Travis-CI. The simple workflow is to tag a commit and push it to Github. If it passes tests on a late Python version, it will be automatically deployed to PyPI. Other things to consider when making a release: - first ensure that tests pass (preferably on Windows and Linux) - check that the changelog is current for the intended release Running Tests ============= Tests are `continuously run `_ using Travis-CI. To run the tests yourself, you'll want keyring installed to some environment in which it can be tested. Recommended technique is described below. Using tox --------- Keyring prefers use of `tox `_ to run tests. Simply install and invoke ``tox``. This technique is the one used by the Travis-CI script. ---------- Background ---------- 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/ .. image:: https://badges.gitter.im/jaraco/keyring.svg :alt: Join the chat at https://gitter.im/jaraco/keyring :target: https://gitter.im/jaraco/keyring?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7 Provides-Extra: docs Provides-Extra: testing keyring-18.0.1/keyring.egg-info/SOURCES.txt0000664000372000037200000000254313445723357021162 0ustar travistravis00000000000000.flake8 .gitignore .readthedocs.yml .travis-macos .travis.yml CHANGES.rst LICENSE README.rst appveyor.yml conftest.py hook-keyring.backend.py pyproject.toml pytest.ini setup.cfg setup.py skeleton.md tox.ini docs/conf.py docs/history.rst docs/index.rst docs/_templates/tidelift-sidebar.html keyring/__init__.py keyring/__main__.py keyring/backend.py keyring/cli.py keyring/core.py keyring/credentials.py keyring/devpi_client.py keyring/errors.py keyring/http.py keyring/py27compat.py keyring/py32compat.py keyring/py33compat.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/OS_X.py keyring/backends/SecretService.py keyring/backends/Windows.py keyring/backends/_OS_X_API.py keyring/backends/__init__.py keyring/backends/chainer.py keyring/backends/fail.py keyring/backends/kwallet.py keyring/backends/null.py keyring/tests/__init__.py keyring/tests/test_backend.py keyring/tests/util.py keyring/tests/backends/__init__.py keyring/tests/backends/test_OS_X.py keyring/tests/backends/test_SecretService.py keyring/tests/backends/test_Windows.py keyring/tests/backends/test_chainer.py keyring/tests/backends/test_kwallet.py keyring/util/__init__.py keyring/util/platform_.py keyring/util/properties.py tests/test_packaging.pykeyring-18.0.1/keyring.egg-info/dependency_links.txt0000664000372000037200000000000113445723357023340 0ustar travistravis00000000000000 keyring-18.0.1/keyring.egg-info/entry_points.txt0000664000372000037200000000044713445723357022575 0ustar travistravis00000000000000[console_scripts] keyring = keyring.cli:main [devpi_client] keyring = keyring.devpi_client [keyring.backends] KWallet = keyring.backends.kwallet SecretService = keyring.backends.SecretService Windows = keyring.backends.Windows chainer = keyring.backends.chainer macOS = keyring.backends.OS_X keyring-18.0.1/keyring.egg-info/requires.txt0000664000372000037200000000054413445723357021675 0ustar travistravis00000000000000entrypoints [:(sys_platform == "linux2" or sys_platform == "linux") and python_version < "3.5"] secretstorage<3 [:sys_platform == "linux" and python_version >= "3.5"] secretstorage [:sys_platform == "win32"] pywin32-ctypes!=0.1.0,!=0.1.1 [docs] sphinx jaraco.packaging>=3.2 rst.linker>=1.9 [testing] pytest!=3.7.3,>=3.5 pytest-checkdocs pytest-flake8 keyring-18.0.1/keyring.egg-info/top_level.txt0000664000372000037200000000001013445723357022013 0ustar travistravis00000000000000keyring keyring-18.0.1/pyproject.toml0000664000372000037200000000017013445723334017035 0ustar travistravis00000000000000[build-system] requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] build-backend = "setuptools.build_meta" keyring-18.0.1/pytest.ini0000664000372000037200000000071013445723334016152 0ustar travistravis00000000000000[pytest] norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 ignore:Using or importing the ABCs::flake8:410 # workaround for https://sourceforge.net/p/docutils/bugs/348/ ignore:'U' mode is deprecated::docutils.io # workaround for https://gitlab.com/pycqa/flake8/issues/275 ignore:You passed a bytestring as `filenames`.::flake8 keyring-18.0.1/setup.cfg0000664000372000037200000000321713445723360015746 0ustar travistravis00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE name = keyring author = Kang Zhang author_email = jobo.zh@gmail.com maintainer = Jason R. Coombs maintainer_email = jaraco@jaraco.com description = Store and access your passwords safely. long_description = file:README.rst url = https://github.com/jaraco/keyring classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: MIT License License :: OSI Approved :: Python Software Foundation License License :: OSI Approved :: MIT License Programming Language :: Python :: 2.7 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 [options] packages = find: include_package_data = true python_requires = >=2.7 install_requires = entrypoints pywin32-ctypes!=0.1.0,!=0.1.1; sys_platform=="win32" secretstorage; sys_platform=="linux" and python_version>="3.5" secretstorage<3; (sys_platform=="linux2" or sys_platform=="linux") and python_version<"3.5" setup_requires = setuptools_scm >= 1.15.0 [options.extras_require] testing = pytest >= 3.5, !=3.7.3 pytest-checkdocs pytest-flake8 docs = sphinx jaraco.packaging >= 3.2 rst.linker >= 1.9 [options.entry_points] console_scripts = keyring=keyring.cli:main devpi_client = keyring = keyring.devpi_client keyring.backends = Windows = keyring.backends.Windows macOS = keyring.backends.OS_X SecretService = keyring.backends.SecretService KWallet = keyring.backends.kwallet chainer = keyring.backends.chainer [egg_info] tag_build = tag_date = 0 keyring-18.0.1/setup.py0000664000372000037200000000016013445723334015632 0ustar travistravis00000000000000#!/usr/bin/env python import setuptools if __name__ == "__main__": setuptools.setup(use_scm_version=True) keyring-18.0.1/skeleton.md0000664000372000037200000001744113445723334016300 0ustar travistravis00000000000000# Overview This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. ## An SCM Managed Approach While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a git repo capturing the evolution and culmination of these best practices. It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton. # Usage ## new projects To use skeleton for a new project, simply pull the skeleton into a new project: ``` $ git init my-new-project $ cd my-new-project $ git pull gh://jaraco/skeleton ``` Now customize the project to suit your individual project needs. ## existing projects If you have an existing project, you can still incorporate the skeleton by merging it into the codebase. ``` $ git merge skeleton --allow-unrelated-histories ``` The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton. ## Updating Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar git operations. Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. # Features The features/techniques employed by the skeleton include: - PEP 517/518 based build relying on setuptools as the build tool - setuptools declarative configuration using setup.cfg - tox for running tests - A README.rst as reStructuredText with some popular badges, but with readthedocs and appveyor badges commented out - A CHANGES.rst file intended for publishing release notes about the project. ## Packaging Conventions A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on setuptools (a minimum version compatible with setup.cfg declarative config). The setup.cfg file implements the following features: - Assumes universal wheel for release - Advertises the project's LICENSE file (MIT by default) - Reads the README.rst file into the long description - Some common Trove classifiers - Includes all packages discovered in the repo - Data files in the package are also included (not just Python files) - Declares the required Python versions - Declares install requirements (empty by default) - Declares setup requirements for legacy environments - Supplies two 'extras': - testing: requirements for running tests - docs: requirements for building docs - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts - Placeholder for defining entry points Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: - derive the project version from SCM tags - ensure that all files committed to the repo are automatically included in releases ## Running Tests The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest). Other environments (invoked with `tox -e {name}`) supplied include: - a `build-docs` environment to build the documentation - a `release` environment to publish the package to PyPI A pytest.ini is included to define common options around running tests. In particular: - rely on default test discovery in the current directory - avoid recursing into common directories not containing tests - run doctests on modules and invoke flake8 tests - in doctests, allow unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. - filters out known warnings caused by libraries/functionality included by the skeleton Relies a .flake8 file to correct some default behaviors: - allow tabs for indentation (legacy for jaraco projects) - disable mutually incompatible rules W503 and W504. ## Continuous Integration The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command. In addition to running tests, an additional deploy stage is configured to automatically release tagged commits. The username and password for PyPI must be configured for each project using the `travis` command and only after the travis project is created. As releases are cut with [twine](https://pypi.org/project/twine), the two values are supplied through the `TWINE_USERNAME` and `TWINE_PASSWORD`. To configure the latter as a secret, run the following command: ``` echo "TWINE_PASSWORD={password}" | travis encrypt ``` Or disable it in the CI definition and configure it through the web UI. Features include: - test against Python 2 and 3 - run on Ubuntu Xenial - correct for broken IPv6 Also provided is a minimal template for running under Appveyor (Windows). ## Building Documentation Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e build-docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. In addition to building the sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. ## Cutting releases By default, tagged commits are released through the continuous integration deploy stage. keyring-18.0.1/tests/0000775000372000037200000000000013445723360015264 5ustar travistravis00000000000000keyring-18.0.1/tests/test_packaging.py0000664000372000037200000000050213445723334020617 0ustar travistravis00000000000000import pkg_resources def test_entry_point(): """ Keyring provides exactly one 'keyring' console script that's a callable. """ eps = pkg_resources.iter_entry_points('console_scripts') ep, = ( ep for ep in eps if ep.name == 'keyring' ) assert callable(ep.resolve()) keyring-18.0.1/tox.ini0000664000372000037200000000105113445723334015433 0ustar travistravis00000000000000[tox] envlist = python minversion = 2.4 [testenv] deps = setuptools>=31.0.1 commands = pytest {posargs} usedevelop = True extras = testing [testenv:build-docs] extras = docs testing changedir = docs commands = python -m sphinx . {toxinidir}/build/html [testenv:release] skip_install = True deps = pep517>=0.5 # workaround for https://github.com/pypa/twine/issues/423 git+https://github.com/pypa/twine path.py commands = python -c "import path; path.Path('dist').rmtree_p()" python -m pep517.build . python -m twine upload dist/*