Jelmer Vernooij avatar Jelmer Vernooij committed 83c6ef5

Add a SecretServiceKeyring backend.

This uses the secret service D-Bus API, which is provided by newer versions of GNOME and KDE.

http://standards.freedesktop.org/secret-service/

Comments (0)

Files changed (3)

 
 * **OSXKeychain**: supports the Keychain service in Mac OS X.
 * **KDEKWallet**: supports the KDE's Kwallet service.
-* **GnomeKeyring**: for Gnome environment.
+* **GnomeKeyring**: for Gnome 2 environment.
+* **SecretServiceKeyring**: for newer GNOME and KDE environments.
 
 Besides these native password storing services provided by operating systems.
 Python keyring lib also provides following build-in keyrings.

keyring/backend.py

     def abstractmethod(funcobj):
         return funcobj
 
-try:
-    import gnomekeyring
-except ImportError:
-    pass
-
 _KEYRING_SETTING = 'keyring-setting'
 _CRYPTED_PASSWORD = 'crypted-password'
 _BLOCK_SIZE = 32
     def get_password(self, service, username):
         """Get password of the username for the service
         """
+        import gnomekeyring
+
         try:
             items = gnomekeyring.find_network_password_sync(username, service)
         except gnomekeyring.NoMatchError:
     def set_password(self, service, username, password):
         """Set password for the username of the service
         """
+        import gnomekeyring
         try:
             gnomekeyring.set_network_password_sync(None, username, service,
                 None, None, None, None, 0, password)
             # The user pressed "Cancel" when prompted to unlock their keyring.
             raise PasswordSetError("cancelled by user")
 
+
+class SecretServiceKeyring(KeyringBackend):
+    """Secret Service Keyring"""
+
+    def supported(self):
+        try:
+            import dbus
+        except ImportError:
+            return -1
+        try:
+            bus = dbus.SessionBus()
+            bus.get_object('org.freedesktop.secrets',
+                '/org/freedesktop/secrets')
+        except dbus.exceptions.DBusException:
+            return -1
+        else:
+            return 1
+
+    def get_password(self, service, username):
+        """Get password of the username for the service
+        """
+        import dbus
+        bus = dbus.SessionBus()
+        service_obj = self._bus.get_object('org.freedesktop.secrets',
+            '/org/freedesktop/secrets')
+        service = dbus.Interface(service_obj,
+            'org.freedesktop.Secret.Service')
+        unlocked, locked = service.SearchItems(
+            {"user": username, "service": service})
+        session = service.OpenSession("plain", "")
+        locked, prompt = service.Unlock(locked)
+        assert prompt == "/"
+        secrets = service.GetSecrets(unlocked + locked, session)
+        for item_path, secret in secrets.iteritems():
+            return "".join([str(x) for x in secret[2]])
+        return None
+
+    def set_password(self, service, username, password):
+        """Set password for the username of the service
+        """
+        import dbus
+        bus = dbus.SessionBus()
+        service_obj = self._bus.get_object('org.freedesktop.secrets',
+            '/org/freedesktop/secrets')
+        service = dbus.Interface(service_obj,
+            'org.freedesktop.Secret.Service')
+        collection_obj = bus.get_object(
+            'org.freedesktop.secrets',
+            '/org/freedesktop/secrets/aliases/default')
+        collection = dbus.Interface(collection_obj,
+            'org.freedesktop.Secret.Collection')
+        attributes = {
+            "service": service,
+            "username": username
+            }
+        session = service.OpenSession("plain", "")
+        secret = dbus.Struct(
+            (session, "", dbus.ByteArray(password), "text/plain"))
+        properties = {
+            "org.freedesktop.Secret.Item.Label": "%s @ %s" % (
+                username, service),
+            "org.freedesktop.Secret.Item.Attributes": attributes}
+        (item, prompt) = collection.CreateItem(properties, secret,
+            True)
+        assert prompt == "/"
+
+
 kwallet = None
 
 def open_kwallet(kwallet_module=None, qt_module=None):
         _all_keyring = [ OSXKeychain(), GnomeKeyring(), KDEKWallet(),
                          CryptedFileKeyring(), UncryptedFileKeyring(),
                          Win32CryptoKeyring(), Win32CryptoRegistry(),
-                         WinVaultKeyring()]
+                         WinVaultKeyring(), SecretServiceKeyring() ]
     return _all_keyring
 

keyring/tests/test_backend.py

             pass
         return -1
 
+class SecretServiceKeyringTestCase(BackendBasicTestCase):
+    __test__ = True
+
+    def environ(self):
+        return dict(DISPLAY='1',
+                    DBUS_SESSION_BUS_ADDRESS='1')
+
+    def init_keyring(self):
+        print >> sys.stderr, "Testing SecretServiceKeyring, following password prompts are for this keyring"
+        return keyring.backend.SecretServiceKeyring()
+
+    def test_supported_no_module(self):
+        with ImportKiller('dbus'):
+            with Environ(**self.environ()):
+                self.assertEqual(-1, self.keyring.supported())
+
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(OSXKeychainTestCase))
     suite.addTest(unittest.makeSuite(GnomeKeyringTestCase))
+    suite.addTest(unittest.makeSuite(SecretServiceKeyringTestCase))
     suite.addTest(unittest.makeSuite(KDEWalletCanceledTestCase))
     suite.addTest(unittest.makeSuite(KDEKWalletTestCase))
     suite.addTest(unittest.makeSuite(KDEKWalletInQApplication))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.