Commits

rl_0x0  committed 04a7463

Fix issue with gnomekeyring when unicode passed as service, user or password. Add unicode tests. Tweak SecretService code to pass unicode to dbus as it is expected

  • Participants
  • Parent commits 5038d4d

Comments (0)

Files changed (2)

File keyring/backend.py

         """
         import gnomekeyring
 
+        service = self._safe_string(service)
+        username = self._safe_string(username)
         try:
             items = gnomekeyring.find_network_password_sync(username, service)
         except gnomekeyring.NoMatchError:
         """Set password for the username of the service
         """
         import gnomekeyring
+
+        service = self._safe_string(service)
+        username = self._safe_string(username)
+        password = self._safe_string(password)
         try:
             gnomekeyring.item_create_sync(
                 self.KEYRING_NAME, gnomekeyring.ITEM_NETWORK_PASSWORD,
             # The user pressed "Cancel" when prompted to unlock their keyring.
             raise PasswordSetError("cancelled by user")
 
+    def _safe_string(self, source, encoding='utf-8'):
+	"""Convert unicode to string as gnomekeyring barfs on unicode"""
+        if isinstance(source, unicode):
+            return source.encode(encoding)
+        return str(source)
 
 class SecretServiceKeyring(KeyringBackend):
     """Secret Service Keyring"""
         else:
             return 1
 
+    def _str_to_dbus_str(self, s, strict=False):
+        """Given a string, do our best to turn it into a unicode compatible
+        object.
+        """
+        if issubclass(s.__class__, unicode):
+            # It's already unicode, no problem.
+            return s
+        
+        # It's not unicode.  Convert it to a unicode string.
+        try:
+            return unicode(s)
+        except UnicodeEncodeError:
+            logger.exception("Failed to convert '%s' to unicode" % s)
+            if strict:
+                raise
+            else:
+                return unicode(s, errors='replace')
+
     def get_password(self, service, username):
         """Get password of the username for the service
         """
         import dbus
+        service = self._str_to_dbus_str(service)
+        username = self._str_to_dbus_str(username)
         bus = dbus.SessionBus()
         service_obj = bus.get_object('org.freedesktop.secrets',
             '/org/freedesktop/secrets')
         _, session = service_iface.OpenSession("plain", "")
         no_longer_locked, prompt = service_iface.Unlock(locked)
         assert prompt == "/"
-        secrets = service_iface.GetSecrets(unlocked + locked, session)
+        secrets = service_iface.GetSecrets(unlocked + locked, session, 
+	                                   byte_arrays=True)
         for item_path, secret in secrets.iteritems():
-            return "".join([str(x) for x in secret[2]])
+	    return unicode(secret[2])
         return None
 
     def set_password(self, service, username, password):
         """Set password for the username of the service
         """
         import dbus
+        service = self._str_to_dbus_str(service)
+        username = self._str_to_dbus_str(username)
+        password = self._str_to_dbus_str(password)
         bus = dbus.SessionBus()
         service_obj = bus.get_object('org.freedesktop.secrets',
             '/org/freedesktop/secrets')
             }
         _, session = service_iface.OpenSession("plain", "")
 
-        if isinstance(password, unicode):
-            password = password.encode('utf-8')
         secret = dbus.Struct(
-            (session, "", dbus.ByteArray(password), "text/plain"))
+            (session, "", dbus.ByteArray(password), "application/octet-stream"))
         properties = {
             "org.freedesktop.Secret.Item.Label": "%s @ %s" % (
                 username, service),

File keyring/tests/test_backend.py

+# -*- coding: utf-8 -*-
 """
 test_backend.py
 
 
 ALPHABET = string.ascii_letters + string.digits
 DIFFICULT_CHARS = string.whitespace + string.punctuation
+UNICODE_CHARS = u"""κόσμεНа берегу пустынных волнSîne klâwen durh die wolken sint
+geslagen, er stîget ûf mit grôzer kraft"""
 
 class ImportKiller(object):
     "Context manager to make an import of a given name or names fail."
         service = random_string(20, DIFFICULT_CHARS)
         self.check_set_get(service, username, password)
 
+    def test_unicode_chars(self):
+        password = random_string(20, UNICODE_CHARS)
+        username = random_string(20, UNICODE_CHARS)
+        service = random_string(20, UNICODE_CHARS)
+        self.check_set_get(service, username, password)
+
     def test_different_user(self):
         """
         Issue #47 reports that WinVault isn't storing passwords for
             with Environ(**environ):
                 self.assertEqual(0, self.keyring.supported())
 
-
 @unittest.skipUnless(is_kwallet_supported(),
                      "Need KWallet")
 class KDEKWalletTestCase(BackendBasicTests, unittest.TestCase):