Commit 624a2c5c authored by Philipp Hörist's avatar Philipp Hörist

Merge branch 'keyring' into 'master'

Use keyring module to also handle password storage on linux

See merge request gajim/gajim!206
parents 1e830475 f1e95825
import subprocess
__version__ = "0.98.2"
__version__ = "0.98.3"
try:
node = subprocess.Popen('git rev-parse --short=12 HEAD', shell=True,
......
......@@ -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')
......@@ -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():
......
......@@ -280,4 +280,7 @@ setup(
'pyOpenSSL>=0.12',
'pyasn1',
],
extras_require={
'secret_password': ["keyring"]
}
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment