Mathias Panzenböck avatar Mathias Panzenböck committed 785e21c

reimplemented kwallet support via dbus

Comments (0)

Files changed (2)

keyring/backend.py

             if not kwallet.hasFolder('Python'):
                 kwallet.createFolder('Python')
             kwallet.setFolder('Python')
+        return kwallet
     finally:
         app.exit()
 
 
 kwallet = None
+
+try:
+    import dbus
+except ImportError:
+    has_dbus = False
+else:
+    has_dbus = True
+
 try:
     from PyKDE4.kdeui import KWallet
     from PyQt4 import QtCore, QtGui
 except ImportError:
-    kwallet = None
+    has_pykde = False
 else:
-    kwallet = open_kwallet()
+    has_pykde = True
 
 
-class KDEKWallet(KeyringBackend):
-    """KDE KWallet"""
+if not has_dbus:
+    if has_pykde:
+        kwallet = open_kwallet()
 
-    def supported(self):
-        if kwallet is None:
-            return -1
-        else:
-            return 1
+    class KDEKWallet(KeyringBackend):
+        """KDE KWallet"""
 
-    def get_password(self, service, username):
-        """Get password of the username for the service
+        def supported(self):
+            if kwallet is None:
+                return -1
+            else:
+                return 1
+
+        def get_password(self, service, username):
+            """Get password of the username for the service
+            """
+            key = username + '@' + service
+            network = KWallet.Wallet.NetworkWallet()
+            if kwallet.keyDoesNotExist(network, 'Python', key):
+                return None
+
+            result = kwallet.readPassword(key)[1]
+            # The string will be a PyQt4.QtCore.QString, so turn it into a unicode
+            # object.
+            return unicode(result)
+
+        def set_password(self, service, username, password):
+            """Set password for the username of the service
+            """
+            kwallet.writePassword(username+'@'+service, password)
+else:
+    class KDEKWallet(KeyringBackend):
+        """KDE KWallet via Python DBus.
+        
+        This does not need the KDE Python bindings and thus no QApplication
+        instance. Instead it uses the Python DBus bindings. In addition using
+        this let's KWallet show the service name as the app-id in the popup
+        dialog (instead of just showing 'Python').
         """
-        key = username + '@' + service
-        network = KWallet.Wallet.NetworkWallet()
-        if kwallet.keyDoesNotExist(network, 'Python', key):
-            return None
 
-        result = kwallet.readPassword(key)[1]
-        # The string will be a PyQt4.QtCore.QString, so turn it into a unicode
-        # object.
-        return unicode(result)
+        def __init__(self):
+            self._handle = None
 
-    def set_password(self, service, username, password):
-        """Set password for the username of the service
-        """
-        kwallet.writePassword(username+'@'+service, password)
+        def supported(self):
+            if has_dbus:
+                try:
+                    kwallet = self._get_kwallet()
+                    if not kwallet.isEnabled():
+                        return -1
+                except dbus.DBusException:
+                    return -1
+                return 1
+            else:
+                return -1
+
+        def _get_winid(self):
+            return 0
+    
+        def _get_kwallet(self):
+            # Don't cache dbus reference because else the wallet isn't
+            # reopened when it was force-closed by someone else. Instead
+            # readPassword just returns an empty string.
+            # PyKDE4.kdeui.KWallet seem to suffer from the same behaviour.
+            bus     = dbus.SessionBus()
+            proxy   = bus.get_object('org.kde.kwalletd','/modules/kwalletd')
+            kwallet = dbus.Interface(proxy,'org.kde.KWallet')
+            return kwallet
+
+        def _get_slot(self, service, username):
+            key     = username + "@" + service
+            kwallet = self._get_kwallet()
+
+            if self._handle is not None:
+                if kwallet.isOpen(self._handle):
+                    return kwallet, self._handle, key
+                self._handle = None
+
+            wallet_name = kwallet.networkWallet()
+
+            if kwallet.keyDoesNotExist(wallet_name, 'Python', key):
+                return kwallet, None, key
+
+            handle = kwallet.open(wallet_name, self._get_winid(), service)
+    
+            if handle != -1:
+                self._handle = handle
+
+            return kwallet, handle, key
+
+        def _get_or_create_slot(self, service, username):
+            kwallet, handle, key = self._get_slot(service, username)
+
+            if handle is None:
+                wallet_name = kwallet.networkWallet()
+                handle = kwallet.open(wallet_name, self._get_winid(), service)
+
+            if handle == -1:
+                raise PasswordSetError("Can't access the wallet from the system")
+
+            if not kwallet.hasFolder(handle, 'Python', service):
+                if not kwallet.createFolder(handle, 'Python', service):
+                    raise PasswordSetError("Can't create wallet folder for password storage")
+        
+            self._handle = handle
+            return kwallet, handle, key
+
+        def close(self, service='Python', force=False):
+            if self._handle is not None:
+                self._get_kwallet().close(self._handle, force, service)
+                self._handle = None
+
+        def del_password(self, service, username):
+            """Delete password for the username of the service
+            """
+            kwallet, handle, key = self._get_slot(service, username)
+
+            if handle is None:
+                return None
+
+            if handle == -1:
+                raise PasswordSetError("Can't access the wallet from the system")
+
+            if kwallet.removeEntry(handle, 'Python', key, service) != 0:
+                raise PasswordSetError("Can't delete the password from the system")
+
+        def get_password(self, service, username):
+            """Get password of the username for the service
+            """
+            kwallet, handle, key = self._get_slot(service, username)
+        
+            if handle is None:
+                return None
+        
+            if handle == -1:
+                raise OSError("Can't access the wallet from the system")
+        
+            # a dbus.String is already a sub-class of unicode:
+            return kwallet.readPassword(handle, 'Python', key, service)
+    
+        def set_password(self, service, username, password):
+            """Set password for the username of the service
+            """
+            kwallet, handle, key = self._get_or_create_slot(service, username)
+            if kwallet.writePassword(handle, 'Python', key, password, service) != 0:
+                raise PasswordSetError("Can't write the password to the system")
 
 class BasicFileKeyring(KeyringBackend):
     """BasicFileKeyring is a filebased implementation of keyring.
                 return module
 
             try:
-                # avoid import the imported modules
-                module = sys.modules[keyring_name[:keyring_name.rfind('.')]]
+                # avoid import the imported modules				
+                module_name = keyring_name.rsplit('.',1)[0]
+                if module_name == 'keyring.backend':
+                    # The keyring.backend module is only semi loaded at
+                    # this stage and so we have to handle it separately.
+                    # Otherwise it would be executed twice and the
+                    # isinstance call in set_keyring fails.
+                    module = backend
+                else:
+                    module = sys.modules[module_name]
             except KeyError:
-                module = load_module( keyring_name, sys.path+[keyring_path])
+                module = load_module(keyring_name, sys.path+[keyring_path])
 
             keyring_class = keyring_name.split('.')[-1].strip()
             exec  "keyring_temp = module." + keyring_class + "() " in locals()
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.