SecretStorage-2.3.1/0000755000175000017500000000000012760322233015131 5ustar dmitrydmitry00000000000000SecretStorage-2.3.1/secretstorage/0000755000175000017500000000000012760322233020003 5ustar dmitrydmitry00000000000000SecretStorage-2.3.1/secretstorage/collection.py0000644000175000017500000001721412713126105022513 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2013 # License: BSD """Collection is a place where secret items are stored. Normally, only the default collection should be used, but this module allows to use any registered collection. Use :func:`get_default_collection` to get the default collection (and create it, if necessary). Collections are usually automatically unlocked when user logs in, but collections can also be locked and unlocked using :meth:`Collection.lock` and :meth:`Collection.unlock` methods (unlocking requires showing the unlocking prompt to user and can be synchronous or asynchronous). Creating new items and editing existing ones is possible only in unlocked collection.""" import dbus from secretstorage.defines import SS_PREFIX, SS_PATH from secretstorage.exceptions import LockedException, ItemNotFoundException from secretstorage.item import Item from secretstorage.util import bus_get_object, InterfaceWrapper, \ exec_prompt_glib, format_secret, open_session, to_unicode, unlock_objects COLLECTION_IFACE = SS_PREFIX + 'Collection' SERVICE_IFACE = SS_PREFIX + 'Service' DEFAULT_COLLECTION = '/org/freedesktop/secrets/aliases/default' SESSION_COLLECTION = '/org/freedesktop/secrets/collection/session' class Collection(object): """Represents a collection.""" def __init__(self, bus, collection_path=DEFAULT_COLLECTION, session=None): collection_obj = bus_get_object(bus, collection_path) self.bus = bus self.session = session self.collection_path = collection_path self.collection_iface = InterfaceWrapper(collection_obj, COLLECTION_IFACE) self.collection_props_iface = InterfaceWrapper(collection_obj, dbus.PROPERTIES_IFACE) self.collection_props_iface.Get(COLLECTION_IFACE, 'Label', signature='ss') def is_locked(self): """Returns :const:`True` if item is locked, otherwise :const:`False`.""" return bool(self.collection_props_iface.Get( COLLECTION_IFACE, 'Locked', signature='ss')) def ensure_not_locked(self): """If collection is locked, raises :exc:`~secretstorage.exceptions.LockedException`.""" if self.is_locked(): raise LockedException('Collection is locked!') def unlock(self, callback=None): """Requests unlocking the collection. If `callback` is specified, calls it when unlocking is complete (see :func:`~secretstorage.util.exec_prompt` description for details). Otherwise, uses loop from GLib API and returns a boolean representing whether the operation was dismissed.""" return unlock_objects(self.bus, [self.collection_path], callback) def lock(self): """Locks the collection.""" service_obj = bus_get_object(self.bus, SS_PATH) service_iface = InterfaceWrapper(service_obj, SERVICE_IFACE) service_iface.Lock([self.collection_path], signature='ao') def delete(self): """Deletes the collection and all items inside it.""" self.ensure_not_locked() self.collection_iface.Delete(signature='') def get_all_items(self): """Returns a generator of all items in the collection.""" for item_path in self.collection_props_iface.Get( COLLECTION_IFACE, 'Items', signature='ss'): yield Item(self.bus, item_path, self.session) def search_items(self, attributes): """Returns a generator of items with the given attributes. `attributes` should be a dictionary.""" result = self.collection_iface.SearchItems(attributes, signature='a{ss}') for item_path in result: yield Item(self.bus, item_path, self.session) def get_label(self): """Returns the collection label.""" label = self.collection_props_iface.Get(COLLECTION_IFACE, 'Label', signature='ss') return to_unicode(label) def set_label(self, label): """Sets collection label to `label`.""" self.ensure_not_locked() self.collection_props_iface.Set(COLLECTION_IFACE, 'Label', label, signature='ssv') def create_item(self, label, attributes, secret, replace=False, content_type='text/plain'): """Creates a new :class:`~secretstorage.item.Item` with given `label` (unicode string), `attributes` (dictionary) and `secret` (bytestring). If `replace` is :const:`True`, replaces the existing item with the same attributes. If `content_type` is given, also sets the content type of the secret (``text/plain`` by default). Returns the created item.""" self.ensure_not_locked() if not self.session: self.session = open_session(self.bus) secret = format_secret(self.session, secret, content_type) attributes = dbus.Dictionary(attributes, signature='ss') properties = { SS_PREFIX+'Item.Label': label, SS_PREFIX+'Item.Attributes': attributes } new_item, prompt = self.collection_iface.CreateItem(properties, secret, replace, signature='a{sv}(oayays)b') return Item(self.bus, new_item, self.session) def create_collection(bus, label, alias='', session=None): """Creates a new :class:`Collection` with the given `label` and `alias` and returns it. This action requires prompting. If prompt is dismissed, raises :exc:`~secretstorage.exceptions.ItemNotFoundException`. This is synchronous function, uses loop from GLib API.""" if not session: session = open_session(bus) properties = {SS_PREFIX+'Collection.Label': label} service_obj = bus_get_object(bus, SS_PATH) service_iface = dbus.Interface(service_obj, SERVICE_IFACE) collection_path, prompt = service_iface.CreateCollection(properties, alias, signature='a{sv}s') if len(collection_path) > 1: return Collection(bus, collection_path, session=session) dismissed, unlocked = exec_prompt_glib(bus, prompt) if dismissed: raise ItemNotFoundException('Prompt dismissed.') return Collection(bus, unlocked, session=session) def get_all_collections(bus): """Returns a generator of all available collections.""" service_obj = bus_get_object(bus, SS_PATH) service_props_iface = dbus.Interface(service_obj, dbus.PROPERTIES_IFACE) for collection_path in service_props_iface.Get(SERVICE_IFACE, 'Collections', signature='ss'): yield Collection(bus, collection_path) def get_default_collection(bus, session=None): """Returns the default collection. If it doesn't exist, creates it.""" try: return Collection(bus) except ItemNotFoundException: return create_collection(bus, 'Default', 'default', session) def get_any_collection(bus): """Returns any collection, in the following order of preference: - The default collection; - The "session" collection (usually temporary); - The first collection in the collections list.""" try: return Collection(bus) except ItemNotFoundException: pass try: # GNOME Keyring provides session collection where items # are stored in process memory. return Collection(bus, SESSION_COLLECTION) except ItemNotFoundException: pass collections = list(get_all_collections(bus)) if collections: return collections[0] else: raise ItemNotFoundException('No collections found.') def get_collection_by_alias(bus, alias): """Returns the collection with the given `alias`. If there is no such collection, raises :exc:`~secretstorage.exceptions.ItemNotFoundException`.""" service_obj = bus_get_object(bus, SS_PATH) service_iface = dbus.Interface(service_obj, SERVICE_IFACE) collection_path = service_iface.ReadAlias(alias, signature='s') if len(collection_path) <= 1: raise ItemNotFoundException('No collection with such alias.') return Collection(bus, collection_path) def search_items(bus, attributes): """Returns a generator of items in all collections with the given attributes. `attributes` should be a dictionary.""" service_obj = bus_get_object(bus, SS_PATH) service_iface = dbus.Interface(service_obj, SERVICE_IFACE) locked, unlocked = service_iface.SearchItems(attributes, signature='a{ss}') for item_path in locked + unlocked: yield Item(bus, item_path) SecretStorage-2.3.1/secretstorage/__init__.py0000644000175000017500000000366612760252237022135 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2013 # License: BSD """This file provides quick access to all SecretStorage API. Please refer to documentation of individual modules for API details. It also provides some functions for compatibility with older SecretStorage releases. Those functions are not recommended for use in new software.""" import dbus from secretstorage.collection import Collection, create_collection, \ get_all_collections, get_default_collection, get_any_collection, \ get_collection_by_alias, search_items from secretstorage.item import Item from secretstorage.defines import DBUS_NOT_SUPPORTED, DBUS_EXEC_FAILED, \ DBUS_NO_REPLY, DBUS_ACCESS_DENIED from secretstorage.exceptions import SecretStorageException, \ SecretServiceNotAvailableException, LockedException, \ ItemNotFoundException __version_tuple__ = (2, 3, 1) __version__ = '.'.join(map(str, __version_tuple__)) def dbus_init(main_loop=True, use_qt_loop=False): """Returns new SessionBus_. If `main_loop` is :const:`True` and no D-Bus main loop is registered yet, registers a default main loop (PyQt5 main loop if `use_qt_loop` is :const:`True`, otherwise GLib main loop). .. _SessionBus: https://www.freedesktop.org/wiki/IntroductionToDBus/#buses .. note:: Qt uses GLib main loops on UNIX-like systems by default, so one will rarely need to set `use_qt_loop` to :const:`True`. """ if main_loop and not dbus.get_default_main_loop(): if use_qt_loop: from dbus.mainloop.pyqt5 import DBusQtMainLoop DBusQtMainLoop(set_as_default=True) else: from dbus.mainloop.glib import DBusGMainLoop DBusGMainLoop(set_as_default=True) try: return dbus.SessionBus() except dbus.exceptions.DBusException as e: if e.get_dbus_name() in (DBUS_NOT_SUPPORTED, DBUS_EXEC_FAILED, DBUS_NO_REPLY, DBUS_ACCESS_DENIED): raise SecretServiceNotAvailableException( e.get_dbus_message()) raise SecretStorage-2.3.1/secretstorage/dhcrypto.py0000644000175000017500000000457212755122126022224 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2014 # License: BSD '''This module contains needed classes, functions and constants to implement dh-ietf1024-sha256-aes128-cbc-pkcs7 secret encryption algorithm.''' import hmac import math import os from hashlib import sha256 from cryptography.utils import int_from_bytes # A standard 1024 bits (128 bytes) prime number for use in Diffie-Hellman exchange DH_PRIME_1024_BYTES = ( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ) if hasattr(int, 'to_bytes'): def int_to_bytes(number): return int.to_bytes(number, math.ceil(number.bit_length() / 8), 'big') else: from cryptography.utils import int_to_bytes DH_PRIME_1024 = int_from_bytes(bytearray(DH_PRIME_1024_BYTES), 'big') class Session(object): def __init__(self): self.object_path = None self.server_public_key = None self.aes_key = None self.encrypted = True # 128-bytes-long strong random number self.my_private_key = int_from_bytes(os.urandom(0x80), 'big') self.my_public_key = pow(2, self.my_private_key, DH_PRIME_1024) def set_server_public_key(self, server_public_key): common_secret = pow(server_public_key, self.my_private_key, DH_PRIME_1024) common_secret = int_to_bytes(common_secret) # Prepend NULL bytes if needed common_secret = b'\x00' * (0x80 - len(common_secret)) + common_secret # HKDF with null salt, empty info and SHA-256 hash salt = b'\x00' * 0x20 pseudo_random_key = hmac.new(salt, common_secret, sha256).digest() output_block = hmac.new(pseudo_random_key, b'\x01', sha256).digest() # Resulting AES key should be 128-bit self.aes_key = output_block[:0x10] SecretStorage-2.3.1/secretstorage/util.py0000644000175000017500000001416312755122150021337 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2013 # License: BSD """This module provides some utility functions, but these shouldn't normally be used by external applications.""" import dbus import os from secretstorage.defines import DBUS_UNKNOWN_METHOD, DBUS_NO_SUCH_OBJECT, \ DBUS_SERVICE_UNKNOWN, DBUS_NO_REPLY, DBUS_NOT_SUPPORTED, DBUS_EXEC_FAILED, \ SS_PATH, SS_PREFIX, ALGORITHM_DH, ALGORITHM_PLAIN from secretstorage.dhcrypto import Session, int_to_bytes from secretstorage.exceptions import ItemNotFoundException, \ SecretServiceNotAvailableException from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.utils import int_from_bytes BUS_NAME = 'org.freedesktop.secrets' SERVICE_IFACE = SS_PREFIX + 'Service' class InterfaceWrapper(dbus.Interface): """Wraps :cls:`dbus.Interface` class and replaces some D-Bus exceptions with :doc:`SecretStorage exceptions `.""" def catch_errors(self, function_in): def function_out(*args, **kwargs): try: return function_in(*args, **kwargs) except dbus.exceptions.DBusException as e: if e.get_dbus_name() == DBUS_UNKNOWN_METHOD: raise ItemNotFoundException('Item does not exist!') if e.get_dbus_name() == DBUS_NO_SUCH_OBJECT: raise ItemNotFoundException(e.get_dbus_message()) if e.get_dbus_name() in (DBUS_NO_REPLY, DBUS_NOT_SUPPORTED): raise SecretServiceNotAvailableException( e.get_dbus_message()) raise return function_out def __getattr__(self, attribute): result = dbus.Interface.__getattr__(self, attribute) if callable(result): result = self.catch_errors(result) return result def bus_get_object(bus, object_path, service_name=None): """A wrapper around :meth:`SessionBus.get_object` that raises :exc:`~secretstorage.exceptions.SecretServiceNotAvailableException` when appropriate.""" name = service_name or BUS_NAME try: return bus.get_object(name, object_path, introspect=False) except dbus.exceptions.DBusException as e: if e.get_dbus_name() in (DBUS_SERVICE_UNKNOWN, DBUS_EXEC_FAILED, DBUS_NO_REPLY): raise SecretServiceNotAvailableException(e.get_dbus_message()) raise def open_session(bus): """Returns a new Secret Service session.""" service_obj = bus_get_object(bus, SS_PATH) service_iface = dbus.Interface(service_obj, SS_PREFIX+'Service') session = Session() try: output, result = service_iface.OpenSession( ALGORITHM_DH, dbus.ByteArray(int_to_bytes(session.my_public_key)), signature='sv' ) except dbus.exceptions.DBusException as e: if e.get_dbus_name() != DBUS_NOT_SUPPORTED: raise output, result = service_iface.OpenSession( ALGORITHM_PLAIN, '', signature='sv' ) session.encrypted = False else: output = int_from_bytes(bytearray(output), 'big') session.set_server_public_key(output) session.object_path = result return session def format_secret(session, secret, content_type): """Formats `secret` to make possible to pass it to the Secret Service API.""" if not isinstance(secret, bytes): secret = secret.encode('utf-8') if not session.encrypted: return dbus.Struct((session.object_path, '', dbus.ByteArray(secret), content_type)) # PKCS-7 style padding padding = 0x10 - (len(secret) & 0xf) secret += bytes(bytearray((padding,)) * padding) aes_iv = os.urandom(0x10) aes = algorithms.AES(session.aes_key) encryptor = Cipher(aes, modes.CBC(aes_iv), default_backend()).encryptor() encrypted_secret = encryptor.update(secret) + encryptor.finalize() return dbus.Struct(( session.object_path, dbus.Array(aes_iv), dbus.Array(bytearray(encrypted_secret)), content_type )) def exec_prompt(bus, prompt, callback): """Executes the given `prompt`, when complete calls `callback` function with two arguments: a boolean representing whether the operation was dismissed and a list of unlocked item paths. A main loop should be running and registered for this function to work.""" prompt_obj = bus_get_object(bus, prompt) prompt_iface = dbus.Interface(prompt_obj, SS_PREFIX+'Prompt') prompt_iface.Prompt('', signature='s') def new_callback(dismissed, unlocked): if isinstance(unlocked, dbus.Array): unlocked = list(unlocked) callback(bool(dismissed), unlocked) prompt_iface.connect_to_signal('Completed', new_callback) def exec_prompt_glib(bus, prompt): """Like :func:`exec_prompt`, but synchronous (uses loop from GLib API). Returns (*dismissed*, *unlocked*) tuple.""" from gi.repository import GLib loop = GLib.MainLoop() result = [] def callback(dismissed, unlocked): result.append(dismissed) result.append(unlocked) loop.quit() exec_prompt(bus, prompt, callback) loop.run() return result[0], result[1] def exec_prompt_qt(bus, prompt): """Like :func:`exec_prompt`, but synchronous (uses loop from PyQt5 API). Returns (*dismissed*, *unlocked*) tuple.""" from PyQt5.QtCore import QCoreApplication app = QCoreApplication([]) result = [] def callback(dismissed, unlocked): result.append(dismissed) result.append(unlocked) app.quit() exec_prompt(bus, prompt, callback) app.exec_() return result[0], result[1] def unlock_objects(bus, paths, callback=None): """Requests unlocking objects specified in `paths`. If `callback` is specified, calls it when unlocking is complete (see :func:`exec_prompt` description for details). Otherwise, uses the loop from GLib API and returns a boolean representing whether the operation was dismissed. .. versionadded:: 2.1.2""" service_obj = bus_get_object(bus, SS_PATH) service_iface = InterfaceWrapper(service_obj, SERVICE_IFACE) unlocked_paths, prompt = service_iface.Unlock(paths, signature='ao') unlocked_paths = list(unlocked_paths) # Convert from dbus.Array if len(prompt) > 1: if callback: exec_prompt(bus, prompt, callback) else: return exec_prompt_glib(bus, prompt)[0] elif callback: # We still need to call it. callback(False, unlocked_paths) def to_unicode(string): """Converts D-Bus string to unicode string.""" try: # For Python 2 return unicode(string) except NameError: # For Python 3 return str(string) SecretStorage-2.3.1/secretstorage/defines.py0000644000175000017500000000143012713126105021766 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2013 # License: BSD # This file contains some common defines. SS_PREFIX = 'org.freedesktop.Secret.' SS_PATH = '/org/freedesktop/secrets' DBUS_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' DBUS_ACCESS_DENIED = 'org.freedesktop.DBus.Error.AccessDenied' DBUS_SERVICE_UNKNOWN = 'org.freedesktop.DBus.Error.ServiceUnknown' DBUS_EXEC_FAILED = 'org.freedesktop.DBus.Error.Spawn.ExecFailed' DBUS_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' DBUS_NOT_SUPPORTED = 'org.freedesktop.DBus.Error.NotSupported' DBUS_NO_SUCH_OBJECT = 'org.freedesktop.Secret.Error.NoSuchObject' ALGORITHM_PLAIN = 'plain' ALGORITHM_DH = 'dh-ietf1024-sha256-aes128-cbc-pkcs7' SecretStorage-2.3.1/secretstorage/exceptions.py0000644000175000017500000000300412563100302022523 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2012 # License: BSD """All secretstorage functions may raise various exceptions when something goes wrong. All exceptions derive from base :exc:`SecretStorageException` class.""" class SecretStorageException(Exception): """All exceptions derive from this class.""" class SecretServiceNotAvailableException(SecretStorageException): """Raised by :class:`~secretstorage.item.Item` or :class:`~secretstorage.collection.Collection` constructors, or by other functions in the :mod:`secretstorage.collection` module, when the Secret Service API is not available.""" class LockedException(SecretStorageException): """Raised when an action cannot be performed because the collection is locked. Use :meth:`~secretstorage.collection.Collection.is_locked` to check if the collection is locked, and :meth:`~secretstorage.collection.Collection.unlock` to unlock it. """ class ItemNotFoundException(SecretStorageException): """Raised when an item does not exist or has been deleted. Example of handling: >>> import secretstorage >>> bus = secretstorage.dbus_init() >>> item_path = '/not/existing/path' >>> try: ... item = secretstorage.Item(bus, item_path) ... except secretstorage.ItemNotFoundException: ... print('Item not found!') ... Item not found! Also, :func:`~secretstorage.collection.create_collection` may raise this exception when a prompt was dismissed during creating the collection. """ SecretStorage-2.3.1/secretstorage/item.py0000644000175000017500000001177612736510206021331 0ustar dmitrydmitry00000000000000# SecretStorage module for Python # Access passwords using the SecretService DBus API # Author: Dmitry Shachnev, 2013 # License: BSD """SecretStorage item contains a *secret*, some *attributes* and a *label* visible to user. Editing all these properties and reading the secret is possible only when the :doc:`collection ` storing the item is unlocked. The collection can be unlocked using collection's :meth:`~secretstorage.collection.Collection.unlock` method.""" import dbus from secretstorage.defines import SS_PREFIX from secretstorage.exceptions import LockedException from secretstorage.util import InterfaceWrapper, bus_get_object, \ open_session, format_secret, to_unicode, unlock_objects from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend ITEM_IFACE = SS_PREFIX + 'Item' class Item(object): """Represents a secret item.""" def __init__(self, bus, item_path, session=None): self.item_path = item_path item_obj = bus_get_object(bus, item_path) self.session = session self.bus = bus self.item_iface = InterfaceWrapper(item_obj, ITEM_IFACE) self.item_props_iface = InterfaceWrapper(item_obj, dbus.PROPERTIES_IFACE) self.item_props_iface.Get(ITEM_IFACE, 'Label', signature='ss') def __eq__(self, other): return self.item_path == other.item_path def is_locked(self): """Returns :const:`True` if item is locked, otherwise :const:`False`.""" return bool(self.item_props_iface.Get(ITEM_IFACE, 'Locked', signature='ss')) def ensure_not_locked(self): """If collection is locked, raises :exc:`~secretstorage.exceptions.LockedException`.""" if self.is_locked(): raise LockedException('Item is locked!') def unlock(self, callback=None): """Requests unlocking the item. Usually, this will mean that the whole collection containing this item will be unlocked. If `callback` is specified, calls it when unlocking is complete (see :func:`~secretstorage.util.exec_prompt` description for details). Otherwise, uses the loop from GLib API and returns a boolean representing whether the operation was dismissed. .. versionadded:: 2.1.2""" return unlock_objects(self.bus, [self.item_path], callback) def get_attributes(self): """Returns item attributes (dictionary).""" attrs = self.item_props_iface.Get(ITEM_IFACE, 'Attributes', signature='ss') return {to_unicode(key): to_unicode(value) for key, value in attrs.items()} def set_attributes(self, attributes): """Sets item attributes to `attributes` (dictionary).""" self.item_props_iface.Set(ITEM_IFACE, 'Attributes', attributes, signature='ssv') def get_label(self): """Returns item label (unicode string).""" label = self.item_props_iface.Get(ITEM_IFACE, 'Label', signature='ss') return to_unicode(label) def set_label(self, label): """Sets item label to `label`.""" self.ensure_not_locked() self.item_props_iface.Set(ITEM_IFACE, 'Label', label, signature='ssv') def delete(self): """Deletes the item.""" self.ensure_not_locked() return self.item_iface.Delete(signature='') def get_secret(self): """Returns item secret (bytestring).""" self.ensure_not_locked() if not self.session: self.session = open_session(self.bus) secret = self.item_iface.GetSecret(self.session.object_path, signature='o') if not self.session.encrypted: return bytes(bytearray(secret[2])) aes = algorithms.AES(self.session.aes_key) aes_iv = bytes(bytearray(secret[1])) decryptor = Cipher(aes, modes.CBC(aes_iv), default_backend()).decryptor() encrypted_secret = bytes(bytearray(secret[2])) padded_secret = decryptor.update(encrypted_secret) + decryptor.finalize() padded_secret = bytearray(padded_secret) return bytes(padded_secret[:-padded_secret[-1]]) def get_secret_content_type(self): """Returns content type of item secret (string).""" self.ensure_not_locked() if not self.session: self.session = open_session(self.bus) secret = self.item_iface.GetSecret(self.session.object_path, signature='o') return str(secret[3]) def set_secret(self, secret, content_type='text/plain'): """Sets item secret to `secret`. If `content_type` is given, also sets the content type of the secret (``text/plain`` by default).""" self.ensure_not_locked() if not self.session: self.session = open_session(self.bus) secret = format_secret(self.session, secret, content_type) self.item_iface.SetSecret(secret, signature='(oayays)') def get_created(self): """Returns UNIX timestamp (integer) representing the time when the item was created. .. versionadded:: 1.1""" return int(self.item_props_iface.Get(ITEM_IFACE, 'Created', signature='ss')) def get_modified(self): """Returns UNIX timestamp (integer) representing the time when the item was last modified.""" return int(self.item_props_iface.Get(ITEM_IFACE, 'Modified', signature='ss')) def to_tuple(self): """Returns (*attributes*, *secret*) tuple representing the item.""" self.ensure_not_locked() return self.get_attributes(), self.get_secret() SecretStorage-2.3.1/docs/0000755000175000017500000000000012760322232016060 5ustar dmitrydmitry00000000000000SecretStorage-2.3.1/docs/collection.rst0000644000175000017500000000025712563100302020742 0ustar dmitrydmitry00000000000000======================================= The ``secretstorage.collection`` module ======================================= .. automodule:: secretstorage.collection :members: SecretStorage-2.3.1/docs/conf.py0000644000175000017500000000453612760252224017372 0ustar dmitrydmitry00000000000000#!/usr/bin/env python3 import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'SecretStorage' copyright = '2016, Dmitry Shachnev' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.3' # The full version, including alpha/beta/rc tags. release = '2.3.1' # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'description': 'Python bindings to the FreeDesktop.org Secret Service API', 'description_font_style': 'italic', 'github_button': False, 'sidebar_width': '260px', } # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': ['about.html', 'navigation.html', 'sourcelink.html', 'searchbox.html'] } # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'SecretStorage.tex', 'SecretStorage Documentation', 'Dmitry Shachnev', 'manual'), ] SecretStorage-2.3.1/docs/item.rst0000644000175000017500000000022712563100302017542 0ustar dmitrydmitry00000000000000================================= The ``secretstorage.item`` module ================================= .. automodule:: secretstorage.item :members: SecretStorage-2.3.1/docs/util.rst0000644000175000017500000000036312563100302017562 0ustar dmitrydmitry00000000000000============================ Additional utility functions ============================ .. automodule:: secretstorage.util :members: format_secret, exec_prompt, exec_prompt_glib, exec_prompt_qt, unlock_objects SecretStorage-2.3.1/docs/exceptions.rst0000644000175000017500000000016312563100302020764 0ustar dmitrydmitry00000000000000=================== Possible exceptions =================== .. automodule:: secretstorage.exceptions :members: SecretStorage-2.3.1/docs/index.rst0000644000175000017500000000511412731215267017731 0ustar dmitrydmitry00000000000000======================================= Welcome to SecretStorage documentation! ======================================= This module provides a way for securely storing passwords and other secrets. It uses `D-Bus`_-based FreeDesktop.org `Secret Service`_ standard that is, for example, supported by `GNOME Keyring`_ (since version 2.30) and by KSecretsService_. It allows one to create new secret items, delete and search for passwords matching given attributes. It also supports graphical prompts when unlocking is needed. .. _`D-Bus`: https://www.freedesktop.org/wiki/Software/dbus .. _`Secret Service`: https://specifications.freedesktop.org/secret-service/ .. _`GNOME Keyring`: https://wiki.gnome.org/Projects/GnomeKeyring .. _KSecretsService: https://techbase.kde.org/Projects/Utils/ksecretsservice SecretStorage code is hosted on GitHub_. .. _GitHub: https://github.com/mitya57/secretstorage Initializing D-Bus ================== .. seealso:: If you don't know how D-Bus works, please read `Introduction to D-Bus`_ firstly. .. _`Introduction to D-Bus`: https://www.freedesktop.org/wiki/IntroductionToDBus Before using SecretStorage, you need to initialize D-Bus. This can be done using this function: .. autofunction:: secretstorage.dbus_init Examples of using SecretStorage =============================== Creating a new item in the default collection: >>> import secretstorage >>> bus = secretstorage.dbus_init() >>> collection = secretstorage.get_default_collection(bus) >>> attributes = {'application': 'myapp', 'another attribute': ... 'another value'} >>> item = collection.create_item('My first item', attributes, ... b'pa$$word') Getting item's label, attributes and secret: >>> item.get_label() 'My first item' >>> item.get_attributes() {'another attribute': 'another value', 'application': 'myapp'} >>> item.get_secret() b'pa$$word' Locking and unlocking collections: >>> collection.lock() >>> collection.is_locked() True >>> collection.unlock() >>> collection.is_locked() False Asynchronously unlocking the collection (the GLib main loop is used here, Qt loop is also supported): >>> from gi.repository import GLib >>> loop = GLib.MainLoop() >>> def callback(dismissed, unlocked): ... print('dismissed:', dismissed) ... print('unlocked:', unlocked) ... loop.quit() ... >>> collection.unlock(callback); loop.run() dismissed: False unlocked: [dbus.ObjectPath('/org/freedesktop/secrets/aliases/default')] Contents ======== .. toctree:: :maxdepth: 2 collection item util exceptions Indices and tables ================== * :ref:`genindex` * :ref:`search` SecretStorage-2.3.1/changelog0000644000175000017500000001024412760252170017006 0ustar dmitrydmitry00000000000000SecretStorage 2.3.1, 2016-08-27 =============================== * Update requires line in setup.py for cryptography port. * Documentation is now hosted on ReadTheDocs. SecretStorage 2.3.0, 2016-08-17 =============================== * Ported from PyCrypto to cryptography module [#6]. * Item.get_secret() now returns a bytes object rather than a bytearray. SecretStorage 2.2.1, 2016-06-27 =============================== * Made dbus-python dependency optional because compiling it from sources is not an option for many users. See issues #4 and #5 for details. SecretStorage 2.2.0, 2016-06-18 =============================== * Deprecated compatibility functions are dropped. * Installation from PyPI now pulls in dbus-python. * Travis CI tests added. * Other minor fixes, simplifications and improvements. SecretStorage 2.1.4, 2016-01-10 =============================== * Catch AccessDenied errors in dbus_init() function. * Documentation improvements. SecretStorage 2.1.3, 2015-12-20 =============================== * Python 2.6 is no longer supported. * Compatibility functions are now deprecated and will be removed in the next major release. * Other minor fixes, simplifications and improvements. SecretStorage 2.1.2, 2015-06-30 =============================== * Add Item.unlock() method. * Use setuptools when it is available. * Documentation now uses Alabaster sphinx theme. * Other documentation fixes and improvements. SecretStorage 2.1.1, 2014-07-12 =============================== * Fixed a bug where common secret was incorrectly generated sometimes (one time of 128). * Other minor improvements. SecretStorage 2.1, 2014-05-28 ============================= * Support running tests with GNOME Keyring when there is no default collection. * When D-Bus main loop is already set, do not set it again. * Make dhcrypto module work with Python < 2.7.4. SecretStorage 2.0, 2014-01-27 ============================= * Add support for encrypted sessions and use them by default. * Get rid of Introspect() calls to make D-Bus messaging faster. SecretStorage 1.1, 2013-11-15 ============================= * Ported to PyQt5. * Added `Item.get_created()` method. * Improvements to error handling. SecretStorage 1.0, 2013-05-08 ============================= * Renamed `exec_prompt_async_*` functions to just `exec_prompt_*` (old aliases kept for compatibility purposes). * Added two helper functions: - `get_any_collection()` for getting default or session collection; - `get_default_collection()` for getting or creating the default collection. * Fix creation of items with empty attributes dict. * Make `SecretServiceNotAvailableException` a subclass of `SecretStorageException`. * Various documentation improvements. SecretStorage 0.9, 2013-03-05 ============================= * Added support for content types in secrets. * Added `Item.get_modified()` method. * Added `get_all_collections()` and `get_collection_by_alias()` functions. * Added `search_items()` function for global search. * Made synchronous version of `Collection.unlock()` return a boolean representing whether the operation was dismissed. * Fixed wrong parsing of Secret Service result in `Collection.get_modified()`. * Various test suite and documentation improvements. SecretStorage 0.8, 2013-01-05 ============================= * Added `Collection` and `Item` classes. * Added support for creating and deleting collections. * Added synchronous loop unlocking support. * Added support for PyQt applications. * Added test suite. * Convert D-Bus exceptions to SecretStorage exceptions. * Rewrote the documentation in Sphinx. * Miscellaneous internal improvements. SecretStorage 0.2, 2012-06-22 ============================= * Added `get_item_attributes` function. * Renamed `get_item_ids` to `get_items_ids`. * Renamed `get_item_by_id` to `get_item`. * Renamed `delete_item_by_id` to `delete_item`. * Made `create_item` return id of the created item. * Added `secretstorage.exceptions` module. * Made all functions throw exceptions from that module. * Updated the documentation. * Added `delete_test_items.py` script that deletes all test items. SecretStorage 0.1, 2012-06-02 ============================= * Initial release. SecretStorage-2.3.1/SecretStorage.egg-info/0000755000175000017500000000000012760322232021374 5ustar dmitrydmitry00000000000000SecretStorage-2.3.1/SecretStorage.egg-info/requires.txt0000644000175000017500000000005012760322230023765 0ustar dmitrydmitry00000000000000cryptography [dbus-python] dbus-python SecretStorage-2.3.1/SecretStorage.egg-info/PKG-INFO0000644000175000017500000001013012760322230022462 0ustar dmitrydmitry00000000000000Metadata-Version: 1.1 Name: SecretStorage Version: 2.3.1 Summary: Python bindings to FreeDesktop.org Secret Service API Home-page: https://github.com/mitya57/secretstorage Author: Dmitry Shachnev Author-email: mitya57@gmail.com License: BSD Description: .. image:: https://api.travis-ci.org/mitya57/secretstorage.svg :target: https://travis-ci.org/mitya57/secretstorage :alt: Travis CI status Module description ================== This module provides a way for securely storing passwords and other secrets. It uses D-Bus `Secret Service`_ API that is supported by GNOME Keyring (since version 2.30) and KSecretsService. The main classes provided are ``secretstorage.Item``, representing a secret item (that has a *label*, a *secret* and some *attributes*) and ``secretstorage.Collection``, a place items are stored in. SecretStorage supports most of the functions provided by Secret Service, including creating and deleting items and collections, editing items, locking and unlocking collections (asynchronous unlocking is also supported). The documentation can be found on `secretstorage.readthedocs.io`_. .. _`Secret Service`: https://specifications.freedesktop.org/secret-service/ .. _`secretstorage.readthedocs.io`: https://secretstorage.readthedocs.io/en/latest/ Building the module =================== .. note:: SecretStorage supports Python 2.7 and all versions of Python since 3.3. Here we assume that your Python version is 3.x. SecretStorage requires these packages to work: * `dbus-python`_ * `python-cryptography`_ To build SecretStorage, use this command:: python3 setup.py build If you have Sphinx_ installed, you can also build the documentation:: python3 setup.py build_sphinx .. _`dbus-python`: https://www.freedesktop.org/wiki/Software/DBusBindings/#dbus-python .. _`python-cryptography`: https://pypi.python.org/pypi/cryptography .. _Sphinx: http://sphinx-doc.org/ Testing the module ================== First, make sure that you have the Secret Service daemon installed. The `GNOME Keyring`_ is the reference server-side implementation for the Secret Service specification. .. _`GNOME Keyring`: https://download.gnome.org/sources/gnome-keyring/ Then, start the daemon and unlock the ``default`` collection, if needed. The testsuite will fail to run if the ``default`` collection exists and is locked. If it does not exist, the testsuite can also use the temporary ``session`` collection, as provided by the GNOME Keyring. Then, run the Python unittest module:: python3 -m unittest discover -s tests If you want to run the tests in an isolated or headless environment, run this command in a D-Bus session:: dbus-run-session -- python3 -m unittest discover -s tests Get the code ============ SecretStorage is available under BSD license. The source code can be found on GitHub_. .. _GitHub: https://github.com/mitya57/secretstorage Platform: Linux Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Security Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires: dbus Requires: cryptography SecretStorage-2.3.1/SecretStorage.egg-info/dependency_links.txt0000644000175000017500000000000112760322230025440 0ustar dmitrydmitry00000000000000 SecretStorage-2.3.1/SecretStorage.egg-info/top_level.txt0000644000175000017500000000001612760322230024121 0ustar dmitrydmitry00000000000000secretstorage SecretStorage-2.3.1/SecretStorage.egg-info/SOURCES.txt0000644000175000017500000000123412760322231023257 0ustar dmitrydmitry00000000000000.gitignore .travis.yml LICENSE MANIFEST.in README.rst changelog setup.py SecretStorage.egg-info/PKG-INFO SecretStorage.egg-info/SOURCES.txt SecretStorage.egg-info/dependency_links.txt SecretStorage.egg-info/requires.txt SecretStorage.egg-info/top_level.txt docs/collection.rst docs/conf.py docs/exceptions.rst docs/index.rst docs/item.rst docs/util.rst secretstorage/__init__.py secretstorage/collection.py secretstorage/defines.py secretstorage/dhcrypto.py secretstorage/exceptions.py secretstorage/item.py secretstorage/util.py tests/cleanup_test_items.py tests/run_tests.py tests/test_collection.py tests/test_dhcrypto.py tests/test_exceptions.py tests/test_item.pySecretStorage-2.3.1/PKG-INFO0000644000175000017500000001013012760322233016221 0ustar dmitrydmitry00000000000000Metadata-Version: 1.1 Name: SecretStorage Version: 2.3.1 Summary: Python bindings to FreeDesktop.org Secret Service API Home-page: https://github.com/mitya57/secretstorage Author: Dmitry Shachnev Author-email: mitya57@gmail.com License: BSD Description: .. image:: https://api.travis-ci.org/mitya57/secretstorage.svg :target: https://travis-ci.org/mitya57/secretstorage :alt: Travis CI status Module description ================== This module provides a way for securely storing passwords and other secrets. It uses D-Bus `Secret Service`_ API that is supported by GNOME Keyring (since version 2.30) and KSecretsService. The main classes provided are ``secretstorage.Item``, representing a secret item (that has a *label*, a *secret* and some *attributes*) and ``secretstorage.Collection``, a place items are stored in. SecretStorage supports most of the functions provided by Secret Service, including creating and deleting items and collections, editing items, locking and unlocking collections (asynchronous unlocking is also supported). The documentation can be found on `secretstorage.readthedocs.io`_. .. _`Secret Service`: https://specifications.freedesktop.org/secret-service/ .. _`secretstorage.readthedocs.io`: https://secretstorage.readthedocs.io/en/latest/ Building the module =================== .. note:: SecretStorage supports Python 2.7 and all versions of Python since 3.3. Here we assume that your Python version is 3.x. SecretStorage requires these packages to work: * `dbus-python`_ * `python-cryptography`_ To build SecretStorage, use this command:: python3 setup.py build If you have Sphinx_ installed, you can also build the documentation:: python3 setup.py build_sphinx .. _`dbus-python`: https://www.freedesktop.org/wiki/Software/DBusBindings/#dbus-python .. _`python-cryptography`: https://pypi.python.org/pypi/cryptography .. _Sphinx: http://sphinx-doc.org/ Testing the module ================== First, make sure that you have the Secret Service daemon installed. The `GNOME Keyring`_ is the reference server-side implementation for the Secret Service specification. .. _`GNOME Keyring`: https://download.gnome.org/sources/gnome-keyring/ Then, start the daemon and unlock the ``default`` collection, if needed. The testsuite will fail to run if the ``default`` collection exists and is locked. If it does not exist, the testsuite can also use the temporary ``session`` collection, as provided by the GNOME Keyring. Then, run the Python unittest module:: python3 -m unittest discover -s tests If you want to run the tests in an isolated or headless environment, run this command in a D-Bus session:: dbus-run-session -- python3 -m unittest discover -s tests Get the code ============ SecretStorage is available under BSD license. The source code can be found on GitHub_. .. _GitHub: https://github.com/mitya57/secretstorage Platform: Linux Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Security Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires: dbus Requires: cryptography SecretStorage-2.3.1/README.rst0000644000175000017500000000512712760116403016625 0ustar dmitrydmitry00000000000000.. image:: https://api.travis-ci.org/mitya57/secretstorage.svg :target: https://travis-ci.org/mitya57/secretstorage :alt: Travis CI status Module description ================== This module provides a way for securely storing passwords and other secrets. It uses D-Bus `Secret Service`_ API that is supported by GNOME Keyring (since version 2.30) and KSecretsService. The main classes provided are ``secretstorage.Item``, representing a secret item (that has a *label*, a *secret* and some *attributes*) and ``secretstorage.Collection``, a place items are stored in. SecretStorage supports most of the functions provided by Secret Service, including creating and deleting items and collections, editing items, locking and unlocking collections (asynchronous unlocking is also supported). The documentation can be found on `secretstorage.readthedocs.io`_. .. _`Secret Service`: https://specifications.freedesktop.org/secret-service/ .. _`secretstorage.readthedocs.io`: https://secretstorage.readthedocs.io/en/latest/ Building the module =================== .. note:: SecretStorage supports Python 2.7 and all versions of Python since 3.3. Here we assume that your Python version is 3.x. SecretStorage requires these packages to work: * `dbus-python`_ * `python-cryptography`_ To build SecretStorage, use this command:: python3 setup.py build If you have Sphinx_ installed, you can also build the documentation:: python3 setup.py build_sphinx .. _`dbus-python`: https://www.freedesktop.org/wiki/Software/DBusBindings/#dbus-python .. _`python-cryptography`: https://pypi.python.org/pypi/cryptography .. _Sphinx: http://sphinx-doc.org/ Testing the module ================== First, make sure that you have the Secret Service daemon installed. The `GNOME Keyring`_ is the reference server-side implementation for the Secret Service specification. .. _`GNOME Keyring`: https://download.gnome.org/sources/gnome-keyring/ Then, start the daemon and unlock the ``default`` collection, if needed. The testsuite will fail to run if the ``default`` collection exists and is locked. If it does not exist, the testsuite can also use the temporary ``session`` collection, as provided by the GNOME Keyring. Then, run the Python unittest module:: python3 -m unittest discover -s tests If you want to run the tests in an isolated or headless environment, run this command in a D-Bus session:: dbus-run-session -- python3 -m unittest discover -s tests Get the code ============ SecretStorage is available under BSD license. The source code can be found on GitHub_. .. _GitHub: https://github.com/mitya57/secretstorage SecretStorage-2.3.1/LICENSE0000644000175000017500000000274012731215541016142 0ustar dmitrydmitry00000000000000Copyright 2012-2016 Dmitry Shachnev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SecretStorage-2.3.1/setup.cfg0000644000175000017500000000007312760322233016752 0ustar dmitrydmitry00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 SecretStorage-2.3.1/tests/0000755000175000017500000000000012760322233016273 5ustar dmitrydmitry00000000000000SecretStorage-2.3.1/tests/test_collection.py0000644000175000017500000000220212563100302022023 0ustar dmitrydmitry00000000000000# Tests for SecretStorage # Author: Dmitry Shachnev, 2013 # License: BSD # This file tests the secretstorage.Collection class. import unittest from secretstorage import dbus_init, get_any_collection, get_all_collections, Collection class CollectionTest(unittest.TestCase): """A test case that tests that all common methods of Collection class work and do not crash.""" @classmethod def setUpClass(cls): cls.bus = dbus_init(main_loop=False) cls.collection = get_any_collection(cls.bus) def test_all_collections(self): labels = map(Collection.get_label, get_all_collections(self.bus)) self.assertIn(self.collection.get_label(), labels) def test_all_items(self): for item in self.collection.get_all_items(): item.get_label() def test_create_empty_item(self): item = self.collection.create_item('', {}, b'') item.delete() def test_label(self): old_label = self.collection.get_label() self.collection.set_label('Hello!') self.assertEqual(self.collection.get_label(), 'Hello!') self.collection.set_label(old_label) self.assertEqual(self.collection.get_label(), old_label) if __name__ == '__main__': unittest.main() SecretStorage-2.3.1/tests/run_tests.py0000644000175000017500000000165712713126105020702 0ustar dmitrydmitry00000000000000#!/usr/bin/env python import os.path import sys import subprocess import unittest tests_dir = os.path.dirname(__file__) sys.path.insert(0, os.path.dirname(tests_dir)) import secretstorage if __name__ == '__main__': print('Running with Python %d.%d.%d (SecretStorage from %s)' % (sys.version_info[:3] + (os.path.dirname(secretstorage.__file__),))) mock = None if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]): mock = subprocess.Popen(('/usr/bin/python3', sys.argv[1],), stdout=subprocess.PIPE, universal_newlines=True) bus_name = mock.stdout.readline().rstrip() secretstorage.util.BUS_NAME = bus_name print('Bus name set to %r' % secretstorage.util.BUS_NAME) loader = unittest.TestLoader() runner = unittest.TextTestRunner(verbosity=2) result = runner.run(loader.discover(tests_dir)) if mock is not None: mock.terminate() sys.exit(not result.wasSuccessful()) SecretStorage-2.3.1/tests/cleanup_test_items.py0000755000175000017500000000037212563100302022531 0ustar dmitrydmitry00000000000000#!/usr/bin/env python3 import secretstorage bus = secretstorage.dbus_init() items = secretstorage.search_items(bus, {'application': 'secretstorage-test'}) for item in items: print('Deleting item with label %r.' % item.get_label()) item.delete() SecretStorage-2.3.1/tests/test_exceptions.py0000644000175000017500000000200412563100302022051 0ustar dmitrydmitry00000000000000# Tests for SecretStorage # Author: Dmitry Shachnev, 2013 # License: BSD # Various exception tests import unittest import secretstorage from secretstorage.exceptions import ItemNotFoundException class ExceptionsTest(unittest.TestCase): """A test case that ensures that all SecretStorage exceptions are raised correctly.""" @classmethod def setUpClass(cls): cls.bus = secretstorage.dbus_init(main_loop=False) cls.collection = secretstorage.get_any_collection(cls.bus) def test_double_deleting(self): item = self.collection.create_item('MyItem', {'application': 'secretstorage-test'}, b'pa$$word') item.delete() self.assertRaises(ItemNotFoundException, item.delete) def test_non_existing_item(self): self.assertRaises(ItemNotFoundException, secretstorage.Item, self.bus, '/not/existing/path') def test_non_existing_collection(self): self.assertRaises(ItemNotFoundException, secretstorage.get_collection_by_alias, self.bus, 'non-existing-alias') if __name__ == '__main__': unittest.main() SecretStorage-2.3.1/tests/test_item.py0000644000175000017500000000545312736001613020650 0ustar dmitrydmitry00000000000000# Tests for SecretStorage # Author: Dmitry Shachnev, 2013 # License: BSD # This file tests the secretstorage.Collection class. import unittest import time from secretstorage import dbus_init, search_items, get_any_collection ATTRIBUTES = {'application': 'secretstorage-test', 'attribute': 'qwerty'} NEW_ATTRIBUTES = {'application': 'secretstorage-test', 'newattribute': 'asdfgh'} class ItemTest(unittest.TestCase): """A test case that tests that all common methods of Item class work and do not crash.""" @classmethod def setUpClass(cls): cls.bus = dbus_init(main_loop=False) cls.collection = get_any_collection(cls.bus) cls.created_timestamp = time.time() cls.item = cls.collection.create_item('My item', ATTRIBUTES, b'pa$$word') cls.other_item = cls.collection.create_item('My item', ATTRIBUTES, '', content_type='data/null') def test_equal(self): self.assertEqual(self.item, self.item) self.assertNotEqual(self.item, self.other_item) self.assertEqual(self.other_item, self.other_item) def test_searchable(self): search_results = self.collection.search_items(ATTRIBUTES) self.assertIn(self.item, search_results) search_results = search_items(self.bus, ATTRIBUTES) self.assertIn(self.item, search_results) def test_item_in_all_items(self): all_items = self.collection.get_all_items() self.assertIn(self.item, all_items) def test_attributes(self): attributes = self.item.get_attributes() for key in ATTRIBUTES: self.assertEqual(ATTRIBUTES[key], attributes[key]) self.item.set_attributes(NEW_ATTRIBUTES) attributes = self.item.get_attributes() for key in NEW_ATTRIBUTES: self.assertEqual(NEW_ATTRIBUTES[key], attributes[key]) self.item.set_attributes(ATTRIBUTES) def test_label(self): self.assertEqual(self.item.get_label(), 'My item') self.item.set_label('Hello!') self.assertEqual(self.item.get_label(), 'Hello!') def test_secret(self): self.assertEqual(self.item.get_secret(), b'pa$$word') self.item.set_secret(b'newpa$$word') self.assertIsInstance(self.item.get_secret(), bytes) self.assertEqual(self.item.get_secret(), b'newpa$$word') self.assertEqual(self.other_item.get_secret(), b'') def test_secret_content_type(self): self.assertEqual(self.item.get_secret_content_type(), 'text/plain') # The check below fails in gnome-keyring because it doesn't really # support content types. #self.assertEqual(self.other_item.get_secret_content_type(), 'data/null') def test_modified(self): now = time.time() modified = self.item.get_modified() self.assertAlmostEqual(now, modified, places=-1) def test_created(self): created = self.item.get_created() self.assertAlmostEqual(self.created_timestamp, created, places=-1) @classmethod def tearDownClass(cls): cls.item.delete() cls.other_item.delete() if __name__ == '__main__': unittest.main() SecretStorage-2.3.1/tests/test_dhcrypto.py0000644000175000017500000000102512755122167021546 0ustar dmitrydmitry00000000000000# Tests for SecretStorage # Author: Dmitry Shachnev, 2014 # License: BSD # This file tests the dhcrypto module. import unittest from secretstorage.dhcrypto import int_to_bytes class ConversionTest(unittest.TestCase): """A test case that tests conversion functions between bytes and long.""" def test_int_to_bytes(self): self.assertEqual(int_to_bytes(1), b'\x01') self.assertEqual(int_to_bytes(258), b'\x01\x02') self.assertEqual(int_to_bytes(1 << 64), b'\x01' + b'\x00' * 8) if __name__ == '__main__': unittest.main() SecretStorage-2.3.1/.gitignore0000644000175000017500000000007512611700141017114 0ustar dmitrydmitry00000000000000build dist MANIFEST __pycache__ *.pyc SecretStorage.egg-info SecretStorage-2.3.1/setup.py0000755000175000017500000000237312760252214016654 0ustar dmitrydmitry00000000000000#!/usr/bin/python import os.path try: from setuptools import setup except ImportError: from distutils.core import setup version = '2.3.1' readme_file = open(os.path.join(os.path.dirname(__file__), 'README.rst')) long_description = '\n' + readme_file.read() readme_file.close() classifiers = [ 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: BSD License', 'Operating System :: POSIX', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Security', 'Topic :: Software Development :: Libraries :: Python Modules' ] setup(name='SecretStorage', version=version, description='Python bindings to FreeDesktop.org Secret Service API', long_description=long_description, author='Dmitry Shachnev', author_email='mitya57@gmail.com', url='https://github.com/mitya57/secretstorage', packages=['secretstorage'], platforms='Linux', license='BSD', classifiers=classifiers, install_requires=['cryptography'], extras_require={ 'dbus-python': ['dbus-python'], }, requires=['dbus', 'cryptography'] ) SecretStorage-2.3.1/.travis.yml0000644000175000017500000000116312760251216017245 0ustar dmitrydmitry00000000000000sudo: required dist: trusty language: python python: - "2.7" - "3.3" - "3.4" - "3.5" before_install: - sudo apt-get update - sudo apt-get install -y libdbus-glib-1-dev install: - which python3.3-config && sed -i "s|not getvar('Py_ENABLE_SHARED')|True|" $(which python3.3-config) || true - pip install dbus-python cryptography before_script: - git clone git://git.gnome.org/libsecret.git script: - dbus-launch --exit-with-session python tests/run_tests.py libsecret/libsecret/mock-service-normal.py - dbus-launch --exit-with-session python tests/run_tests.py libsecret/libsecret/mock-service-only-plain.py SecretStorage-2.3.1/MANIFEST.in0000644000175000017500000000016012563100302016654 0ustar dmitrydmitry00000000000000include tests/*.py include LICENSE include README.rst include changelog include docs/*.rst include docs/conf.py