Mind Bending

Gnome Keyring

Following my last post lines, I’ll show today how we can implement a wrapper class to "control" the Gnome Keyring. This isn’t the most secure way, since we store the keyring password in a variable. But is better then let your keyring open for all other applications.

First we need to create a class that creates a "Keyring Manager", responsible for:

  • consulting the existing keyrings;
  • create keyrings;
  • delete keyrings;
  • set the default keyring;
  • lock all keyrings.

Secondly, we need a wrapper for the Keyring. Since the keyring has many features and is not my intention to wrap all those I’ll just show the essentials. My keyring wrapper is able to:

  • lock the keyring;
  • unlock the keyring;
  • show if the keyring is locked
  • list it’s item ids;
  • give access to the GnomeKeyringInfo;
  • change the keyring password;
  • list the keyring’s item attributes;
  • show the keyring’s item secret.

In my code, I’ve used some decorators to ensure that some informations will be updated before/after some task or to lock/unlock the keyring. This is my code:

import gnomekeyring as gk
import glib

APP_NAME = 'MyApp'

glib.set_application_name(APP_NAME)

if not gk.is_available():
    print 'Gnome Keyring is unavailable!'
    exit()

class KeyringManager:
    def __init__(self):
        self.default_keyring = gk.get_default_keyring_sync()
        self.keyrings = gk.list_keyring_names_sync()

    def __update_keyring_names(when):
        def func(f):
            def func_params(self, *args, **kwargs):
                if when == 'before':
                    self.keyrings = gk.list_keyring_names_sync()

                ret = f(self, *args, **kwargs)

                if when == 'after':
                    self.keyrings = gk.list_keyring_names_sync()

                return ret
            return func_params
        return func

    @__update_keyring_names('after')
    def create_keyring(self, name, password):
        gk.create_sync(name, password)

    @__update_keyring_names('after')
    def delete_keyring(self, name):
        gk.delete_sync(name)

    def lock_all_keyrings(self):
        gk.lock_all_sync()

    def set_default_keyring(self, name):
        gk.set_default_keyring_sync(name)
        self.default_keyring = gk.get_default_keyring_sync()

    @__update_keyring_names('before')
    def __contains__(self, name):
        return name in self.keyrings

    @__update_keyring_names('before')
    def __getitem__(self, name):
        if name in self:
            return Keyring(name)
        return None

class Keyring:
    def __init__(self, name):
        self.name = name
        self.info = gk.get_info_sync(name)
        self.passwd = None
        if not self.info.get_is_locked():
            self.lock()

    def lock(self):
        gk.lock_sync(self.name)

    def unlock(self, passwd):
        gk.unlock_sync(self.name, passwd)
        self.passwd = passwd

    def __lock_unlock_dec(func):
        def decorator(self, *args, **kwargs):
            self.unlock(self.passwd)
            ret = func(self, *args, **kwargs)
            self.lock()
            return ret
        return decorator

    @__lock_unlock_dec
    def list_items_id(self):
        self.items_id = gk.list_item_ids_sync(self.name)
        return self.items_id

    def is_locked(self):
        self.update_info()
        return self.info.get_is_locked()

    def update_info(self):
        self.info = gk.get_info_sync(self.name)

    def change_password(self, old_passwd, new_passwd):
        gk.change_password_sync(self.name, old_passwd, new_passwd)

    @__lock_unlock_dec
    def __getitem__(self, count):
        item_id = gk.list_item_ids_sync(self.name)[count]
        attr = gk.item_get_attributes_sync(self.name, item_id)
        attr['id'] = item_id
        return attr

    @__lock_unlock_dec
    def get_item_secret(self, item_id):
        item_info = gk.item_get_info_sync(self.name, item_id)
        return item_info.get_secret()

if __name__ == '__main__':
    km = KeyringManager()
    print km.keyrings
    mykey = km['MyKeyring']
    mykey.passwd = 'mypasswd'
    print mykey.list_items_id()

    print '#'*10

    for item in mykey:
        print item

    print '#'*10

    print mykey[0]
    print 'item', mykey[0]['id'], 'secret:', mykey.get_item_secret(mykey[0]['id'])

I’ve saved this code as KeyringWrapper.py, it’s execution gives me the following output:

$ python KeyringWrapper.py
['Beholder', 'login', 'MyKeyring', 'session']
[1L, 2L, 3L]
##########
{'username': 'magnun', 'protocol': 'ssh', 'port': '22', 'application': 'MyApp', 'server': 'Neptune', 'id': 1L}
{'username': 'guest', 'protocol': 'ssh', 'port': '22', 'application': 'MyApp', 'server': 'Neptune', 'id': 2L}
{'username': 'magnun', 'protocol': 'ssh', 'port': '22', 'application': 'MyApp', 'server': 'Jupiter', 'id': 3L}
##########
{'username': 'magnun', 'protocol': 'ssh', 'port': '22', 'application': 'MyApp', 'server': 'Neptune', 'id': 1L}
item 1 secret: mypasswd

This code is just some basis. It’s necessary to expand it to wrap essential writing functions like item_set_attributes, set_info_sync, item_create and others. But as I said, this isn’t the most secure way, so I don’t think that write those wappers don’t worth the spent time.

Magnun

Magnun

Graduated in Telecommunication Engineering, but currently working with GNU/Linux infrastructure and in the spare time I'm an Open Source programmer (Python and C), a drawer and author in the Mind Bending Blog.


Comments

comments powered by Disqus