Merge branch 'keyring' into 'master'

Use keyring module to also handle password storage on linux

See merge request gajim/gajim!206
This commit is contained in:
Philipp Hörist 2018-02-06 20:52:44 +01:00
commit 624a2c5cbc
4 changed files with 34 additions and 82 deletions

View File

@ -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,

View File

@ -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')

View File

@ -24,11 +24,7 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
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,17 @@ 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 +86,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 +102,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 +121,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():

View File

@ -280,4 +280,7 @@ setup(
'pyOpenSSL>=0.12',
'pyasn1',
],
extras_require={
'secret_password': ["keyring"]
}
)