diff options
Diffstat (limited to 'src/gui/ConfBackend.py')
-rw-r--r-- | src/gui/ConfBackend.py | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/gui/ConfBackend.py b/src/gui/ConfBackend.py new file mode 100644 index 00000000..d3def183 --- /dev/null +++ b/src/gui/ConfBackend.py @@ -0,0 +1,246 @@ +from abrt_utils import _, log, log1, log2 + +# Doc on Gnome keyring API: +# http://library.gnome.org/devel/gnome-keyring/stable/ +# Python bindings are in gnome-python2-desktop package + +try: + import gnomekeyring as gkey +except ImportError, e: + gkey = None + +# Exceptions +class ConfBackendInitError(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.what = msg + + def __str__(self): + return self.what + +class ConfBackendSaveError(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.what = msg + + def __str__(self): + return self.what + +class ConfBackendLoadError(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.what = msg + + def __str__(self): + return self.what + + +class ConfBackend(object): + def __init__(self): + pass + + def save(self, name, settings): + """ Default save method has to be implemented in derived class """ + raise NotImplementedError + + def load(self, name): + """ Default load method has to be implemented in derived class """ + raise NotImplementedError + + +# We use Gnome keyring in the following way: +# we store passwords for each plugin in a key named "abrt:<plugin_name>". +# The value of the key becomes the value of "Password" setting. +# Other settings (if plugin has them) are stored as attributes of this key. +# +# Example: Key "abrt:Bugzilla" with bugzilla password as value, and with attributes: +# +# Application: abrt +# AbrtPluginInfo: Bugzilla +# NoSSLVerify: yes +# Login: user@host.com +# BugzillaURL: https://host.with.bz.com/ +# +# Attributes "Application" and "AbrtPluginInfo" are special, they are used +# for efficient key retrieval via keyring API find_items_sync() function. + +g_default_key_ring = None + +class ConfBackendGnomeKeyring(ConfBackend): + def __init__(self): + global g_default_key_ring + + ConfBackend.__init__(self) + if g_default_key_ring: + return + if not gkey or not gkey.is_available(): + raise ConfBackendInitError(_("Cannot connect to the Gnome Keyring daemon.")) + try: + g_default_key_ring = gkey.get_default_keyring_sync() + except: + # could happen if keyring daemon is running, but we run gui under + # user who is not the owner of the running session - using su + raise ConfBackendInitError(_("Cannot get the default keyring.")) + + def save(self, name, settings): + settings_tmp = settings.copy() + settings_tmp["Application"] = "abrt" + settings_tmp["AbrtPluginInfo"] = name + + # delete all keyring items containg "AbrtPluginInfo":"<plugin_name>", + # so we always have only 1 item per plugin + try: + item_list = gkey.find_items_sync(gkey.ITEM_GENERIC_SECRET, { "AbrtPluginInfo": str(name) }) + for item in item_list: + log2("found old keyring item: ring:'%s' item_id:%s attrs:%s", item.keyring, item.item_id, str(item.attributes)) + log2("deleting it from keyring '%s'", g_default_key_ring) + gkey.item_delete_sync(g_default_key_ring, item.item_id) + except gkey.NoMatchError: + # nothing found + pass + except gkey.DeniedError: + raise ConfBackendSaveError(_("Access to gnome-keyring has been denied, plugins settings will not be saved.")) + # if plugin has a "Password" setting, we handle it specially: in keyring, + # it is stored as item.secret, not as one of attributes + password = "" + if "Password" in settings_tmp: + password = settings_tmp["Password"] + del settings_tmp["Password"] + # store new settings for this plugin as one keyring item + try: + gkey.item_create_sync(g_default_key_ring, + gkey.ITEM_GENERIC_SECRET, + "abrt:%s" % name, # display_name + settings_tmp, # attrs + password, # secret + True) + except gkey.DeniedError: + raise ConfBackendSaveError(_("Access to gnome-keyring has been denied, plugins settings will not be saved.")) + + def load(self, name): + item_list = None + #FIXME: make this configurable + # this actually makes GUI to ask twice per every plugin + # which have it's settings stored in keyring + attempts = 2 + while attempts: + try: + log2("looking for keyring items with 'AbrtPluginInfo:%s' attr", str(name)) + item_list = gkey.find_items_sync(gkey.ITEM_GENERIC_SECRET, {"AbrtPluginInfo":str(name)}) + for item in item_list: + # gnome keyring is weeeeird. why display_name, type, mtime, ctime + # aren't available in find_items_sync() results? why we need to + # get them via additional call, item_get_info_sync()? + # internally, item has GNOME_KEYRING_TYPE_FOUND type, + # and info has GNOME_KEYRING_TYPE_ITEM_INFO type. + # why not use the same type for both? + # + # and worst of all, this information took four hours of googling... + # + #info = gkey.item_get_info_sync(item.keyring, item.item_id) + log2("found keyring item: ring:'%s' item_id:%s attrs:%s", # "secret:'%s' display_name:'%s'" + item.keyring, item.item_id, str(item.attributes) #, item.secret, info.get_display_name() + ) + except gkey.NoMatchError: + # nothing found + pass + except gkey.DeniedError: + attempts -= 1 + log2("gk-authorization has failed %i time(s)", 2-attempts) + if attempts == 0: + # we tried 2 times, so giving up the authorization + raise ConfBackendLoadError(_("Access to gnome-keyring has been denied, cannot load the settings for %s!" % name)) + continue + break + + if item_list: + retval = item_list[0].attributes.copy() + retval["Password"] = item_list[0].secret + return retval + return {} + + # This routine loads setting for all plugins. It doesn't need plugin name. + # Thus we can avoid talking to abrtd just in order to get plugin names. + def load_all(self): + retval = {} + item_list = {} + + # UGLY compat cludge for users who has saved items without "Application" attr + # (abrt <= 1.0.3 was saving those) + item_ids = gkey.list_item_ids_sync(g_default_key_ring) + log2("all keyring item ids:%s", item_ids) + for item_id in item_ids: + info = gkey.item_get_info_sync(g_default_key_ring, item_id) + attrs = gkey.item_get_attributes_sync(g_default_key_ring, item_id) + log2("keyring item %s: attrs:%s", item_id, str(attrs)) + if "AbrtPluginInfo" in attrs: + if not "Application" in attrs: + log2("updating old-style keyring item") + attrs["Application"] = "abrt" + try: + gkey.item_set_attributes_sync(g_default_key_ring, item_id, attrs) + except: + log2("error updating old-style keyring item") + plugin_name = attrs["AbrtPluginInfo"] + # If plugin has a "Password" setting, we handle it specially: in keyring, + # it is stored as item.secret, not as one of attributes + if info.get_secret(): + attrs["Password"] = info.get_secret() + # avoiding sending useless duplicate info over dbus... + del attrs["AbrtPluginInfo"] + try: + del attrs["Application"] + except: + pass + retval[plugin_name] = attrs; + # end of UGLY compat cludge + + try: + log2("looking for keyring items with 'Application:abrt' attr") + item_list = gkey.find_items_sync(gkey.ITEM_GENERIC_SECRET, { "Application": "abrt" }) + except gkey.NoMatchError: + # nothing found + pass + except gkey.DeniedError: + raise ConfBackendLoadError(_("Access to gnome-keyring has been denied, cannot load settings.")) + + for item in item_list: + # gnome keyring is weeeeird. why display_name, type, mtime, ctime + # aren't available in find_items_sync() results? why we need to + # get them via additional call, item_get_info_sync()? + # internally, item has GNOME_KEYRING_TYPE_FOUND type, + # and info has GNOME_KEYRING_TYPE_ITEM_INFO type. + # why not use the same type for both? + # + # and worst of all, this information took four hours of googling... + # + #info = gkey.item_get_info_sync(item.keyring, item.item_id) + log2("found keyring item: ring:%s item_id:%s attrs:%s", # "secret:%s display_name:'%s'" + item.keyring, item.item_id, str(item.attributes) #, item.secret, info.get_display_name() + ) + attrs = item.attributes.copy() + if "AbrtPluginInfo" in attrs: + plugin_name = attrs["AbrtPluginInfo"] + # If plugin has a "Password" setting, we handle it specially: in keyring, + # it is stored as item.secret, not as one of attributes + if item.secret: + attrs["Password"] = item.secret + # avoiding sending useless duplicate info over dbus... + del attrs["AbrtPluginInfo"] + try: + del attrs["Application"] + except: + pass + retval[plugin_name] = attrs + return retval + + +# Rudimentary backend factory + +currentConfBackend = None + +def getCurrentConfBackend(): + global currentConfBackend + if not currentConfBackend: + currentConfBackend = ConfBackendGnomeKeyring() + return currentConfBackend |