keyrings.alt-1.1.1/0000755000076500000240000000000012665366216014523 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/.gitignore0000644000076500000240000000006412650704627016507 0ustar jaracostaff00000000000000dist/ build/ .eggs/ *.pyc *.egg-info/ .cache/ keyrings.alt-1.1.1/.hgtags0000644000076500000240000000027012665366205015776 0ustar jaracostaff00000000000000b3173ad03e2b87d50ab31eecc5e6e57e9963785d 1.0 f9defd8cbdfabaeb1739fd02929f272ba7e4be73 1.0.1 acdefb2c6c473bf6460004dbbe1f62280fcdeac8 1.1 43b2bf00ab0477d3734932feb05ce6d533df255a 1.1.1 keyrings.alt-1.1.1/.travis.yml0000644000076500000240000000015412645677507016642 0ustar jaracostaff00000000000000sudo: false language: python python: - 2.7 - 3.5 script: - pip install -U pytest - python setup.py test keyrings.alt-1.1.1/CHANGES.rst0000644000076500000240000000027112665366172016326 0ustar jaracostaff000000000000001.1.1 ===== Test cleanup. Exclude tests during install. 1.1 === FileBacked backends now have a ``repr`` that includes the file path. 1.0 === Initial release based on Keyring 7.3. keyrings.alt-1.1.1/conftest.py0000644000076500000240000000020212645732554016714 0ustar jaracostaff00000000000000import platform collect_ignore = [] if platform.system() != 'Windows': collect_ignore.append('keyrings/alt/_win_crypto.py') keyrings.alt-1.1.1/docs/0000755000076500000240000000000012665366216015453 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/docs/conf.py0000644000076500000240000000062212645742642016751 0ustar jaracostaff00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- import setuptools_scm extensions = [ 'sphinx.ext.autodoc', ] # General information about the project. project = 'keyrings.alt' copyright = '2016 Jason R. Coombs' # The short X.Y version. version = setuptools_scm.get_version(root='..', relative_to=__file__) # The full version, including alpha/beta/rc tags. release = version master_doc = 'index' keyrings.alt-1.1.1/docs/history.rst0000644000076500000240000000011112645677507017705 0ustar jaracostaff00000000000000:tocdepth: 2 .. _changes: History ******* .. include:: ../CHANGES.rst keyrings.alt-1.1.1/docs/index.rst0000644000076500000240000000170312645742767017324 0ustar jaracostaff00000000000000Welcome to keyrings.alt documentation! ====================================== .. toctree:: :maxdepth: 1 history .. automodule:: keyrings.alt.file :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.Gnome :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.Google :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.keyczar :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.kwallet :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.multi :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.pyfs :members: :undoc-members: :show-inheritance: .. automodule:: keyrings.alt.Windows :members: :undoc-members: :show-inheritance: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` keyrings.alt-1.1.1/keyrings/0000755000076500000240000000000012665366216016356 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/keyrings/__init__.py0000644000076500000240000000007012645701547020462 0ustar jaracostaff00000000000000__import__("pkg_resources").declare_namespace(__name__) keyrings.alt-1.1.1/keyrings/alt/0000755000076500000240000000000012665366216017136 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/keyrings/alt/__init__.py0000644000076500000240000000000012645701557021234 0ustar jaracostaff00000000000000keyrings.alt-1.1.1/keyrings/alt/_win_crypto.py0000644000076500000240000000662512650704627022051 0ustar jaracostaff00000000000000 from ctypes import Structure, POINTER, c_void_p, cast, create_string_buffer, \ c_char_p, byref, memmove from ctypes import windll, WinDLL, WINFUNCTYPE try: from ctypes import wintypes except ValueError: # see http://bugs.python.org/issue16396 raise ImportError("wintypes") from keyring.util.escape import u # Crypto API ctypes bindings class DATA_BLOB(Structure): _fields_ = [('cbData', wintypes.DWORD), ('pbData', POINTER(wintypes.BYTE))] class CRYPTPROTECT_PROMPTSTRUCT(Structure): _fields_ = [('cbSize', wintypes.DWORD), ('dwPromptFlags', wintypes.DWORD), ('hwndApp', wintypes.HWND), ('szPrompt', POINTER(wintypes.WCHAR))] # Flags for CRYPTPROTECT_PROMPTSTRUCT CRYPTPROTECT_PROMPT_ON_UNPROTECT = 1 CRYPTPROTECT_PROMPT_ON_PROTECT = 2 # Flags for CryptProtectData/CryptUnprotectData CRYPTPROTECT_UI_FORBIDDEN = 0x01 CRYPTPROTECT_LOCAL_MACHINE = 0x04 CRYPTPROTECT_CRED_SYNC = 0x08 CRYPTPROTECT_AUDIT = 0x10 CRYPTPROTECT_NO_RECOVERY = 0x20 CRYPTPROTECT_VERIFY_PROTECTION = 0x40 CRYPTPROTECT_CRED_REGENERATE = 0x80 # Crypto API Functions _dll = WinDLL('CRYPT32.DLL') CryptProtectData = WINFUNCTYPE(wintypes.BOOL, POINTER(DATA_BLOB), POINTER(wintypes.WCHAR), POINTER(DATA_BLOB), c_void_p, POINTER(CRYPTPROTECT_PROMPTSTRUCT), wintypes.DWORD, POINTER(DATA_BLOB))(('CryptProtectData', _dll)) CryptUnprotectData = WINFUNCTYPE(wintypes.BOOL, POINTER(DATA_BLOB), POINTER(wintypes.WCHAR), POINTER(DATA_BLOB), c_void_p, POINTER(CRYPTPROTECT_PROMPTSTRUCT), wintypes.DWORD, POINTER(DATA_BLOB))( ('CryptUnprotectData', _dll)) # Functions def encrypt(data, non_interactive=0): blobin = DATA_BLOB(cbData=len(data), pbData=cast(c_char_p(data), POINTER(wintypes.BYTE))) blobout = DATA_BLOB() if not CryptProtectData(byref(blobin), u('python-keyring-lib.win32crypto'), None, None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blobout)): raise OSError("Can't encrypt") encrypted = create_string_buffer(blobout.cbData) memmove(encrypted, blobout.pbData, blobout.cbData) windll.kernel32.LocalFree(blobout.pbData) return encrypted.raw def decrypt(encrypted, non_interactive=0): blobin = DATA_BLOB(cbData=len(encrypted), pbData=cast(c_char_p(encrypted), POINTER(wintypes.BYTE))) blobout = DATA_BLOB() if not CryptUnprotectData(byref(blobin), u('python-keyring-lib.win32crypto'), None, None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blobout)): raise OSError("Can't decrypt") data = create_string_buffer(blobout.cbData) memmove(data, blobout.pbData, blobout.cbData) windll.kernel32.LocalFree(blobout.pbData) return data.raw keyrings.alt-1.1.1/keyrings/alt/file.py0000644000076500000240000002324012645764203020424 0ustar jaracostaff00000000000000from __future__ import with_statement import os import getpass import base64 import sys import json import abc from keyring.py27compat import configparser from keyring.errors import PasswordDeleteError from keyring.backend import KeyringBackend from keyring.util import platform_, properties from keyring.util.escape import escape as escape_for_ini class FileBacked(object): @abc.abstractproperty def filename(self): """ The filename used to store the passwords. """ @properties.NonDataProperty def file_path(self): """ The path to the file where passwords are stored. This property may be overridden by the subclass or at the instance level. """ return os.path.join(platform_.data_root(), self.filename) def __repr__(self): tmpl = "<{self.__class__.__name__} at {self.file_path}>" return tmpl.format(**locals()) class BaseKeyring(FileBacked, KeyringBackend): """ BaseKeyring is a file-based implementation of keyring. This keyring stores the password directly in the file and provides methods which may be overridden by subclasses to support encryption and decryption. The encrypted payload is stored in base64 format. """ @abc.abstractmethod def encrypt(self, password): """ Given a password (byte string), return an encrypted byte string. """ @abc.abstractmethod def decrypt(self, password_encrypted): """ Given a password encrypted by a previous call to `encrypt`, return the original byte string. """ def get_password(self, service, username): """ Read the password from the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # load the passwords from the file config = configparser.RawConfigParser() if os.path.exists(self.file_path): config.read(self.file_path) # fetch the password try: password_base64 = config.get(service, username).encode() # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = self.decrypt(password_encrypted).decode('utf-8') except (configparser.NoOptionError, configparser.NoSectionError): password = None return password def set_password(self, service, username, password): """Write the password in the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # encrypt the password password_encrypted = self.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted).decode() # ensure the file exists self._ensure_file_path() # load the keyring from the disk config = configparser.RawConfigParser() config.read(self.file_path) # update the keyring with the password if not config.has_section(service): config.add_section(service) config.set(service, username, password_base64) # save the keyring back to the file with open(self.file_path, 'w') as config_file: config.write(config_file) def _ensure_file_path(self): """ Ensure the storage path exists. If it doesn't, create it with "go-rwx" permissions. """ storage_root = os.path.dirname(self.file_path) if storage_root and not os.path.isdir(storage_root): os.makedirs(storage_root) if not os.path.isfile(self.file_path): # create the file without group/world permissions with open(self.file_path, 'w'): pass user_read_write = 0o600 os.chmod(self.file_path, user_read_write) def delete_password(self, service, username): """Delete the password for the username of the service. """ service = escape_for_ini(service) username = escape_for_ini(username) config = configparser.RawConfigParser() if os.path.exists(self.file_path): config.read(self.file_path) try: if not config.remove_option(service, username): raise PasswordDeleteError("Password not found") except configparser.NoSectionError: raise PasswordDeleteError("Password not found") # update the file with open(self.file_path, 'w') as config_file: config.write(config_file) class PlaintextKeyring(BaseKeyring): """Simple File Keyring with no encryption""" priority = .5 "Applicable for all platforms, but not recommended" filename = 'keyring_pass.cfg' def encrypt(self, password): """Directly return the password itself. """ return password def decrypt(self, password_encrypted): """Directly return encrypted password. """ return password_encrypted class Encrypted(object): """ PyCrypto-backed Encryption support """ block_size = 32 def _create_cipher(self, password, salt, IV): """ Create the cipher object to encrypt or decrypt a payload. """ from Crypto.Protocol.KDF import PBKDF2 from Crypto.Cipher import AES pw = PBKDF2(password, salt, dkLen=self.block_size) return AES.new(pw[:self.block_size], AES.MODE_CFB, IV) def _get_new_password(self): while True: password = getpass.getpass( "Please set a password for your new keyring: ") confirm = getpass.getpass('Please confirm the password: ') if password != confirm: sys.stderr.write("Error: Your passwords didn't match\n") continue if '' == password.strip(): # forbid the blank password sys.stderr.write("Error: blank passwords aren't allowed.\n") continue return password class EncryptedKeyring(Encrypted, BaseKeyring): """PyCrypto File Keyring""" filename = 'crypted_pass.cfg' pw_prefix = 'pw:'.encode() @properties.ClassProperty @classmethod def priority(self): "Applicable for all platforms, but not recommended." try: __import__('Crypto.Cipher.AES') __import__('Crypto.Protocol.KDF') __import__('Crypto.Random') except ImportError: raise RuntimeError("PyCrypto required") if not json: raise RuntimeError("JSON implementation such as simplejson " "required.") return .6 @properties.NonDataProperty def keyring_key(self): # _unlock or _init_file will set the key or raise an exception if self._check_file(): self._unlock() else: self._init_file() return self.keyring_key def _init_file(self): """ Initialize a new password file and set the reference password. """ self.keyring_key = self._get_new_password() # set a reference password, used to check that the password provided # matches for subsequent checks. self.set_password('keyring-setting', 'password reference', 'password reference value') def _check_file(self): """ Check if the file exists and has the expected password reference. """ if not os.path.exists(self.file_path): return False self._migrate() config = configparser.RawConfigParser() config.read(self.file_path) try: config.get( escape_for_ini('keyring-setting'), escape_for_ini('password reference'), ) except (configparser.NoSectionError, configparser.NoOptionError): return False return True def _unlock(self): """ Unlock this keyring by getting the password for the keyring from the user. """ self.keyring_key = getpass.getpass( 'Please enter password for encrypted keyring: ') try: ref_pw = self.get_password('keyring-setting', 'password reference') assert ref_pw == 'password reference value' except AssertionError: self._lock() raise ValueError("Incorrect Password") def _lock(self): """ Remove the keyring key from this instance. """ del self.keyring_key def encrypt(self, password): from Crypto.Random import get_random_bytes salt = get_random_bytes(self.block_size) from Crypto.Cipher import AES IV = get_random_bytes(AES.block_size) cipher = self._create_cipher(self.keyring_key, salt, IV) password_encrypted = cipher.encrypt(self.pw_prefix + password) # Serialize the salt, IV, and encrypted password in a secure format data = dict( salt=salt, IV=IV, password_encrypted=password_encrypted, ) for key in data: data[key] = base64.encodestring(data[key]).decode() return json.dumps(data).encode() def decrypt(self, password_encrypted): # unpack the encrypted payload data = json.loads(password_encrypted.decode()) for key in data: data[key] = base64.decodestring(data[key].encode()) cipher = self._create_cipher(self.keyring_key, data['salt'], data['IV']) plaintext = cipher.decrypt(data['password_encrypted']) assert plaintext.startswith(self.pw_prefix) return plaintext[3:] def _migrate(self, keyring_password=None): """ Convert older keyrings to the current format. """ keyrings.alt-1.1.1/keyrings/alt/Gnome.py0000644000076500000240000001051312645701557020554 0ustar jaracostaff00000000000000try: import gi gi.require_version('GnomeKeyring', '1.0') from gi.repository import GnomeKeyring except (ImportError, ValueError): pass from keyring.backend import KeyringBackend from keyring.errors import PasswordSetError, PasswordDeleteError from keyring.util import properties from keyring.py27compat import unicode_str class Keyring(KeyringBackend): """Gnome Keyring""" KEYRING_NAME = None """ Name of the keyring in which to store the passwords. Use None for the default keyring. """ @properties.ClassProperty @classmethod def priority(cls): if 'GnomeKeyring' not in globals(): raise RuntimeError("GnomeKeyring module required") result = GnomeKeyring.get_default_keyring_sync()[0] if result != GnomeKeyring.Result.OK: raise RuntimeError(result.value_name) return 1 @property def keyring_name(self): system_default = GnomeKeyring.get_default_keyring_sync()[1] return self.KEYRING_NAME or system_default def _find_passwords(self, service, username, deleting=False): """Get password of the username for the service """ passwords = [] service = self._safe_string(service) username = self._safe_string(username) for attrs_tuple in (('username', 'service'), ('user', 'domain')): attrs = GnomeKeyring.Attribute.list_new() GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[0], username) GnomeKeyring.Attribute.list_append_string(attrs, attrs_tuple[1], service) result, items = GnomeKeyring.find_items_sync( GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) if result == GnomeKeyring.Result.OK: passwords += items elif deleting: if result == GnomeKeyring.Result.CANCELLED: raise PasswordDeleteError("Cancelled by user") elif result != GnomeKeyring.Result.NO_MATCH: raise PasswordDeleteError(result.value_name) return passwords def get_password(self, service, username): """Get password of the username for the service """ items = self._find_passwords(service, username) if not items: return None secret = items[0].secret return secret if isinstance(secret, unicode_str) else secret.decode('utf-8') def set_password(self, service, username, password): """Set password for the username of the service """ service = self._safe_string(service) username = self._safe_string(username) password = self._safe_string(password) attrs = GnomeKeyring.Attribute.list_new() GnomeKeyring.Attribute.list_append_string(attrs, 'username', username) GnomeKeyring.Attribute.list_append_string(attrs, 'service', service) GnomeKeyring.Attribute.list_append_string(attrs, 'application', 'python-keyring') result = GnomeKeyring.item_create_sync( self.keyring_name, GnomeKeyring.ItemType.NETWORK_PASSWORD, "Password for '%s' on '%s'" % (username, service), attrs, password, True)[0] if result == GnomeKeyring.Result.CANCELLED: # The user pressed "Cancel" when prompted to unlock their keyring. raise PasswordSetError("Cancelled by user") elif result != GnomeKeyring.Result.OK: raise PasswordSetError(result.value_name) def delete_password(self, service, username): """Delete the password for the username of the service. """ items = self._find_passwords(service, username, deleting=True) if not items: raise PasswordDeleteError("Password not found") for current in items: result = GnomeKeyring.item_delete_sync(current.keyring, current.item_id) if result == GnomeKeyring.Result.CANCELLED: raise PasswordDeleteError("Cancelled by user") elif result != GnomeKeyring.Result.OK: raise PasswordDeleteError(result.value_name) def _safe_string(self, source, encoding='utf-8'): """Convert unicode to string as gnomekeyring barfs on unicode""" if not isinstance(source, str): return source.encode(encoding) return str(source) keyrings.alt-1.1.1/keyrings/alt/Google.py0000644000076500000240000003010312645701557020720 0ustar jaracostaff00000000000000from __future__ import absolute_import import os import sys import copy import codecs import base64 import io try: import gdata.docs.service except ImportError: pass from . import keyczar from keyring import errors from keyring import credentials from keyring.py27compat import input, pickle from keyring.backend import KeyringBackend from keyring.util import properties from keyring.errors import ExceptionRaisedContext class EnvironCredential(credentials.EnvironCredential): """Retrieve credentials from specifically named environment variables """ def __init__(self): super(EnvironCredential, self).__init__('GOOGLE_KEYRING_USER', 'GOOGLE_KEYRING_PASSWORD') class DocsKeyring(KeyringBackend): """Backend that stores keyring on Google Docs. Note that login and any other initialisation is deferred until it is actually required to allow this keyring class to be added to the global _all_keyring list. """ keyring_title = 'GoogleKeyring' # status enums OK = 1 FAIL = 0 CONFLICT = -1 def __init__(self, credential, source, crypter, collection=None, client=None, can_create=True, input_getter=input ): self.credential = credential self.crypter = crypter self.source = source self._collection = collection self.can_create = can_create self.input_getter = input_getter self._keyring_dict = None if not client: self._client = gdata.docs.service.DocsService() else: self._client = client self._client.source = source self._client.ssl = True self._login_reqd = True @properties.ClassProperty @classmethod def priority(cls): if not cls._has_gdata(): raise RuntimeError("Requires gdata") if not keyczar.has_keyczar(): raise RuntimeError("Requires keyczar") return 3 @classmethod def _has_gdata(cls): with ExceptionRaisedContext() as exc: gdata.__name__ return not bool(exc) def get_password(self, service, username): """Get password of the username for the service """ result = self._get_entry(self._keyring, service, username) if result: result = self._decrypt(result) return result def set_password(self, service, username, password): """Set password for the username of the service """ password = self._encrypt(password or '') keyring_working_copy = copy.deepcopy(self._keyring) service_entries = keyring_working_copy.get(service) if not service_entries: service_entries = {} keyring_working_copy[service] = service_entries service_entries[username] = password save_result = self._save_keyring(keyring_working_copy) if save_result == self.OK: self._keyring_dict = keyring_working_copy return elif save_result == self.CONFLICT: # check if we can avoid updating self.docs_entry, keyring_dict = self._read() existing_pwd = self._get_entry(self._keyring, service, username) conflicting_pwd = self._get_entry(keyring_dict, service, username) if conflicting_pwd == password: # if someone else updated it to the same value then we are done self._keyring_dict = keyring_working_copy return elif conflicting_pwd is None or conflicting_pwd == existing_pwd: # if doesn't already exist or is unchanged then update it new_service_entries = keyring_dict.get(service, {}) new_service_entries[username] = password keyring_dict[service] = new_service_entries save_result = self._save_keyring(keyring_dict) if save_result == self.OK: self._keyring_dict = keyring_dict return else: raise errors.PasswordSetError( 'Failed write after conflict detected') else: raise errors.PasswordSetError( 'Conflict detected, service:%s and username:%s was '\ 'set to a different value by someone else' %(service, username)) raise errors.PasswordSetError('Could not save keyring') def delete_password(self, service, username): return self._del_entry(self._keyring, service, username) @property def client(self): if not self._client.GetClientLoginToken(): try: self._client.ClientLogin(self.credential.username, self.credential.password, self._client.source) except gdata.service.CaptchaRequired: sys.stdout.write('Please visit ' + self._client.captcha_url) answer = self.input_getter('Answer to the challenge? ') self._client.email = self.credential.username self._client.password = self.credential.password self._client.ClientLogin( self.credential.username, self.credential.password, self._client.source, captcha_token=self._client.captcha_token, captcha_response=answer) except gdata.service.BadAuthentication: raise errors.InitError('Users credential were unrecognized') except gdata.service.Error: raise errors.InitError('Login Error') return self._client @property def collection(self): return self._collection or self.credential.username.split('@')[0] @property def _keyring(self): if self._keyring_dict is None: self.docs_entry, self._keyring_dict = self._read() return self._keyring_dict def _get_entry(self, keyring_dict, service, username): result = None service_entries = keyring_dict.get(service) if service_entries: result = service_entries.get(username) return result def _del_entry(self, keyring_dict, service, username): service_entries = keyring_dict.get(service) if not service_entries: raise errors.PasswordDeleteError("No matching service") try: del service_entries[username] except KeyError: raise errors.PasswordDeleteError("Not found") if not service_entries: del keyring_dict[service] def _decrypt(self, value): if not value: return '' return self.crypter.decrypt(value) def _encrypt(self, value): if not value: return '' return self.crypter.encrypt(value) def _get_doc_title(self): return '%s' %self.keyring_title def _read(self): from gdata.docs.service import DocumentQuery title_query = DocumentQuery(categories=[self.collection]) title_query['title'] = self._get_doc_title() title_query['title-exact'] = 'true' docs = self.client.QueryDocumentListFeed(title_query.ToUri()) if not docs.entry: if self.can_create: docs_entry = None keyring_dict = {} else: raise errors.InitError( '%s not found in %s and create not permitted' %(self._get_doc_title(), self.collection)) else: docs_entry = docs.entry[0] file_contents = '' try: url = docs_entry.content.src url += '&exportFormat=txt' server_response = self.client.request('GET', url) if server_response.status != 200: raise errors.InitError( 'Could not read existing Google Docs keyring') file_contents = server_response.read() if file_contents.startswith(codecs.BOM_UTF8): file_contents = file_contents[len(codecs.BOM_UTF8):] keyring_dict = pickle.loads(base64.urlsafe_b64decode( file_contents.decode('string-escape'))) except pickle.UnpicklingError as ex: raise errors.InitError( 'Could not unpickle existing Google Docs keyring', ex) except TypeError as ex: raise errors.InitError( 'Could not decode existing Google Docs keyring', ex) return docs_entry, keyring_dict def _save_keyring(self, keyring_dict): """Helper to actually write the keyring to Google""" import gdata result = self.OK file_contents = base64.urlsafe_b64encode(pickle.dumps(keyring_dict)) try: if self.docs_entry: extra_headers = {'Content-Type': 'text/plain', 'Content-Length': len(file_contents)} self.docs_entry = self.client.Put( file_contents, self.docs_entry.GetEditMediaLink().href, extra_headers=extra_headers ) else: from gdata.docs.service import DocumentQuery # check for existence of folder, create if required folder_query = DocumentQuery(categories=['folder']) folder_query['title'] = self.collection folder_query['title-exact'] = 'true' docs = self.client.QueryDocumentListFeed(folder_query.ToUri()) if docs.entry: folder_entry = docs.entry[0] else: folder_entry = self.client.CreateFolder(self.collection) file_handle = io.BytesIO(file_contents) media_source = gdata.MediaSource( file_handle=file_handle, content_type='text/plain', content_length=len(file_contents), file_name='temp') self.docs_entry = self.client.Upload( media_source, self._get_doc_title(), folder_or_uri=folder_entry ) except gdata.service.RequestError as ex: try: if ex.message['reason'].lower().find('conflict') != -1: result = self.CONFLICT else: # Google docs has a bug when updating a shared document # using PUT from any account other that the owner. # It returns an error 400 "Sorry, there was an error saving the file. Please try again" # *despite* actually updating the document! # Workaround by re-reading to see if it actually updated if ex.message['body'].find( 'Sorry, there was an error saving the file') != -1: new_docs_entry, new_keyring_dict = self._read() if new_keyring_dict == keyring_dict: result = self.OK else: result = self.FAIL else: result = self.FAIL except: result = self.FAIL return result class KeyczarDocsKeyring(DocsKeyring): """Google Docs keyring using keyczar initialized from environment variables """ def __init__(self): crypter = keyczar.EnvironCrypter() credential = EnvironCredential() source = os.environ.get('GOOGLE_KEYRING_SOURCE') super(KeyczarDocsKeyring, self).__init__( credential, source, crypter) def supported(self): """Return if this keyring supports current environment: -1: not applicable 0: suitable 1: recommended """ try: from keyczar import keyczar return super(KeyczarDocsKeyring, self).supported() except ImportError: return -1 keyrings.alt-1.1.1/keyrings/alt/keyczar.py0000644000076500000240000000560512645701557021165 0ustar jaracostaff00000000000000from __future__ import absolute_import import os import abc try: from keyczar import keyczar except ImportError: pass from keyring.backend import Crypter from keyring import errors def has_keyczar(): with errors.ExceptionRaisedContext() as exc: keyczar.__name__ return not bool(exc) class BaseCrypter(Crypter): """Base Keyczar keyset encryption and decryption. The keyset initialisation is deferred until required. """ @abc.abstractproperty def keyset_location(self): """Location for the main keyset that may be encrypted or not""" pass @abc.abstractproperty def encrypting_keyset_location(self): """Location for the encrypting keyset. Use None to indicate that the main keyset is not encrypted """ pass @property def crypter(self): """The actual keyczar crypter""" if not hasattr(self, '_crypter'): # initialise the Keyczar keysets if not self.keyset_location: raise ValueError('No encrypted keyset location!') reader = keyczar.readers.CreateReader(self.keyset_location) if self.encrypting_keyset_location: encrypting_keyczar = keyczar.Crypter.Read( self.encrypting_keyset_location) reader = keyczar.readers.EncryptedReader(reader, encrypting_keyczar) self._crypter = keyczar.Crypter(reader) return self._crypter def encrypt(self, value): """Encrypt the value. """ if not value: return '' return self.crypter.Encrypt(value) def decrypt(self, value): """Decrypt the value. """ if not value: return '' return self.crypter.Decrypt(value) class Crypter(BaseCrypter): """A Keyczar crypter using locations specified in the constructor """ def __init__(self, keyset_location, encrypting_keyset_location=None): self._keyset_location = keyset_location self._encrypting_keyset_location = encrypting_keyset_location @property def keyset_location(self): return self._keyset_location @property def encrypting_keyset_location(self): return self._encrypting_keyset_location class EnvironCrypter(BaseCrypter): """A Keyczar crypter using locations specified by environment vars """ KEYSET_ENV_VAR = 'KEYRING_KEYCZAR_ENCRYPTED_LOCATION' ENC_KEYSET_ENV_VAR = 'KEYRING_KEYCZAR_ENCRYPTING_LOCATION' @property def keyset_location(self): val = os.environ.get(self.KEYSET_ENV_VAR) if not val: raise ValueError('%s environment value not set' % self.KEYSET_ENV_VAR) return val @property def encrypting_keyset_location(self): return os.environ.get(self.ENC_KEYSET_ENV_VAR) keyrings.alt-1.1.1/keyrings/alt/kwallet.py0000644000076500000240000000747612645701557021170 0ustar jaracostaff00000000000000from __future__ import absolute_import import os import sys from keyring.py27compat import unicode_str from keyring.backend import KeyringBackend from keyring.errors import PasswordDeleteError from keyring.errors import PasswordSetError, ExceptionRaisedContext from keyring.util import properties # mixing Qt4 & Qt5 causes errors and may segfault if 'PyQt5' not in sys.modules: try: from PyKDE4.kdeui import KWallet from PyQt4 import QtGui except ImportError: pass kwallet = None def open_kwallet(kwallet_module=None, qt_module=None): # If we specified the kwallet_module and/or qt_module, surely we won't need # the cached kwallet object... if kwallet_module is None and qt_module is None: global kwallet if not kwallet is None: return kwallet # Allow for the injection of module-like objects for testing purposes. if kwallet_module is None: kwallet_module = KWallet.Wallet if qt_module is None: qt_module = QtGui # KDE wants us to instantiate an application object. app = None if qt_module.qApp.instance() == None: app = qt_module.QApplication([]) try: window = qt_module.QWidget() kwallet = kwallet_module.openWallet( kwallet_module.NetworkWallet(), window.winId(), kwallet_module.Synchronous) if kwallet is not None: if not kwallet.hasFolder('Python'): kwallet.createFolder('Python') kwallet.setFolder('Python') return kwallet finally: if app: app.exit() class QtKeyring(KeyringBackend): """KDE KWallet""" @properties.ClassProperty @classmethod def priority(cls): with ExceptionRaisedContext() as exc: KWallet.__name__ if exc: raise RuntimeError("KDE libraries not available") if "DISPLAY" not in os.environ: raise RuntimeError("cannot connect to X server") # Infer if KDE environment is active based on environment vars. # TODO: Does PyKDE provide a better indicator? kde_session_keys = ( 'KDE_SESSION_ID', # most environments 'KDE_FULL_SESSION', # openSUSE ) if not set(os.environ).intersection(kde_session_keys): return 0 return 5 def get_password(self, service, username): """Get password of the username for the service """ key = username + '@' + service network = KWallet.Wallet.NetworkWallet() wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. return None if wallet.keyDoesNotExist(network, 'Python', key): return None result = wallet.readPassword(key)[1] # The string will be a PyQt4.QtCore.QString, so turn it into a unicode # object. return unicode_str(result) def set_password(self, service, username, password): """Set password for the username of the service """ wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordSetError("Cancelled by user") wallet.writePassword(username+'@'+service, password) def delete_password(self, service, username): """Delete the password for the username of the service. """ key = username + '@' + service wallet = open_kwallet() if wallet is None: # the user pressed "cancel" when prompted to unlock their keyring. raise PasswordDeleteError("Cancelled by user") if wallet.keyDoesNotExist(wallet.walletName(), 'Python', key): raise PasswordDeleteError("Password not found") wallet.removeEntry(key) keyrings.alt-1.1.1/keyrings/alt/multi.py0000644000076500000240000000421312645701557020641 0ustar jaracostaff00000000000000import itertools from keyring.util import properties from keyring.backend import KeyringBackend from keyring import errors class MultipartKeyringWrapper(KeyringBackend): """A wrapper around an existing keyring that breaks the password into smaller parts to handle implementations that have limits on the maximum length of passwords i.e. Windows Vault """ def __init__(self, keyring, max_password_size=512): self._keyring = keyring self._max_password_size = max_password_size @properties.ClassProperty @classmethod def priority(cls): return 0 def get_password(self, service, username): """Get password of the username for the service """ init_part = self._keyring.get_password(service, username) if init_part: parts = [init_part,] i = 1 while True: next_part = self._keyring.get_password( service, '%s{{part_%d}}' %(username, i)) if next_part: parts.append(next_part) i += 1 else: break return ''.join(parts) return None def set_password(self, service, username, password): """Set password for the username of the service """ segments = range(0, len(password), self._max_password_size) password_parts = [ password[i:i + self._max_password_size] for i in segments] for i, password_part in enumerate(password_parts): curr_username = username if i > 0: curr_username += '{{part_%d}}' %i self._keyring.set_password(service, curr_username, password_part) def delete_password(self, service, username): self._keyring.delete_password(service, username) count = itertools.count(1) while True: part_name = '%(username)s{{part_%(index)d}}' % dict( index = next(count), **vars()) try: self._keyring.delete_password(service, part_name) except errors.PasswordDeleteError: break keyrings.alt-1.1.1/keyrings/alt/pyfs.py0000644000076500000240000002320112645701557020466 0ustar jaracostaff00000000000000import os import base64 import sys from keyring.py27compat import configparser from keyring import errors from keyring.util.escape import escape as escape_for_ini from keyring.util import platform_, properties from keyring.backend import KeyringBackend, NullCrypter from . import keyczar try: import fs.opener import fs.osfs import fs.errors import fs.path import fs.remote except ImportError: pass def has_pyfs(): """ Does this environment have pyfs installed? Should return False even when Mercurial's Demand Import allowed import of fs.*. """ with errors.ExceptionRaisedContext() as exc: fs.__name__ return not bool(exc) class BasicKeyring(KeyringBackend): """BasicKeyring is a Pyfilesystem-based implementation of keyring. It stores the password directly in the file, and supports encryption and decryption. The encrypted password is stored in base64 format. Being based on Pyfilesystem the file can be local or network-based and served by any of the filesystems supported by Pyfilesystem including Amazon S3, FTP, WebDAV, memory and more. """ _filename = 'keyring_pyf_pass.cfg' def __init__(self, crypter, filename=None, can_create=True, cache_timeout=None): super(BasicKeyring, self).__init__() self._crypter = crypter def_fn = os.path.join(platform_.data_root(), self.__class__._filename) self._filename = filename or def_fn self._can_create = can_create self._cache_timeout = cache_timeout @properties.NonDataProperty def file_path(self): """ The path to the file where passwords are stored. This property may be overridden by the subclass or at the instance level. """ return os.path.join(platform_.data_root(), self.filename) @property def filename(self): """The filename used to store the passwords. """ return self._filename def encrypt(self, password): """Encrypt the password. """ if not password or not self._crypter: return password or b'' return self._crypter.encrypt(password) def decrypt(self, password_encrypted): """Decrypt the password. """ if not password_encrypted or not self._crypter: return password_encrypted or b'' return self._crypter.decrypt(password_encrypted) def _open(self, mode='r'): """Open the password file in the specified mode """ open_file = None writeable = 'w' in mode or 'a' in mode or '+' in mode try: # NOTE: currently the MemOpener does not split off any filename # which causes errors on close() # so we add a dummy name and open it separately if (self.filename.startswith('mem://') or self.filename.startswith('ram://')): open_file = fs.opener.fsopendir(self.filename).open('kr.cfg', mode) else: if not hasattr(self, '_pyfs'): # reuse the pyfilesystem and path self._pyfs, self._path = fs.opener.opener.parse(self.filename, writeable=writeable) # cache if permitted if self._cache_timeout is not None: self._pyfs = fs.remote.CacheFS( self._pyfs, cache_timeout=self._cache_timeout) open_file = self._pyfs.open(self._path, mode) except fs.errors.ResourceNotFoundError: if self._can_create: segments = fs.opener.opener.split_segments(self.filename) if segments: # this seems broken, but pyfilesystem uses it, so we must fs_name, credentials, url1, url2, path = segments.groups() assert fs_name, 'Should be a remote filesystem' host = '' # allow for domain:port if ':' in url2: split_url2 = url2.split('/', 1) if len(split_url2) > 1: url2 = split_url2[1] else: url2 = '' host = split_url2[0] pyfs = fs.opener.opener.opendir('%s://%s' %(fs_name, host)) # cache if permitted if self._cache_timeout is not None: pyfs = fs.remote.CacheFS( pyfs, cache_timeout=self._cache_timeout) # NOTE: fs.path.split does not function in the same way os os.path.split... at least under windows url2_path, url2_filename = os.path.split(url2) if url2_path and not pyfs.exists(url2_path): pyfs.makedir(url2_path, recursive=True) else: # assume local filesystem full_url = fs.opener._expand_syspath(self.filename) # NOTE: fs.path.split does not function in the same way os os.path.split... at least under windows url2_path, url2 = os.path.split(full_url) pyfs = fs.osfs.OSFS(url2_path) try: # reuse the pyfilesystem and path self._pyfs = pyfs self._path = url2 return pyfs.open(url2, mode) except fs.errors.ResourceNotFoundError: if writeable: raise else: pass # NOTE: ignore read errors as the underlying caller can fail safely if writeable: raise else: pass return open_file @property def config(self): """load the passwords from the config file """ if not hasattr(self, '_config'): raw_config = configparser.RawConfigParser() f = self._open() if f: raw_config.readfp(f) f.close() self._config = raw_config return self._config def get_password(self, service, username): """Read the password from the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # fetch the password try: password_base64 = self.config.get(service, username).encode() # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = self.decrypt(password_encrypted).decode('utf-8') except (configparser.NoOptionError, configparser.NoSectionError): password = None return password def set_password(self, service, username, password): """Write the password in the file. """ service = escape_for_ini(service) username = escape_for_ini(username) # encrypt the password password = password or '' password_encrypted = self.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted).decode() # write the modification if not self.config.has_section(service): self.config.add_section(service) self.config.set(service, username, password_base64) config_file = UnicodeWriterAdapter(self._open('w')) self.config.write(config_file) config_file.close() def delete_password(self, service, username): service = escape_for_ini(service) username = escape_for_ini(username) try: self.config.remove_option(service, username) except configparser.NoSectionError: raise errors.PasswordDeleteError('Password not found') config_file = UnicodeWriterAdapter(self._open('w')) self.config.write(config_file) config_file.close() @properties.ClassProperty @classmethod def priority(cls): if not has_pyfs(): raise RuntimeError("pyfs required") return 2 class UnicodeWriterAdapter(object): """ Wrap an object with a .write method to accept 'str' on Python 2 and make it a Unicode string. """ def __init__(self, orig): self._orig = orig def __getattr__(self, *args, **kwargs): return getattr(self._orig, *args, **kwargs) def write(self, value): if isinstance(value, str): value = value.decode('ascii') return self._orig.write(value) if sys.version_info > (3,): UnicodeWriterAdapter = lambda x: x class PlaintextKeyring(BasicKeyring): """Unencrypted Pyfilesystem Keyring """ def __init__(self, filename=None, can_create=True, cache_timeout=None): super(PlaintextKeyring, self).__init__( NullCrypter(), filename=filename, can_create=can_create, cache_timeout=cache_timeout) class EncryptedKeyring(BasicKeyring): """Encrypted Pyfilesystem Keyring """ _filename = 'crypted_pyf_pass.cfg' def __init__(self, crypter, filename=None, can_create=True, cache_timeout=None): super(EncryptedKeyring, self).__init__( crypter, filename=filename, can_create=can_create, cache_timeout=cache_timeout) class KeyczarKeyring(EncryptedKeyring): """Encrypted Pyfilesystem Keyring using Keyczar keysets specified in environment vars """ def __init__(self): super(KeyczarKeyring, self).__init__( keyczar.EnvironCrypter()) keyrings.alt-1.1.1/keyrings/alt/Windows.py0000644000076500000240000001330312645701557021141 0ustar jaracostaff00000000000000import sys import base64 import platform import functools from keyring.util import properties from keyring.backend import KeyringBackend from keyring.errors import PasswordDeleteError, ExceptionRaisedContext from . import file try: # prefer pywin32-ctypes from win32ctypes import pywintypes from win32ctypes import win32cred # force demand import to raise ImportError win32cred.__name__ except ImportError: # fallback to pywin32 try: import pywintypes import win32cred except ImportError: pass try: import winreg except ImportError: try: # Python 2 compatibility import _winreg as winreg except ImportError: pass try: from . import _win_crypto except ImportError: pass def has_pywin32(): """ Does this environment have pywin32? Should return False even when Mercurial's Demand Import allowed import of win32cred. """ with ExceptionRaisedContext() as exc: win32cred.__name__ return not bool(exc) def has_wincrypto(): """ Does this environment have wincrypto? Should return False even when Mercurial's Demand Import allowed import of _win_crypto, so accesses an attribute of the module. """ with ExceptionRaisedContext() as exc: _win_crypto.__name__ return not bool(exc) class EncryptedKeyring(file.BaseKeyring): """ A File-based keyring secured by Windows Crypto API. """ @properties.ClassProperty @classmethod def priority(self): """ Preferred over file.EncryptedKeyring but not other, more sophisticated Windows backends. """ if not platform.system() == 'Windows': raise RuntimeError("Requires Windows") return .8 filename = 'wincrypto_pass.cfg' def encrypt(self, password): """Encrypt the password using the CryptAPI. """ return _win_crypto.encrypt(password) def decrypt(self, password_encrypted): """Decrypt the password using the CryptAPI. """ return _win_crypto.decrypt(password_encrypted) class RegistryKeyring(KeyringBackend): """ RegistryKeyring is a keyring which use Windows CryptAPI to encrypt the user's passwords and store them under registry keys """ @properties.ClassProperty @classmethod def priority(self): """ Preferred on Windows when pywin32 isn't installed """ if platform.system() != 'Windows': raise RuntimeError("Requires Windows") if not has_wincrypto(): raise RuntimeError("Requires ctypes") return 2 def get_password(self, service, username): """Get password of the username for the service """ try: # fetch the password key = r'Software\%s\Keyring' % service hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key) password_saved = winreg.QueryValueEx(hkey, username)[0] password_base64 = password_saved.encode('ascii') # decode with base64 password_encrypted = base64.decodestring(password_base64) # decrypted the password password = _win_crypto.decrypt(password_encrypted).decode('utf-8') except EnvironmentError: password = None return password def set_password(self, service, username, password): """Write the password to the registry """ # encrypt the password password_encrypted = _win_crypto.encrypt(password.encode('utf-8')) # encode with base64 password_base64 = base64.encodestring(password_encrypted) # encode again to unicode password_saved = password_base64.decode('ascii') # store the password key_name = r'Software\%s\Keyring' % service hkey = winreg.CreateKey(winreg.HKEY_CURRENT_USER, key_name) winreg.SetValueEx(hkey, username, 0, winreg.REG_SZ, password_saved) def delete_password(self, service, username): """Delete the password for the username of the service. """ try: key_name = r'Software\%s\Keyring' % service hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0, winreg.KEY_ALL_ACCESS) winreg.DeleteValue(hkey, username) winreg.CloseKey(hkey) except WindowsError: e = sys.exc_info()[1] raise PasswordDeleteError(e) self._delete_key_if_empty(service) def _delete_key_if_empty(self, service): key_name = r'Software\%s\Keyring' % service key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0, winreg.KEY_ALL_ACCESS) try: winreg.EnumValue(key, 0) return except WindowsError: pass winreg.CloseKey(key) # it's empty; delete everything while key_name != 'Software': parent, sep, base = key_name.rpartition('\\') key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, parent, 0, winreg.KEY_ALL_ACCESS) winreg.DeleteKey(key, base) winreg.CloseKey(key) key_name = parent class OldPywinError(object): """ A compatibility wrapper for old PyWin32 errors, such as reported in https://bitbucket.org/kang/python-keyring-lib/issue/140/ """ def __init__(self, orig): self.orig = orig @property def funcname(self): return self.orig[1] @property def winerror(self): return self.orig[0] @classmethod def wrap(cls, orig_err): attr_check = functools.partial(hasattr, orig_err) is_old = not all(map(attr_check, ['funcname', 'winerror'])) return cls(orig_err) if is_old else orig_err keyrings.alt-1.1.1/keyrings.alt.egg-info/0000755000076500000240000000000012665366216020627 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/keyrings.alt.egg-info/dependency_links.txt0000644000076500000240000000000112665366216024675 0ustar jaracostaff00000000000000 keyrings.alt-1.1.1/keyrings.alt.egg-info/entry_points.txt0000644000076500000240000000037412665366216024131 0ustar jaracostaff00000000000000[keyring.backends] Gnome = keyrings.alt.Gnome Google = keyrings.alt.Google Windows (alt) = keyrings.alt.Windows file = keyrings.alt.file keyczar = keyrings.alt.keyczar kwallet = keyrings.alt.kwallet multi = keyrings.alt.multi pyfs = keyrings.alt.pyfs keyrings.alt-1.1.1/keyrings.alt.egg-info/namespace_packages.txt0000644000076500000240000000001112665366216025152 0ustar jaracostaff00000000000000keyrings keyrings.alt-1.1.1/keyrings.alt.egg-info/PKG-INFO0000644000076500000240000000126012665366216021723 0ustar jaracostaff00000000000000Metadata-Version: 1.1 Name: keyrings.alt Version: 1.1.1 Summary: Alternate keyring implementations Home-page: https://github.com/jaraco/keyrings.alt Author: Jason R. Coombs Author-email: jaraco@jaraco.com License: UNKNOWN Description: keyrings.alt ============ Alternate keyring backend implementations for use with the `keyring package `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 keyrings.alt-1.1.1/keyrings.alt.egg-info/SOURCES.txt0000644000076500000240000000151212665366216022512 0ustar jaracostaff00000000000000.gitignore .hgtags .travis.yml CHANGES.rst README.rst conftest.py pytest.ini setup.cfg setup.py docs/conf.py docs/history.rst docs/index.rst keyrings/__init__.py keyrings.alt.egg-info/PKG-INFO keyrings.alt.egg-info/SOURCES.txt keyrings.alt.egg-info/dependency_links.txt keyrings.alt.egg-info/entry_points.txt keyrings.alt.egg-info/namespace_packages.txt keyrings.alt.egg-info/top_level.txt keyrings/alt/Gnome.py keyrings/alt/Google.py keyrings/alt/Windows.py keyrings/alt/__init__.py keyrings/alt/_win_crypto.py keyrings/alt/file.py keyrings/alt/keyczar.py keyrings/alt/kwallet.py keyrings/alt/multi.py keyrings/alt/pyfs.py tests/__init__.py tests/mocks.py tests/test_Gnome.py tests/test_Google.py tests/test_Windows.py tests/test_crypto.py tests/test_file.py tests/test_keyczar.py tests/test_kwallet.py tests/test_multi.py tests/test_pyfs.pykeyrings.alt-1.1.1/keyrings.alt.egg-info/top_level.txt0000644000076500000240000000001112665366216023351 0ustar jaracostaff00000000000000keyrings keyrings.alt-1.1.1/PKG-INFO0000644000076500000240000000126012665366216015617 0ustar jaracostaff00000000000000Metadata-Version: 1.1 Name: keyrings.alt Version: 1.1.1 Summary: Alternate keyring implementations Home-page: https://github.com/jaraco/keyrings.alt Author: Jason R. Coombs Author-email: jaraco@jaraco.com License: UNKNOWN Description: keyrings.alt ============ Alternate keyring backend implementations for use with the `keyring package `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 keyrings.alt-1.1.1/pytest.ini0000644000076500000240000000016312645677507016562 0ustar jaracostaff00000000000000[pytest] norecursedirs=*.egg .eggs dist build addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS keyrings.alt-1.1.1/README.rst0000644000076500000240000000022112645742633016204 0ustar jaracostaff00000000000000keyrings.alt ============ Alternate keyring backend implementations for use with the `keyring package `_. keyrings.alt-1.1.1/setup.cfg0000644000076500000240000000024712665366216016347 0ustar jaracostaff00000000000000[aliases] release = sdist bdist_wheel build_sphinx upload upload_docs test = pytest [wheel] universal = 1 [egg_info] tag_svn_revision = 0 tag_build = tag_date = 0 keyrings.alt-1.1.1/setup.py0000644000076500000240000000465512650704627016243 0ustar jaracostaff00000000000000#!/usr/bin/env python # Generated by jaraco.develop 2.27.1 # https://pypi.python.org/pypi/jaraco.develop import io import sys import setuptools with io.open('README.rst', encoding='utf-8') as readme: long_description = readme.read() needs_pytest = {'pytest', 'test'}.intersection(sys.argv) pytest_runner = ['pytest_runner'] if needs_pytest else [] needs_sphinx = {'release', 'build_sphinx', 'upload_docs'}.intersection(sys.argv) sphinx = ['sphinx'] if needs_sphinx else [] needs_wheel = {'release', 'bdist_wheel'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] test_requirements = [ 'gdata', 'python-keyczar', 'fs>=0.5', 'pycrypto', ] "dependencies for running tests" if sys.version_info >= (3, 0): # gdata doesn't currently install on Python 3. Omit it also. # http://code.google.com/p/gdata-python-client/issues/detail?id=229 test_requirements.remove('gdata') # keyczar doesn't currently install on Python 3. Omit it also. # http://code.google.com/p/keyczar/issues/detail?id=125 test_requirements.remove('python-keyczar') setup_params = dict( name='keyrings.alt', use_scm_version=True, author="Jason R. Coombs", author_email="jaraco@jaraco.com", description="Alternate keyring implementations", long_description=long_description, url="https://github.com/jaraco/keyrings.alt", packages=setuptools.find_packages(exclude=['tests']), include_package_data=True, namespace_packages=['keyrings'], install_requires=[ ], extras_require={ }, setup_requires=[ 'setuptools_scm>=1.9', ] + pytest_runner + sphinx + wheel, tests_require=[ 'keyring[test]', ] + test_requirements, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", ], entry_points={ 'keyring.backends': [ 'file = keyrings.alt.file', 'Gnome = keyrings.alt.Gnome', 'Google = keyrings.alt.Google', 'keyczar = keyrings.alt.keyczar', 'kwallet = keyrings.alt.kwallet', 'multi = keyrings.alt.multi', 'pyfs = keyrings.alt.pyfs', 'Windows (alt) = keyrings.alt.Windows', ], }, ) if __name__ == '__main__': setuptools.setup(**setup_params) keyrings.alt-1.1.1/tests/0000755000076500000240000000000012665366216015665 5ustar jaracostaff00000000000000keyrings.alt-1.1.1/tests/__init__.py0000644000076500000240000000000012645702441017754 0ustar jaracostaff00000000000000keyrings.alt-1.1.1/tests/mocks.py0000644000076500000240000001444012650706425017350 0ustar jaracostaff00000000000000""" Various mock objects for testing """ import base64 import io from keyring.py27compat import pickle, unicode_str class MockAtom(object): """ Mocks an atom in the GData service. """ def __init__(self, value): self.text = value class MockEntry(object): """ Mocks and entry returned from the GData service. """ def __init__(self, title, ID): self.title = MockAtom(title) self.id = MockAtom('http://mock.example.com/%s' % ID) self.ID = ID # simpler lookup for key value def GetEditMediaLink(self): return MockLink() class MockHTTPClient(object): """ Mocks the functionality of an http client. """ def request(*args, **kwargs): pass class MockGDataService(object): """ Provides the common functionality of a Google Service. """ http_client = MockHTTPClient() def __init__(self, email=None, password=None, account_type='HOSTED_OR_GOOGLE', service=None, auth_service_url=None, source=None, server=None, additional_headers=None, handler=None, tokens=None, http_client=None, token_store=None): """ Create the Service with the default parameters. """ self.email = email self.password = password self.account_type = account_type self.service = service self.auth_service_url = auth_service_url self.server = server self.login_token = None def GetClientLoginToken(self): return self.login_token def SetClientLoginToken(self, token): self.login_token = token def ClientLogin(self, username, password, account_type=None, service=None, auth_service_url=None, source=None, captcha_token=None, captcha_response=None): """ Client side login to the service. """ if hasattr(self, '_login_err'): raise self._login_err() class MockDocumentService(MockGDataService): """ Implements the minimum functionality of the Google Document service. """ def Upload(self, media_source, title, folder_or_uri=None, label=None): """ Upload a document. """ if hasattr(self, '_upload_err'): raise self._upload_err() if not hasattr(self, '_upload_count'): self._upload_count = 0 # save the data for asserting against self._upload_data = dict(media_source=media_source, title=title, folder_or_uri=folder_or_uri, label=label) self._upload_count += 1 return MockEntry(title, 'mockentry%3A' + title) def QueryDocumentListFeed(self, uri): if hasattr(self, '_listfeed'): return self._listfeed return MockListFeed() def CreateFolder(self, title, folder_or_uri=None): if hasattr(self, '_create_folder_err'): raise self._create_folder_err() if hasattr(self, '_create_folder'): return self._create_folder return MockListEntry() def Put(self, data, uri, extra_headers=None, url_params=None, escape_params=True, redirects_remaining=3, media_source=None, converter=None): self._put_data = None if not hasattr(self, '_put_count'): self._put_count = 0 if hasattr(self, '_put_err'): # allow for a list of errors if type(self._put_err) == list: put_err = self._put_err.pop(0) if not len(self._put_err): delattr(self, '_put_err') else: put_err = self._put_err if type(put_err) == tuple: raise put_err[0](put_err[1]) else: raise put_err() # save the data for asserting against assert isinstance(data, str) or isinstance(data, unicode_str), \ 'Should be a string' self._put_data = pickle.loads(base64.urlsafe_b64decode(data)) self._put_count += 1 return MockEntry('', 'mockentry%3A' + '') def Export(self, entry_or_id_or_url, file_path, gid=None, extra_params=None): if hasattr(self, '_export_err'): raise self._export_err() if hasattr(self, '_export_data'): export_file = open(file_path, 'wb') export_file.write(self._export_data) export_file.close() def request(self, data, uri): if hasattr(self, '_request_err'): if type(self._request_err) == tuple: raise self._request_err[0](self._request_err[1]) else: raise self._request_err() if hasattr(self, '_request_response'): return MockHttpResponse(self._request_response) class MockHttpResponse(io.BytesIO, object): def __init__(self, response_dict): super(MockHttpResponse, self).__init__(response_dict.get('data', '')) self.status = response_dict.get('status', 200) self.reason = response_dict.get('reason', '') class MockListFeed(object): @property def entry(self): if hasattr(self, '_entry'): return self._entry return [] class MockListEntry(object): pass class MockLink(object): @property def href(self): return '' class MockContent(object): @property def src(self): return 'src' class MockDocumentListEntry(object): @property def content(self): return MockContent() def GetEditMediaLink(self): return MockLink() class MockKeyczarReader(object): def __init__(self, location): self.location = location class MockKeyczarEncryptedReader(object): def __init__(self, reader, crypter): self._reader = reader self._crypter = crypter class MockKeyczarReaders(object): @staticmethod def CreateReader(location): return MockKeyczarReader(location) @staticmethod def EncryptedReader(reader, crypter): return MockKeyczarEncryptedReader(reader, crypter) class MockKeyczarCrypter(object): def __init__(self, reader): self.reader = reader @staticmethod def Read(location): return MockKeyczarCrypter(MockKeyczarReader(location)) class MockKeyczar(object): @property def readers(self): return MockKeyczarReaders @property def Crypter(self): return MockKeyczarCrypter keyrings.alt-1.1.1/tests/test_crypto.py0000644000076500000240000000145712645703771020624 0ustar jaracostaff00000000000000import unittest import mock from .test_file import FileKeyringTests from keyrings.alt import file def is_crypto_supported(): try: __import__('Crypto.Cipher.AES') __import__('Crypto.Protocol.KDF') __import__('Crypto.Random') except ImportError: return False return True @unittest.skipUnless(is_crypto_supported(), "Need Crypto module") class CryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase): def setUp(self): super(self.__class__, self).setUp() fake_getpass = mock.Mock(return_value='abcdef') self.patcher = mock.patch('getpass.getpass', fake_getpass) self.patcher.start() def tearDown(self): self.patcher.stop() def init_keyring(self): return file.EncryptedKeyring() keyrings.alt-1.1.1/tests/test_file.py0000644000076500000240000000312312645702570020207 0ustar jaracostaff00000000000000import os import tempfile import sys import errno import unittest from keyring.tests.test_backend import BackendBasicTests from keyring.tests.util import random_string from keyrings.alt import file class FileKeyringTests(BackendBasicTests): def setUp(self): super(FileKeyringTests, self).setUp() self.keyring = self.init_keyring() self.keyring.file_path = self.tmp_keyring_file = tempfile.mktemp() def tearDown(self): try: os.unlink(self.tmp_keyring_file) except (OSError,): e = sys.exc_info()[1] if e.errno != errno.ENOENT: # No such file or directory raise def test_encrypt_decrypt(self): password = random_string(20) # keyring.encrypt expects bytes password = password.encode('utf-8') encrypted = self.keyring.encrypt(password) self.assertEqual(password, self.keyring.decrypt(encrypted)) class UncryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase): def init_keyring(self): return file.PlaintextKeyring() @unittest.skipIf(sys.platform == 'win32', "Group/World permissions aren't meaningful on Windows") def test_keyring_not_created_world_writable(self): """ Ensure that when keyring creates the file that it's not overly- permissive. """ self.keyring.set_password('system', 'user', 'password') self.assertTrue(os.path.exists(self.keyring.file_path)) group_other_perms = os.stat(self.keyring.file_path).st_mode & 0o077 self.assertEqual(group_other_perms, 0) keyrings.alt-1.1.1/tests/test_Gnome.py0000644000076500000240000000211212645702626020334 0ustar jaracostaff00000000000000import types import sys import unittest from keyring.tests.test_backend import BackendBasicTests from keyring.tests.util import NoNoneDictMutator from keyrings.alt import Gnome def ImportBlesser(*names, **changes): """A context manager to temporarily make it possible to import a module""" for name in names: changes[name] = types.ModuleType(name) return NoNoneDictMutator(sys.modules, **changes) @unittest.skipUnless(Gnome.Keyring.viable, "Need GnomeKeyring") class GnomeKeyringTestCase(BackendBasicTests, unittest.TestCase): def init_keyring(self): k = Gnome.Keyring() # Store passwords in the session (in-memory) keyring for the tests. This # is going to be automatically cleared when the user logoff. k.KEYRING_NAME = 'session' return k def test_supported(self): with ImportBlesser('gi.repository'): self.assertTrue(Gnome.Keyring.viable) def test_supported_no_module(self): with NoNoneDictMutator(Gnome.__dict__, GnomeKeyring=None): self.assertFalse(Gnome.Keyring.viable) keyrings.alt-1.1.1/tests/test_Google.py0000644000076500000240000003616412650704627020520 0ustar jaracostaff00000000000000import codecs import base64 import unittest from keyring.tests.test_backend import BackendBasicTests from keyrings.alt import Google from keyring.credentials import SimpleCredential from keyring.backend import NullCrypter from keyring import errors from keyring.py27compat import input, pickle from . import mocks def is_gdata_supported(): try: __import__('gdata.service') except ImportError: return False return True def init_google_docs_keyring(client, can_create=True, input_getter=input): credentials = SimpleCredential('foo', 'bar') return Google.DocsKeyring(credentials, 'test_src', NullCrypter(), client=client, can_create=can_create, input_getter=input_getter ) @unittest.skipUnless(is_gdata_supported(), "Need Google Docs (gdata)") class GoogleDocsKeyringTestCase(BackendBasicTests, unittest.TestCase): """Run all the standard tests on a new keyring""" def init_keyring(self): client = mocks.MockDocumentService() client.SetClientLoginToken('foo') return init_google_docs_keyring(client) @unittest.skipUnless(is_gdata_supported(), "Need Google Docs (gdata)") class GoogleDocsKeyringInteractionTestCase(unittest.TestCase): """Additional tests for Google Doc interactions""" def _init_client(self, set_token=True): client = mocks.MockDocumentService() if set_token: client.SetClientLoginToken('interaction') return client def _init_keyring(self, client): self.keyring = init_google_docs_keyring(client) def _init_listfeed(self): listfeed = mocks.MockListFeed() listfeed._entry = [mocks.MockDocumentListEntry(), mocks.MockDocumentListEntry() ] return listfeed def _encode_data(self, data): return base64.urlsafe_b64encode(pickle.dumps(data)) def test_handles_auth_failure(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.BadAuthentication self._init_keyring(client) with self.assertRaises(errors.InitError): self.keyring.client def test_handles_auth_error(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.Error self._init_keyring(client) with self.assertRaises(errors.InitError): self.keyring.client def test_handles_login_captcha(self): import gdata client = self._init_client(set_token=False) client._login_err = gdata.service.CaptchaRequired client.captcha_url = 'a_captcha_url' client.captcha_token = 'token' self.get_input_called = False def _get_input(prompt): self.get_input_called = True delattr(client, '_login_err') return 'Foo' self.keyring = init_google_docs_keyring(client, input_getter=_get_input) self.keyring.client self.assertTrue(self.get_input_called, 'Should have got input') def test_retrieves_existing_keyring_with_and_without_bom(self): client = self._init_client() dummy_entries = dict(section1=dict(user1='pwd1')) no_utf8_bom_entries = self._encode_data(dummy_entries) client._request_response = dict(status=200, data=no_utf8_bom_entries) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual(self.keyring.get_password('section1', 'user1'), 'pwd1') utf8_bom_entries = codecs.BOM_UTF8 + no_utf8_bom_entries client._request_response = dict(status=200, data=utf8_bom_entries) self._init_keyring(client) self.assertEqual(self.keyring.get_password('section1', 'user1'), 'pwd1') def test_handles_retrieve_failure(self): client = self._init_client() client._listfeed = self._init_listfeed() client._request_response = dict(status=400, reason='Data centre explosion') self._init_keyring(client) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_handles_corrupt_retrieve(self): client = self._init_client() dummy_entries = dict(section1=dict(user1='pwd1')) client._request_response = dict(status=200, data='broken' + self._encode_data(dummy_entries)) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_no_create_if_requested(self): client = self._init_client() self.keyring = init_google_docs_keyring(client, can_create=False) self.assertRaises(errors.InitError, self.keyring.get_password, 'any', 'thing') def test_no_set_if_create_folder_fails_on_new_keyring(self): import gdata client = self._init_client() client._create_folder_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'service-a', 'user-A', 'password-A') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set after write fail') def test_no_set_if_write_fails_on_new_keyring(self): import gdata client = self._init_client() client._upload_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'service-a', 'user-A', 'password-A') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set after write fail') def test_no_set_if_write_fails_on_existing_keyring(self): import gdata client = self._init_client() dummy_entries = dict(sectionB=dict(user9='pwd9')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = gdata.service.RequestError client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual(self.keyring.get_password('sectionB', 'user9'), 'pwd9', 'Correct password should be set in existing keyring') self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionB', 'user9', 'Not the same pwd') self.assertEqual(self.keyring.get_password('sectionB', 'user9'), 'pwd9', 'Password should be unchanged after write fail') def test_writes_correct_data_to_google_docs(self): client = self._init_client() dummy_entries = dict(sectionWriteChk=dict(userWriteChk='pwd')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._listfeed = self._init_listfeed() self._init_keyring(client) self.keyring.set_password('sectionWriteChk', 'userWritechk', 'new_pwd') self.assertIsNotNone(client._put_data, 'Should have written data') self.assertEquals( 'new_pwd', client._put_data.get('sectionWriteChk').get('userWritechk'), 'Did not write updated password!') def test_handles_write_conflict_on_different_service(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictA=dict( userwriteConflictA='pwdwriteConflictA')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [(gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictA', 'userwriteConflictA'), 'pwdwriteConflictA', 'Correct password should be set in existing keyring') dummy_entries['diffSection'] = dict(foo='bar') client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) new_pwd = 'Not the same pwd' self.keyring.set_password('sectionWriteConflictA', 'userwriteConflictA', new_pwd) self.assertEquals(self.keyring.get_password('sectionWriteConflictA', 'userwriteConflictA'), new_pwd ) self.assertEqual(1, client._put_count, 'Write not called after conflict resolution') def test_handles_write_conflict_on_same_service_and_username(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictB=dict( userwriteConflictB='pwdwriteConflictB')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = (gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}) client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictB', 'userwriteConflictB'), 'pwdwriteConflictB', 'Correct password should be set in existing keyring') conflicting_dummy_entries = dict(sectionWriteConflictB=dict( userwriteConflictB='pwdwriteConflictC')) client._request_response = dict(status=200, data=self._encode_data(conflicting_dummy_entries)) self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionWriteConflictB', 'userwriteConflictB', 'new_pwd') def test_handles_write_conflict_with_identical_change(self): import gdata client = self._init_client() dummy_entries = dict(sectionWriteConflictC=dict( userwriteConflictC='pwdwriteConflictC')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [(gdata.service.RequestError, {'status': '406', 'reason': 'Conflict'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) self.assertEqual( self.keyring.get_password('sectionWriteConflictC', 'userwriteConflictC'), 'pwdwriteConflictC', 'Correct password should be set in existing keyring') new_pwd = 'Not the same pwd' conflicting_dummy_entries = dict(sectionWriteConflictC=dict( userwriteConflictC=new_pwd)) client._request_response = dict(status=200, data=self._encode_data(conflicting_dummy_entries)) self.keyring.set_password('sectionWriteConflictC', 'userwriteConflictC', new_pwd) self.assertEquals(self.keyring.get_password('sectionWriteConflictC', 'userwriteConflictC'), new_pwd ) def test_handles_broken_google_put_when_non_owner_update_fails(self): """Google Docs has a bug when putting to a non-owner see GoogleDocsKeyring._save_keyring() """ import gdata client = self._init_client() dummy_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [( gdata.service.RequestError, { 'status': '400', 'body': 'Sorry, there was an error saving the file. Please try again.', 'reason': 'Bad Request'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) new_pwd = 'newPwdBrokenPut' correct_read_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(correct_read_entries)) self.assertRaises(errors.PasswordSetError, self.keyring.set_password, 'sectionBrokenPut', 'userBrokenPut', new_pwd) def test_handles_broken_google_put_when_non_owner_update(self): """Google Docs has a bug when putting to a non-owner see GoogleDocsKeyring._save_keyring() """ import gdata client = self._init_client() dummy_entries = dict(sectionBrokenPut=dict( userBrokenPut='pwdBrokenPut')) client._request_response = dict(status=200, data=self._encode_data(dummy_entries)) client._put_err = [( gdata.service.RequestError, { 'status': '400', 'body': 'Sorry, there was an error saving the file. Please try again.', 'reason': 'Bad Request'}),] client._listfeed = self._init_listfeed() self._init_keyring(client) new_pwd = 'newPwdBrokenPut' correct_read_entries = dict(sectionBrokenPut=dict( userBrokenPut=new_pwd)) client._request_response = dict(status=200, data=self._encode_data(correct_read_entries)) self.keyring.set_password('sectionBrokenPut', 'userBrokenPut', new_pwd) self.assertEquals(self.keyring.get_password('sectionBrokenPut', 'userBrokenPut'), new_pwd) def test_uses_existing_folder(self): import gdata client = self._init_client() # should not happen client._create_folder_err = gdata.service.RequestError self._init_keyring(client) self.assertEqual(self.keyring.get_password('service-a', 'user-A'), None, 'No password should be set in new keyring') client._listfeed = self._init_listfeed() self.keyring.set_password('service-a', 'user-A', 'password-A') self.assertIsNotNone(client._upload_data, 'Should have written data') self.assertEqual(self.keyring.get_password('service-a', 'user-A'), 'password-A', 'Correct password should be set') keyrings.alt-1.1.1/tests/test_keyczar.py0000644000076500000240000000606512665366020020746 0ustar jaracostaff00000000000000import os import unittest from keyrings.alt import keyczar from . import mocks class KeyczarCrypterTestCase(unittest.TestCase): """Test the keyczar crypter""" def setUp(self): self._orig_keyczar = keyczar.keyczar if hasattr(keyczar, 'keyczar') else None keyczar.keyczar = mocks.MockKeyczar() def tearDown(self): keyczar.keyczar = self._orig_keyczar if keyczar.EnvironCrypter.KEYSET_ENV_VAR in os.environ: del os.environ[keyczar.EnvironCrypter.KEYSET_ENV_VAR] if keyczar.EnvironCrypter.ENC_KEYSET_ENV_VAR in os.environ: del os.environ[keyczar.EnvironCrypter.ENC_KEYSET_ENV_VAR] def testKeyczarCrypterWithUnencryptedReader(self): """ """ location = 'bar://baz' kz_crypter = keyczar.Crypter(location) self.assertEquals(location, kz_crypter.keyset_location) self.assertIsNone(kz_crypter.encrypting_keyset_location) self.assertIsInstance(kz_crypter.crypter, mocks.MockKeyczarCrypter) self.assertIsInstance(kz_crypter.crypter.reader, mocks.MockKeyczarReader) self.assertEquals(location, kz_crypter.crypter.reader.location) def testKeyczarCrypterWithEncryptedReader(self): """ """ location = 'foo://baz' encrypting_location = 'castle://aaargh' kz_crypter = keyczar.Crypter(location, encrypting_location) self.assertEquals(location, kz_crypter.keyset_location) self.assertEquals(encrypting_location, kz_crypter.encrypting_keyset_location) self.assertIsInstance(kz_crypter.crypter, mocks.MockKeyczarCrypter) self.assertIsInstance(kz_crypter.crypter.reader, mocks.MockKeyczarEncryptedReader) self.assertEquals(location, kz_crypter.crypter.reader._reader.location) self.assertEquals(encrypting_location, kz_crypter.crypter.reader._crypter.reader.location) def testKeyczarCrypterEncryptDecryptHandlesEmptyNone(self): location = 'castle://aargh' kz_crypter = keyczar.Crypter(location) self.assertEquals('', kz_crypter.encrypt('')) self.assertEquals('', kz_crypter.encrypt(None)) self.assertEquals('', kz_crypter.decrypt('')) self.assertEquals('', kz_crypter.decrypt(None)) def testEnvironCrypterReadsCorrectValues(self): location = 'foo://baz' encrypting_location = 'castle://aaargh' kz_crypter = keyczar.EnvironCrypter() os.environ[kz_crypter.KEYSET_ENV_VAR] = location self.assertEqual(location, kz_crypter.keyset_location) self.assertIsNone(kz_crypter.encrypting_keyset_location) os.environ[kz_crypter.ENC_KEYSET_ENV_VAR] = encrypting_location self.assertEqual(encrypting_location, kz_crypter.encrypting_keyset_location) def testEnvironCrypterThrowsExceptionOnMissingValues(self): kz_crypter = keyczar.EnvironCrypter() with self.assertRaises(ValueError): kz_crypter.keyset_location self.assertIsNone(kz_crypter.encrypting_keyset_location) keyrings.alt-1.1.1/tests/test_kwallet.py0000644000076500000240000000417512645703022020734 0ustar jaracostaff00000000000000import unittest from keyrings.alt import kwallet from keyring.tests.test_backend import BackendBasicTests def is_qt4_supported(): try: __import__('PyQt4.QtGui') except ImportError: return False return True @unittest.skipUnless(kwallet.QtKeyring.viable, "Need KWallet") class KDEKWalletTestCase(BackendBasicTests, unittest.TestCase): def init_keyring(self): return kwallet.QtKeyring() class UnOpenableKWallet(object): """A module-like object used to test KDE wallet fall-back.""" Synchronous = None def openWallet(self, *args): return None def NetworkWallet(self): return None class FauxQtGui(object): """A fake module-like object used in testing the open_kwallet function.""" class qApp: @staticmethod def instance(): pass class QApplication(object): def __init__(self, *args): pass def exit(self): pass class QWidget(object): def __init__(self, *args): pass def winId(self): pass class KDEWalletCanceledTestCase(unittest.TestCase): def test_user_canceled(self): # If the user cancels either the "enter your password to unlock the # keyring" dialog or clicks "deny" on the "can this application access # the wallet" dialog then openWallet() will return None. The # open_wallet() function should handle that eventuality by returning # None to signify that the KWallet backend is not available. self.assertEqual( kwallet.open_kwallet(UnOpenableKWallet(), FauxQtGui()), None) @unittest.skipUnless(kwallet.QtKeyring.viable and is_qt4_supported(), "Need KWallet and Qt4") class KDEKWalletInQApplication(unittest.TestCase): def test_QApplication(self): try: from PyKDE4.kdeui import KWallet from PyQt4.QtGui import QApplication except: return app = QApplication([]) wallet = kwallet.open_kwallet() self.assertIsInstance(wallet, KWallet.Wallet) app.exit() keyrings.alt-1.1.1/tests/test_multi.py0000644000076500000240000000406212645703075020426 0ustar jaracostaff00000000000000import unittest from keyring.backend import KeyringBackend from keyrings.alt import multi import keyring.errors class MultipartKeyringWrapperTestCase(unittest.TestCase): """Test the wrapper that breaks passwords into smaller chunks""" class MockKeyring(KeyringBackend): priority = 1 def __init__(self): self.passwords = {} def get_password(self, service, username): return self.passwords.get(service+username) def set_password(self, service, username, password): self.passwords[service+username] = password def delete_password(self, service, username): try: del self.passwords[service+username] except KeyError: raise keyring.errors.PasswordDeleteError('not found') def testViablePassThru(self): kr = multi.MultipartKeyringWrapper(self.MockKeyring()) self.assertTrue(kr.viable) def testMissingPassword(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr) self.assertIsNone(kr.get_password('s1', 'u1')) def testSmallPasswordSetInSinglePart(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr) kr.set_password('s1', 'u1', 'p1') self.assertEquals(wrapped_kr.passwords, {'s1u1':'p1'}) # should be able to read it back self.assertEquals(kr.get_password('s1', 'u1'), 'p1') def testLargePasswordSetInMultipleParts(self): wrapped_kr = self.MockKeyring() kr = multi.MultipartKeyringWrapper(wrapped_kr, max_password_size=2) kr.set_password('s2', 'u2', '0123456') self.assertEquals(wrapped_kr.passwords, {'s2u2':'01', 's2u2{{part_1}}':'23', 's2u2{{part_2}}':'45', "s2u2{{part_3}}":'6'}) # should be able to read it back self.assertEquals(kr.get_password('s2', 'u2'), '0123456') keyrings.alt-1.1.1/tests/test_pyfs.py0000644000076500000240000001056712650704627020264 0ustar jaracostaff00000000000000from __future__ import unicode_literals import os import tempfile import textwrap import unittest import keyring.backend from keyrings.alt import pyfs from keyring.tests.test_backend import BackendBasicTests, random_string class ReverseCrypter(keyring.backend.Crypter): """Very silly crypter class""" def encrypt(self, value): return value[::-1] def decrypt(self, value): return value[::-1] class PyfilesystemKeyringTests(BackendBasicTests): """Base class for Pyfilesystem tests""" def setUp(self): super(PyfilesystemKeyringTests, self).setUp() self.keyring = self.init_keyring() def tearDown(self): del self.keyring def test_encrypt_decrypt(self): password = random_string(20) encrypted = self.keyring.encrypt(password) self.assertEqual(password, self.keyring.decrypt(encrypted)) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedMemoryPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with no encryption""" keyring_filename = 'mem://unencrypted' def init_keyring(self): return pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedMemoryPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with no encryption""" keyring_filename = 'mem://some/sub/dir/unencrypted' def init_keyring(self): return pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedLocalPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with no encryption""" keyring_filename = '%s/keyring.cfg' %tempfile.mkdtemp() def init_keyring(self): return pyfs.PlaintextKeyring( filename=self.keyring_filename) def test_handles_preexisting_keyring(self): from fs.opener import opener fs, path = opener.parse(self.keyring_filename, writeable=True) keyring_file = fs.open(path, 'w') file_data = textwrap.dedent(""" [svc1] user1 = cHdkMQ== """).lstrip() keyring_file.write(file_data) keyring_file.close() pyf_keyring = pyfs.PlaintextKeyring( filename=self.keyring_filename) self.assertEquals('pwd1', pyf_keyring.get_password('svc1', 'user1')) def tearDown(self): del self.keyring if os.path.exists(self.keyring_filename): os.remove(self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class UnencryptedLocalPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with no encryption""" keyring_dir = os.path.join(tempfile.mkdtemp(), 'more', 'sub', 'dirs') keyring_filename = os.path.join(keyring_dir, 'keyring.cfg') def init_keyring(self): if not os.path.exists(self.keyring_dir): os.makedirs(self.keyring_dir) return pyfs.PlaintextKeyring( filename=self.keyring_filename) @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedMemoryPyfilesystemKeyringTestCase(PyfilesystemKeyringTests, unittest.TestCase): """Test in memory with encryption""" def init_keyring(self): return pyfs.EncryptedKeyring( ReverseCrypter(), filename='mem://encrypted/keyring.cfg') @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedLocalPyfilesystemKeyringNoSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with encryption""" def init_keyring(self): return pyfs.EncryptedKeyring( ReverseCrypter(), filename='temp://keyring.cfg') @unittest.skipUnless(pyfs.BasicKeyring.viable, "Need Pyfilesystem") class EncryptedLocalPyfilesystemKeyringSubDirTestCase( PyfilesystemKeyringTests, unittest.TestCase): """Test using local temp files with encryption""" def init_keyring(self): return pyfs.EncryptedKeyring( ReverseCrypter(), filename='temp://a/sub/dir/hierarchy/keyring.cfg') keyrings.alt-1.1.1/tests/test_Windows.py0000644000076500000240000000261212650704627020725 0ustar jaracostaff00000000000000from __future__ import print_function import sys import unittest from keyrings.alt import Windows from keyring.tests.test_backend import BackendBasicTests from .test_file import FileKeyringTests def is_win32_crypto_supported(): try: __import__('keyrings.alt._win_crypto') except ImportError: return False return sys.platform in ['win32'] and sys.getwindowsversion()[-2] == 2 def is_winvault_supported(): try: __import__('win32cred') has_pywin32 = True except ImportError: has_pywin32 = False return ( sys.platform in ['win32'] and sys.getwindowsversion().major >= 6 and has_pywin32 ) @unittest.skipUnless(is_win32_crypto_supported(), "Need Windows") class Win32CryptoKeyringTestCase(FileKeyringTests, unittest.TestCase): def init_keyring(self): return Windows.EncryptedKeyring() @unittest.skipUnless(Windows.RegistryKeyring.viable and sys.version_info > (3,), "RegistryKeyring not viable") class RegistryKeyringTestCase(BackendBasicTests, unittest.TestCase): def tearDown(self): # clean up any credentials created for cred in self.credentials_created: try: self.keyring.delete_password(*cred) except Exception as e: print(e, file=sys.stderr) def init_keyring(self): return Windows.RegistryKeyring()