1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
|
import functools
import os
import platform
import warnings
from ...backend import KeyringBackend
from ...compat import properties
from ...errors import KeyringError, KeyringLocked, PasswordDeleteError, PasswordSetError
try:
from . import api
except Exception:
pass
def warn_keychain(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
if self.keychain:
warnings.warn("Specified keychain is ignored. See #623", stacklevel=2)
return func(self, *args, **kwargs)
return wrapper
class Keyring(KeyringBackend):
"""macOS Keychain"""
keychain = os.environ.get('KEYCHAIN_PATH')
"Path to keychain file, overriding default"
@properties.classproperty
def priority(cls):
"""
Preferred for all macOS environments.
"""
if platform.system() != 'Darwin':
raise RuntimeError("macOS required")
if 'api' not in globals():
raise RuntimeError("Security API unavailable")
return 5
@warn_keychain
def set_password(self, service, username, password):
if username is None:
username = ''
try:
api.set_generic_password(self.keychain, service, username, password)
except api.KeychainDenied as e:
raise KeyringLocked(f"Can't store password on keychain: {e}") from e
except api.Error as e:
raise PasswordSetError(f"Can't store password on keychain: {e}") from e
@warn_keychain
def get_password(self, service, username):
if username is None:
username = ''
try:
return api.find_generic_password(self.keychain, service, username)
except api.NotFound:
pass
except api.KeychainDenied as e:
raise KeyringLocked(f"Can't get password from keychain: {e}") from e
except api.Error as e:
raise KeyringError(f"Can't get password from keychain: {e}") from e
@warn_keychain
def delete_password(self, service, username):
if username is None:
username = ''
try:
return api.delete_generic_password(self.keychain, service, username)
except api.Error as e:
raise PasswordDeleteError(f"Can't delete password in keychain: {e}") from e
def with_keychain(self, keychain):
warnings.warn(
"macOS.Keyring.with_keychain is deprecated. Use with_properties instead.",
DeprecationWarning,
stacklevel=2,
)
return self.with_properties(keychain=keychain)
|