From d0f4ebd0bc4a3f0f88bd93d88c5bfa748562fb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sat, 3 Feb 2018 20:32:37 +0100 Subject: [PATCH 1/3] Use keyring module to handle password storage --- gajim/common/passwords.py | 99 ++++++++------------------------------- setup.py | 3 ++ 2 files changed, 22 insertions(+), 80 deletions(-) diff --git a/gajim/common/passwords.py b/gajim/common/passwords.py index bdfb18891..737a3dc66 100644 --- a/gajim/common/passwords.py +++ b/gajim/common/passwords.py @@ -24,11 +24,7 @@ ## along with Gajim. If not, see . ## -import os import logging -import gi - -from gi.repository import GLib from gajim.common import app @@ -37,11 +33,10 @@ __all__ = ['get_password', 'save_password'] log = logging.getLogger('gajim.password') keyring = None -if os.name == 'nt': - try: - import keyring - except ImportError: - log.debug('python-keyring missing, falling back to plaintext storage') +try: + import keyring +except ImportError: + log.debug('python-keyring missing, falling back to plaintext storage') class PasswordStorage(object): @@ -54,54 +49,16 @@ class PasswordStorage(object): raise NotImplementedError -class LibSecretPasswordStorage(PasswordStorage): - """Store password using libsecret""" - identifier = 'libsecret:' - def __init__(self): - gi.require_version('Secret', '1') - gir = __import__('gi.repository', globals(), locals(), ['Secret'], 0) - self.Secret = gir.Secret - self.GAJIM_SCHEMA = self.Secret.Schema.new( - "org.gnome.keyring.NetworkPassword", - self.Secret.SchemaFlags.NONE, - { - 'user': self.Secret.SchemaAttributeType.STRING, - 'server': self.Secret.SchemaAttributeType.STRING, - 'protocol': self.Secret.SchemaAttributeType.STRING, - } - ) - - def get_password(self, account_name): - server = app.config.get_per('accounts', account_name, 'hostname') - user = app.config.get_per('accounts', account_name, 'name') - password = self.Secret.password_lookup_sync(self.GAJIM_SCHEMA, - {'user': user, 'server': server, 'protocol': 'xmpp'}, None) - return password - - def save_password(self, account_name, password, update=True): - server = app.config.get_per('accounts', account_name, 'hostname') - user = app.config.get_per('accounts', account_name, 'name') - display_name = _('XMPP account %s') % user + '@' + server - attributes = {'user': user, 'server': server, 'protocol': 'xmpp'} - try: - return self.Secret.password_store_sync( - self.GAJIM_SCHEMA, attributes, self.Secret.COLLECTION_DEFAULT, - display_name, password or '', None) - except GLib.Error as error: - log.error(error) - return False - - -class SecretWindowsPasswordStorage(PasswordStorage): - """ Windows Keyring """ - identifier = 'winvault:' +class SecretPasswordStorage(PasswordStorage): + """ Store password using Keyring """ + identifier = 'keyring:' def __init__(self): - self.win_keyring = keyring.get_keyring() + self.keyring = keyring.get_keyring() def save_password(self, account_name, password): try: - self.win_keyring.set_password('gajim', account_name, password) + self.keyring.set_password('gajim', account_name, password) return True except: log.exception('error:') @@ -109,19 +66,18 @@ class SecretWindowsPasswordStorage(PasswordStorage): def get_password(self, account_name): log.debug('getting password') - return self.win_keyring.get_password('gajim', account_name) + return self.keyring.get_password('gajim', account_name) class PasswordStorageManager(PasswordStorage): """Access all the implemented password storage backends, knowing which ones are available and which we prefer to use. - Also implements storing directly in gajim config (former - SimplePasswordStorage class).""" + Also implements storing directly in gajim config.""" def __init__(self): self.preferred_backend = None self.libsecret = None - self.winsecret = None + self.secret = None self.connect_backends() self.set_preferred_backend() @@ -131,25 +87,15 @@ class PasswordStorageManager(PasswordStorage): """ # TODO: handle disappearing backends - if app.config.get('use_keyring'): - if os.name == 'nt' and keyring: - self.winsecret = SecretWindowsPasswordStorage() - else: - try: - self.libsecret = LibSecretPasswordStorage() - except (ValueError, AttributeError) as e: - log.debug("Could not connect to libsecret: %s" % e) + if app.config.get('use_keyring') and keyring: + self.secret = SecretPasswordStorage() def get_password(self, account_name): pw = app.config.get_per('accounts', account_name, 'password') if not pw: return pw - if pw.startswith(LibSecretPasswordStorage.identifier) and \ - self.libsecret: - backend = self.libsecret - elif pw.startswith(SecretWindowsPasswordStorage.identifier) and \ - self.winsecret: - backend = self.winsecret + if pw.startswith(SecretPasswordStorage.identifier) and self.secret: + backend = self.secret else: backend = None @@ -157,7 +103,7 @@ class PasswordStorageManager(PasswordStorage): pw = backend.get_password(account_name) if backend != self.preferred_backend: # migrate password to preferred_backend - self.preferred_backend.save_password(account_name, pw) + self.save_password(account_name, pw) # TODO: remove from old backend return pw @@ -176,18 +122,11 @@ class PasswordStorageManager(PasswordStorage): return True def set_preferred_backend(self): - if self.libsecret: - self.preferred_backend = self.libsecret - elif self.winsecret: - self.preferred_backend = self.winsecret + if self.secret: + self.preferred_backend = self.secret else: self.preferred_backend = None - def has_keyring(self): - """Is there a real password storage backend? Else, passwords are stored - plain in gajim config""" - return bool(self.preferred_backend) - passwordStorageManager = None def get_storage(): diff --git a/setup.py b/setup.py index 9d3964a60..f02cf7d5e 100644 --- a/setup.py +++ b/setup.py @@ -280,4 +280,7 @@ setup( 'pyOpenSSL>=0.12', 'pyasn1', ], + extras_require={ + 'secret_password': ["keyring"] + } ) From 0d37609d8a8a16383369545a65dbb8e1187555df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sat, 3 Feb 2018 22:13:43 +0100 Subject: [PATCH 2/3] PasswordStorageManager: remove unused member --- gajim/common/passwords.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gajim/common/passwords.py b/gajim/common/passwords.py index 737a3dc66..868a41546 100644 --- a/gajim/common/passwords.py +++ b/gajim/common/passwords.py @@ -76,7 +76,6 @@ class PasswordStorageManager(PasswordStorage): def __init__(self): self.preferred_backend = None - self.libsecret = None self.secret = None self.connect_backends() From f1e95825c69656239c2ab48fbb0b082f5820b246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Apitzsch?= Date: Sun, 4 Feb 2018 17:02:02 +0100 Subject: [PATCH 3/3] Migrate config for new password identifier + version bump --- gajim/__init__.py | 2 +- gajim/common/optparser.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/gajim/__init__.py b/gajim/__init__.py index d818158b2..870664a75 100644 --- a/gajim/__init__.py +++ b/gajim/__init__.py @@ -1,6 +1,6 @@ import subprocess -__version__ = "0.98.2" +__version__ = "0.98.3" try: node = subprocess.Popen('git rev-parse --short=12 HEAD', shell=True, diff --git a/gajim/common/optparser.py b/gajim/common/optparser.py index c653247a0..179e30756 100644 --- a/gajim/common/optparser.py +++ b/gajim/common/optparser.py @@ -249,6 +249,8 @@ class OptionsParser: self.update_config_to_016112() if old < [0, 98, 2] and new >= [0, 98, 2]: self.update_config_to_0982() + if old < [0, 98, 3] and new >= [0, 98, 3]: + self.update_config_to_0983() app.logger.init_vars() app.logger.attach_cache_database() @@ -924,3 +926,12 @@ class OptionsParser: ''' ) app.config.set('version', '0.98.2') + + def update_config_to_0983(self): + for account in self.old_values['accounts'].keys(): + password = self.old_values['accounts'][account]['password'] + if password == "winvault:": + app.config.set_per('accounts', account, 'password', 'keyring:') + elif password == "libsecret:": + app.config.set_per('accounts', account, 'password', '') + app.config.set('version', '0.98.3')