[Python-modules-commits] [keyrings.alt] 01/04: Import keyrings.alt_1.1.orig.tar.gz

Dmitry Shachnev mitya57 at moszumanska.debian.org
Sat Feb 6 09:32:15 UTC 2016


This is an automated email from the git hooks/post-receive script.

mitya57 pushed a commit to branch master
in repository keyrings.alt.

commit afa347e693b1ad6b9467d7437eacc2ab62d53b43
Author: Dmitry Shachnev <mitya57 at gmail.com>
Date:   Sat Feb 6 12:05:53 2016 +0300

    Import keyrings.alt_1.1.orig.tar.gz
---
 .gitignore                                   |   0
 .hgtags                                      |   3 +
 .travis.yml                                  |   8 +
 CHANGES.rst                                  |  10 +
 PKG-INFO                                     |  20 ++
 README.rst                                   |   5 +
 conftest.py                                  |   6 +
 docs/conf.py                                 |  19 ++
 docs/history.rst                             |   8 +
 docs/index.rst                               |  56 +++++
 keyrings.alt.egg-info/PKG-INFO               |  20 ++
 keyrings.alt.egg-info/SOURCES.txt            |  39 ++++
 keyrings.alt.egg-info/dependency_links.txt   |   1 +
 keyrings.alt.egg-info/entry_points.txt       |  10 +
 keyrings.alt.egg-info/namespace_packages.txt |   1 +
 keyrings.alt.egg-info/top_level.txt          |   2 +
 keyrings/__init__.py                         |   1 +
 keyrings/alt/Gnome.py                        | 108 +++++++++
 keyrings/alt/Google.py                       | 321 ++++++++++++++++++++++++++
 keyrings/alt/Windows.py                      | 193 ++++++++++++++++
 keyrings/alt/__init__.py                     |   0
 keyrings/alt/_win_crypto.py                  | 101 ++++++++
 keyrings/alt/file.py                         | 299 ++++++++++++++++++++++++
 keyrings/alt/keyczar.py                      |  99 ++++++++
 keyrings/alt/kwallet.py                      | 114 +++++++++
 keyrings/alt/multi.py                        |  63 +++++
 keyrings/alt/pyfs.py                         | 272 ++++++++++++++++++++++
 pytest.ini                                   |   4 +
 setup.cfg                                    |  12 +
 setup.py                                     |  79 +++++++
 tests/__init__.py                            |   0
 tests/test_Gnome.py                          |  35 +++
 tests/test_Google.py                         | 331 +++++++++++++++++++++++++++
 tests/test_Windows.py                        |  49 ++++
 tests/test_crypto.py                         |  32 +++
 tests/test_file.py                           |  52 +++++
 tests/test_keyczar.py                        |  76 ++++++
 tests/test_kwallet.py                        |  81 +++++++
 tests/test_multi.py                          |  58 +++++
 tests/test_pyfs.py                           | 136 +++++++++++
 40 files changed, 2724 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/.hgtags b/.hgtags
new file mode 100644
index 0000000..fb6f4b7
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,3 @@
+b3173ad03e2b87d50ab31eecc5e6e57e9963785d 1.0
+f9defd8cbdfabaeb1739fd02929f272ba7e4be73 1.0.1
+acdefb2c6c473bf6460004dbbe1f62280fcdeac8 1.1
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6e5e969
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+sudo: false
+language: python
+python:
+ - 2.7
+ - 3.5
+script:
+ - pip install -U pytest
+ - python setup.py test
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000..b8a1c5a
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,10 @@
+1.1
+===
+
+FileBacked backends now have a ``repr`` that includes the file path.
+
+
+1.0
+===
+
+Initial release based on Keyring 7.3.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..5ba33cf
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,20 @@
+Metadata-Version: 1.1
+Name: keyrings.alt
+Version: 1.1
+Summary: Alternate keyring implementations
+Home-page: https://github.com/jaraco/keyrings.alt
+Author: Jason R. Coombs
+Author-email: jaraco at jaraco.com
+License: UNKNOWN
+Description: keyrings.alt
+        ============
+        
+        Alternate keyring backend implementations for use with the
+        `keyring package <https://pypi.python.org/pypi/keyring>`_.
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..83a0c9f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,5 @@
+keyrings.alt
+============
+
+Alternate keyring backend implementations for use with the
+`keyring package <https://pypi.python.org/pypi/keyring>`_.
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..4af6077
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,6 @@
+import platform
+
+collect_ignore = []
+
+if platform.system() != 'Windows':
+    collect_ignore.append('keyrings/alt/_win_crypto.py')
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..7a15d08
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import setuptools_scm
+
+extensions = [
+    'sphinx.ext.autodoc',
+]
+
+# General information about the project.
+project = 'keyrings.alt'
+copyright = '2016 Jason R. Coombs'
+
+# The short X.Y version.
+version = setuptools_scm.get_version(root='..', relative_to=__file__)
+# The full version, including alpha/beta/rc tags.
+release = version
+
+master_doc = 'index'
diff --git a/docs/history.rst b/docs/history.rst
new file mode 100644
index 0000000..907000b
--- /dev/null
+++ b/docs/history.rst
@@ -0,0 +1,8 @@
+:tocdepth: 2
+
+.. _changes:
+
+History
+*******
+
+.. include:: ../CHANGES.rst
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..4426074
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,56 @@
+Welcome to keyrings.alt documentation!
+======================================
+
+.. toctree::
+   :maxdepth: 1
+
+   history
+
+
+.. automodule:: keyrings.alt.file
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.Gnome
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.Google
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.keyczar
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.kwallet
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.multi
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.pyfs
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+.. automodule:: keyrings.alt.Windows
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/keyrings.alt.egg-info/PKG-INFO b/keyrings.alt.egg-info/PKG-INFO
new file mode 100644
index 0000000..5ba33cf
--- /dev/null
+++ b/keyrings.alt.egg-info/PKG-INFO
@@ -0,0 +1,20 @@
+Metadata-Version: 1.1
+Name: keyrings.alt
+Version: 1.1
+Summary: Alternate keyring implementations
+Home-page: https://github.com/jaraco/keyrings.alt
+Author: Jason R. Coombs
+Author-email: jaraco at jaraco.com
+License: UNKNOWN
+Description: keyrings.alt
+        ============
+        
+        Alternate keyring backend implementations for use with the
+        `keyring package <https://pypi.python.org/pypi/keyring>`_.
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
diff --git a/keyrings.alt.egg-info/SOURCES.txt b/keyrings.alt.egg-info/SOURCES.txt
new file mode 100644
index 0000000..bf82f11
--- /dev/null
+++ b/keyrings.alt.egg-info/SOURCES.txt
@@ -0,0 +1,39 @@
+.gitignore
+.hgtags
+.travis.yml
+CHANGES.rst
+README.rst
+conftest.py
+pytest.ini
+setup.cfg
+setup.py
+docs/conf.py
+docs/history.rst
+docs/index.rst
+keyrings/__init__.py
+keyrings.alt.egg-info/PKG-INFO
+keyrings.alt.egg-info/SOURCES.txt
+keyrings.alt.egg-info/dependency_links.txt
+keyrings.alt.egg-info/entry_points.txt
+keyrings.alt.egg-info/namespace_packages.txt
+keyrings.alt.egg-info/top_level.txt
+keyrings/alt/Gnome.py
+keyrings/alt/Google.py
+keyrings/alt/Windows.py
+keyrings/alt/__init__.py
+keyrings/alt/_win_crypto.py
+keyrings/alt/file.py
+keyrings/alt/keyczar.py
+keyrings/alt/kwallet.py
+keyrings/alt/multi.py
+keyrings/alt/pyfs.py
+tests/__init__.py
+tests/test_Gnome.py
+tests/test_Google.py
+tests/test_Windows.py
+tests/test_crypto.py
+tests/test_file.py
+tests/test_keyczar.py
+tests/test_kwallet.py
+tests/test_multi.py
+tests/test_pyfs.py
\ No newline at end of file
diff --git a/keyrings.alt.egg-info/dependency_links.txt b/keyrings.alt.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/keyrings.alt.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/keyrings.alt.egg-info/entry_points.txt b/keyrings.alt.egg-info/entry_points.txt
new file mode 100644
index 0000000..cdc6313
--- /dev/null
+++ b/keyrings.alt.egg-info/entry_points.txt
@@ -0,0 +1,10 @@
+[keyring.backends]
+Gnome = keyrings.alt.Gnome
+Google = keyrings.alt.Google
+Windows (alt) = keyrings.alt.Windows
+file = keyrings.alt.file
+keyczar = keyrings.alt.keyczar
+kwallet = keyrings.alt.kwallet
+multi = keyrings.alt.multi
+pyfs = keyrings.alt.pyfs
+
diff --git a/keyrings.alt.egg-info/namespace_packages.txt b/keyrings.alt.egg-info/namespace_packages.txt
new file mode 100644
index 0000000..3814a76
--- /dev/null
+++ b/keyrings.alt.egg-info/namespace_packages.txt
@@ -0,0 +1 @@
+keyrings
diff --git a/keyrings.alt.egg-info/top_level.txt b/keyrings.alt.egg-info/top_level.txt
new file mode 100644
index 0000000..a664fbf
--- /dev/null
+++ b/keyrings.alt.egg-info/top_level.txt
@@ -0,0 +1,2 @@
+keyrings
+tests
diff --git a/keyrings/__init__.py b/keyrings/__init__.py
new file mode 100644
index 0000000..5284146
--- /dev/null
+++ b/keyrings/__init__.py
@@ -0,0 +1 @@
+__import__("pkg_resources").declare_namespace(__name__)
diff --git a/keyrings/alt/Gnome.py b/keyrings/alt/Gnome.py
new file mode 100644
index 0000000..16bca90
--- /dev/null
+++ b/keyrings/alt/Gnome.py
@@ -0,0 +1,108 @@
+try:
+    import gi
+    gi.require_version('GnomeKeyring', '1.0')
+    from gi.repository import GnomeKeyring
+except (ImportError, ValueError):
+    pass
+
+from keyring.backend import KeyringBackend
+from keyring.errors import PasswordSetError, PasswordDeleteError
+from keyring.util import properties
+from keyring.py27compat import unicode_str
+
+
+class Keyring(KeyringBackend):
+    """Gnome Keyring"""
+
+    KEYRING_NAME = None
+    """
+    Name of the keyring in which to store the passwords.
+    Use None for the default keyring.
+    """
+
+    @properties.ClassProperty
+    @classmethod
+    def priority(cls):
+        if 'GnomeKeyring' not in globals():
+            raise RuntimeError("GnomeKeyring module required")
+        result = GnomeKeyring.get_default_keyring_sync()[0]
+        if result != GnomeKeyring.Result.OK:
+            raise RuntimeError(result.value_name)
+        return 1
+
+    @property
+    def keyring_name(self):
+        system_default = GnomeKeyring.get_default_keyring_sync()[1]
+        return self.KEYRING_NAME or system_default
+
+    def _find_passwords(self, service, username, deleting=False):
+        """Get password of the username for the service
+        """
+        passwords = []
+
+        service = self._safe_string(service)
+        username = self._safe_string(username)
+        for attrs_tuple in (('username', 'service'), ('user', 'domain')):
+            attrs = GnomeKeyring.Attribute.list_new()
+            GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[0], username)
+            GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[1], service)
+            result, items = GnomeKeyring.find_items_sync(
+                GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs)
+            if result == GnomeKeyring.Result.OK:
+                passwords += items
+            elif deleting:
+                if result == GnomeKeyring.Result.CANCELLED:
+                    raise PasswordDeleteError("Cancelled by user")
+                elif result != GnomeKeyring.Result.NO_MATCH:
+                    raise PasswordDeleteError(result.value_name)
+        return passwords
+
+    def get_password(self, service, username):
+        """Get password of the username for the service
+        """
+        items = self._find_passwords(service, username)
+        if not items:
+            return None
+
+        secret = items[0].secret
+        return secret if isinstance(secret, unicode_str) else secret.decode('utf-8')
+
+    def set_password(self, service, username, password):
+        """Set password for the username of the service
+        """
+        service = self._safe_string(service)
+        username = self._safe_string(username)
+        password = self._safe_string(password)
+        attrs = GnomeKeyring.Attribute.list_new()
+        GnomeKeyring.Attribute.list_append_string(attrs, 'username', username)
+        GnomeKeyring.Attribute.list_append_string(attrs, 'service', service)
+        GnomeKeyring.Attribute.list_append_string(attrs, 'application', 'python-keyring')
+        result = GnomeKeyring.item_create_sync(
+            self.keyring_name, GnomeKeyring.ItemType.NETWORK_PASSWORD,
+            "Password for '%s' on '%s'" % (username, service),
+            attrs, password, True)[0]
+        if result == GnomeKeyring.Result.CANCELLED:
+            # The user pressed "Cancel" when prompted to unlock their keyring.
+            raise PasswordSetError("Cancelled by user")
+        elif result != GnomeKeyring.Result.OK:
+            raise PasswordSetError(result.value_name)
+
+    def delete_password(self, service, username):
+        """Delete the password for the username of the service.
+        """
+        items = self._find_passwords(service, username, deleting=True)
+        if not items:
+            raise PasswordDeleteError("Password not found")
+        for current in items:
+            result = GnomeKeyring.item_delete_sync(current.keyring,
+                                                   current.item_id)
+            if result == GnomeKeyring.Result.CANCELLED:
+                raise PasswordDeleteError("Cancelled by user")
+            elif result != GnomeKeyring.Result.OK:
+                raise PasswordDeleteError(result.value_name)
+
+    def _safe_string(self, source, encoding='utf-8'):
+        """Convert unicode to string as gnomekeyring barfs on unicode"""
+        if not isinstance(source, str):
+            return source.encode(encoding)
+        return str(source)
diff --git a/keyrings/alt/Google.py b/keyrings/alt/Google.py
new file mode 100644
index 0000000..66affe0
--- /dev/null
+++ b/keyrings/alt/Google.py
@@ -0,0 +1,321 @@
+from __future__ import absolute_import
+
+import os
+import sys
+import copy
+import codecs
+import base64
+import io
+
+try:
+    import gdata.docs.service
+except ImportError:
+    pass
+
+from . import keyczar
+from keyring import errors
+from keyring import credentials
+from keyring.py27compat import input, pickle
+from keyring.backend import KeyringBackend
+from keyring.util import properties
+from keyring.errors import ExceptionRaisedContext
+
+class EnvironCredential(credentials.EnvironCredential):
+    """Retrieve credentials from specifically named environment variables
+    """
+
+    def __init__(self):
+        super(EnvironCredential, self).__init__('GOOGLE_KEYRING_USER',
+            'GOOGLE_KEYRING_PASSWORD')
+
+class DocsKeyring(KeyringBackend):
+    """Backend that stores keyring on Google Docs.
+       Note that login and any other initialisation is deferred until it is
+       actually required to allow this keyring class to be added to the
+       global _all_keyring list.
+    """
+
+    keyring_title = 'GoogleKeyring'
+    # status enums
+    OK = 1
+    FAIL = 0
+    CONFLICT = -1
+
+    def __init__(self, credential, source, crypter,
+                 collection=None, client=None,
+                 can_create=True, input_getter=input
+                ):
+        self.credential = credential
+        self.crypter = crypter
+        self.source = source
+        self._collection = collection
+        self.can_create = can_create
+        self.input_getter = input_getter
+        self._keyring_dict = None
+
+        if not client:
+            self._client = gdata.docs.service.DocsService()
+        else:
+            self._client = client
+
+        self._client.source = source
+        self._client.ssl = True
+        self._login_reqd = True
+
+    @properties.ClassProperty
+    @classmethod
+    def priority(cls):
+        if not cls._has_gdata():
+            raise RuntimeError("Requires gdata")
+        if not keyczar.has_keyczar():
+            raise RuntimeError("Requires keyczar")
+        return 3
+
+    @classmethod
+    def _has_gdata(cls):
+        with ExceptionRaisedContext() as exc:
+            gdata.__name__
+        return not bool(exc)
+
+    def get_password(self, service, username):
+        """Get password of the username for the service
+        """
+        result = self._get_entry(self._keyring, service, username)
+        if result:
+            result = self._decrypt(result)
+        return result
+
+    def set_password(self, service, username, password):
+        """Set password for the username of the service
+        """
+        password = self._encrypt(password or '')
+        keyring_working_copy = copy.deepcopy(self._keyring)
+        service_entries = keyring_working_copy.get(service)
+        if not service_entries:
+            service_entries = {}
+            keyring_working_copy[service] = service_entries
+        service_entries[username] = password
+        save_result = self._save_keyring(keyring_working_copy)
+        if save_result == self.OK:
+            self._keyring_dict = keyring_working_copy
+            return
+        elif save_result == self.CONFLICT:
+            # check if we can avoid updating
+            self.docs_entry, keyring_dict = self._read()
+            existing_pwd = self._get_entry(self._keyring, service, username)
+            conflicting_pwd = self._get_entry(keyring_dict, service, username)
+            if conflicting_pwd == password:
+                # if someone else updated it to the same value then we are done
+                self._keyring_dict = keyring_working_copy
+                return
+            elif conflicting_pwd is None or conflicting_pwd == existing_pwd:
+                # if doesn't already exist or is unchanged then update it
+                new_service_entries = keyring_dict.get(service, {})
+                new_service_entries[username] = password
+                keyring_dict[service] = new_service_entries
+                save_result = self._save_keyring(keyring_dict)
+                if save_result == self.OK:
+                    self._keyring_dict = keyring_dict
+                    return
+                else:
+                    raise errors.PasswordSetError(
+                        'Failed write after conflict detected')
+            else:
+                raise errors.PasswordSetError(
+                    'Conflict detected, service:%s and username:%s was '\
+                    'set to a different value by someone else' %(service,
+                                                                 username))
+
+        raise errors.PasswordSetError('Could not save keyring')
+
+    def delete_password(self, service, username):
+        return self._del_entry(self._keyring, service, username)
+
+    @property
+    def client(self):
+        if not self._client.GetClientLoginToken():
+            try:
+                self._client.ClientLogin(self.credential.username,
+                                         self.credential.password,
+                                         self._client.source)
+            except gdata.service.CaptchaRequired:
+                sys.stdout.write('Please visit ' + self._client.captcha_url)
+                answer = self.input_getter('Answer to the challenge? ')
+                self._client.email = self.credential.username
+                self._client.password = self.credential.password
+                self._client.ClientLogin(
+                    self.credential.username,
+                    self.credential.password,
+                    self._client.source,
+                    captcha_token=self._client.captcha_token,
+                    captcha_response=answer)
+            except gdata.service.BadAuthentication:
+                raise errors.InitError('Users credential were unrecognized')
+            except gdata.service.Error:
+                raise errors.InitError('Login Error')
+
+        return self._client
+
+    @property
+    def collection(self):
+        return self._collection or self.credential.username.split('@')[0]
+
+    @property
+    def _keyring(self):
+        if self._keyring_dict is None:
+            self.docs_entry, self._keyring_dict = self._read()
+        return self._keyring_dict
+
+    def _get_entry(self, keyring_dict, service, username):
+        result = None
+        service_entries = keyring_dict.get(service)
+        if service_entries:
+            result = service_entries.get(username)
+        return result
+
+    def _del_entry(self, keyring_dict, service, username):
+        service_entries = keyring_dict.get(service)
+        if not service_entries:
+            raise errors.PasswordDeleteError("No matching service")
+        try:
+            del service_entries[username]
+        except KeyError:
+            raise errors.PasswordDeleteError("Not found")
+        if not service_entries:
+            del keyring_dict[service]
+
+    def _decrypt(self, value):
+        if not value:
+            return ''
+        return self.crypter.decrypt(value)
+
+    def _encrypt(self, value):
+        if not value:
+            return ''
+        return self.crypter.encrypt(value)
+
+    def _get_doc_title(self):
+        return '%s' %self.keyring_title
+
+    def _read(self):
+        from gdata.docs.service import DocumentQuery
+        title_query = DocumentQuery(categories=[self.collection])
+        title_query['title'] = self._get_doc_title()
+        title_query['title-exact'] = 'true'
+        docs = self.client.QueryDocumentListFeed(title_query.ToUri())
+
+        if not docs.entry:
+            if self.can_create:
+                docs_entry = None
+                keyring_dict = {}
+            else:
+                raise errors.InitError(
+                    '%s not found in %s and create not permitted'
+                    %(self._get_doc_title(), self.collection))
+        else:
+            docs_entry = docs.entry[0]
+            file_contents = ''
+            try:
+                url = docs_entry.content.src
+                url += '&exportFormat=txt'
+                server_response = self.client.request('GET', url)
+                if server_response.status != 200:
+                    raise errors.InitError(
+                        'Could not read existing Google Docs keyring')
+                file_contents = server_response.read()
+                if file_contents.startswith(codecs.BOM_UTF8):
+                    file_contents = file_contents[len(codecs.BOM_UTF8):]
+                keyring_dict = pickle.loads(base64.urlsafe_b64decode(
+                    file_contents.decode('string-escape')))
+            except pickle.UnpicklingError as ex:
+                raise errors.InitError(
+                    'Could not unpickle existing Google Docs keyring', ex)
+            except TypeError as ex:
+                raise errors.InitError(
+                    'Could not decode existing Google Docs keyring', ex)
+
+        return docs_entry, keyring_dict
+
+    def _save_keyring(self, keyring_dict):
+        """Helper to actually write the keyring to Google"""
+        import gdata
+        result = self.OK
+        file_contents = base64.urlsafe_b64encode(pickle.dumps(keyring_dict))
+        try:
+            if self.docs_entry:
+                extra_headers = {'Content-Type': 'text/plain',
+                                 'Content-Length': len(file_contents)}
+                self.docs_entry = self.client.Put(
+                    file_contents,
+                    self.docs_entry.GetEditMediaLink().href,
+                    extra_headers=extra_headers
+                    )
+            else:
+                from gdata.docs.service import DocumentQuery
+                # check for existence of folder, create if required
+                folder_query = DocumentQuery(categories=['folder'])
+                folder_query['title'] = self.collection
+                folder_query['title-exact'] = 'true'
+                docs = self.client.QueryDocumentListFeed(folder_query.ToUri())
+                if docs.entry:
+                    folder_entry = docs.entry[0]
+                else:
+                    folder_entry = self.client.CreateFolder(self.collection)
+                file_handle = io.BytesIO(file_contents)
+                media_source = gdata.MediaSource(
+                    file_handle=file_handle,
+                    content_type='text/plain',
+                    content_length=len(file_contents),
+                    file_name='temp')
+                self.docs_entry = self.client.Upload(
+                    media_source,
+                    self._get_doc_title(),
+                    folder_or_uri=folder_entry
+                )
+        except gdata.service.RequestError as ex:
+            try:
+                if ex.message['reason'].lower().find('conflict') != -1:
+                    result = self.CONFLICT
+                else:
+                    # Google docs has a bug when updating a shared document
+                    # using PUT from any account other that the owner.
+                    # It returns an error 400 "Sorry, there was an error saving the file. Please try again"
+                    # *despite* actually updating the document!
+                    # Workaround by re-reading to see if it actually updated
+                    if ex.message['body'].find(
+                        'Sorry, there was an error saving the file') != -1:
+                        new_docs_entry, new_keyring_dict = self._read()
+                        if new_keyring_dict == keyring_dict:
+                            result = self.OK
+                        else:
+                            result = self.FAIL
+                    else:
+                        result = self.FAIL
+            except:
+                result = self.FAIL
+
+        return result
+
+class KeyczarDocsKeyring(DocsKeyring):
+    """Google Docs keyring using keyczar initialized from environment
+    variables
+    """
+
+    def __init__(self):
+        crypter = keyczar.EnvironCrypter()
+        credential = EnvironCredential()
+        source = os.environ.get('GOOGLE_KEYRING_SOURCE')
+        super(KeyczarDocsKeyring, self).__init__(
+            credential, source, crypter)
+
+    def supported(self):
+        """Return if this keyring supports current environment:
+        -1: not applicable
+         0: suitable
+         1: recommended
+        """
+        try:
+            from keyczar import keyczar
+            return super(KeyczarDocsKeyring, self).supported()
+        except ImportError:
+            return -1
diff --git a/keyrings/alt/Windows.py b/keyrings/alt/Windows.py
new file mode 100644
index 0000000..1b591c0
--- /dev/null
+++ b/keyrings/alt/Windows.py
@@ -0,0 +1,193 @@
+import sys
+import base64
+import platform
+import functools
+
+from keyring.util import properties
+from keyring.backend import KeyringBackend
+from keyring.errors import PasswordDeleteError, ExceptionRaisedContext
+from . import file
+
+try:
+    # prefer pywin32-ctypes
+    from win32ctypes import pywintypes
+    from win32ctypes import win32cred
+    # force demand import to raise ImportError
+    win32cred.__name__
+except ImportError:
+    # fallback to pywin32
+    try:
+        import pywintypes
+        import win32cred
+    except ImportError:
+        pass
+
+try:
+    import winreg
+except ImportError:
+    try:
+        # Python 2 compatibility
+        import _winreg as winreg
+    except ImportError:
+        pass
+
+try:
+    from . import _win_crypto
+except ImportError:
+    pass
+
+def has_pywin32():
+    """
+    Does this environment have pywin32?
+    Should return False even when Mercurial's Demand Import allowed import of
+    win32cred.
+    """
+    with ExceptionRaisedContext() as exc:
+        win32cred.__name__
+    return not bool(exc)
+
+def has_wincrypto():
+    """
+    Does this environment have wincrypto?
+    Should return False even when Mercurial's Demand Import allowed import of
+    _win_crypto, so accesses an attribute of the module.
+    """
+    with ExceptionRaisedContext() as exc:
+        _win_crypto.__name__
+    return not bool(exc)
+
+class EncryptedKeyring(file.BaseKeyring):
+    """
+    A File-based keyring secured by Windows Crypto API.
+    """
+
+    @properties.ClassProperty
+    @classmethod
+    def priority(self):
+        """
+        Preferred over file.EncryptedKeyring but not other, more sophisticated
+        Windows backends.
+        """
+        if not platform.system() == 'Windows':
+            raise RuntimeError("Requires Windows")
+        return .8
+
+    filename = 'wincrypto_pass.cfg'
+
+    def encrypt(self, password):
+        """Encrypt the password using the CryptAPI.
+        """
+        return _win_crypto.encrypt(password)
+
+    def decrypt(self, password_encrypted):
+        """Decrypt the password using the CryptAPI.
+        """
+        return _win_crypto.decrypt(password_encrypted)
+
+
+class RegistryKeyring(KeyringBackend):
+    """
+    RegistryKeyring is a keyring which use Windows CryptAPI to encrypt
+    the user's passwords and store them under registry keys
+    """
+
+    @properties.ClassProperty
+    @classmethod
+    def priority(self):
+        """
+        Preferred on Windows when pywin32 isn't installed
+        """
+        if platform.system() != 'Windows':
+            raise RuntimeError("Requires Windows")
+        if not has_wincrypto():
+            raise RuntimeError("Requires ctypes")
+        return 2
+
+    def get_password(self, service, username):
+        """Get password of the username for the service
+        """
+        try:
+            # fetch the password
+            key = r'Software\%s\Keyring' % service
+            hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key)
+            password_saved = winreg.QueryValueEx(hkey, username)[0]
+            password_base64 = password_saved.encode('ascii')
+            # decode with base64
+            password_encrypted = base64.decodestring(password_base64)
+            # decrypted the password
+            password = _win_crypto.decrypt(password_encrypted).decode('utf-8')
+        except EnvironmentError:
+            password = None
+        return password
+
+    def set_password(self, service, username, password):
+        """Write the password to the registry
+        """
+        # encrypt the password
+        password_encrypted = _win_crypto.encrypt(password.encode('utf-8'))
+        # encode with base64
+        password_base64 = base64.encodestring(password_encrypted)
+        # encode again to unicode
+        password_saved = password_base64.decode('ascii')
+
+        # store the password
+        key_name = r'Software\%s\Keyring' % service
+        hkey = winreg.CreateKey(winreg.HKEY_CURRENT_USER, key_name)
+        winreg.SetValueEx(hkey, username, 0, winreg.REG_SZ, password_saved)
+
+    def delete_password(self, service, username):
+        """Delete the password for the username of the service.
+        """
+        try:
+            key_name = r'Software\%s\Keyring' % service
+            hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
+                winreg.KEY_ALL_ACCESS)
+            winreg.DeleteValue(hkey, username)
+            winreg.CloseKey(hkey)
+        except WindowsError:
+            e = sys.exc_info()[1]
+            raise PasswordDeleteError(e)
+        self._delete_key_if_empty(service)
+
+    def _delete_key_if_empty(self, service):
+        key_name = r'Software\%s\Keyring' % service
+        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
+            winreg.KEY_ALL_ACCESS)
+        try:
+            winreg.EnumValue(key, 0)
+            return
+        except WindowsError:
+            pass
+        winreg.CloseKey(key)
+
+        # it's empty; delete everything
+        while key_name != 'Software':
+            parent, sep, base = key_name.rpartition('\\')
+            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, parent, 0,
+                winreg.KEY_ALL_ACCESS)
+            winreg.DeleteKey(key, base)
+            winreg.CloseKey(key)
+            key_name = parent
+
+
+class OldPywinError(object):
+    """
+    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
diff --git a/keyrings/alt/__init__.py b/keyrings/alt/__init__.py
new file mode 100644
index 0000000..e69de29
... 2004 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/keyrings.alt.git



More information about the Python-modules-commits mailing list