mirror of
https://github.com/EvolutionAPI/evolution-client-python.git
synced 2025-12-20 12:22:19 -06:00
initial commit
This commit is contained in:
85
env/lib/python3.10/site-packages/keyring/backends/macOS/__init__.py
vendored
Normal file
85
env/lib/python3.10/site-packages/keyring/backends/macOS/__init__.py
vendored
Normal file
@@ -0,0 +1,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)
|
||||
BIN
env/lib/python3.10/site-packages/keyring/backends/macOS/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/macOS/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/macOS/__pycache__/api.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/macOS/__pycache__/api.cpython-310.pyc
vendored
Normal file
Binary file not shown.
184
env/lib/python3.10/site-packages/keyring/backends/macOS/api.py
vendored
Normal file
184
env/lib/python3.10/site-packages/keyring/backends/macOS/api.py
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import ctypes
|
||||
import functools
|
||||
from ctypes import (
|
||||
byref,
|
||||
c_int32,
|
||||
c_uint32,
|
||||
c_void_p,
|
||||
)
|
||||
from ctypes.util import find_library
|
||||
|
||||
OS_status = c_int32
|
||||
|
||||
|
||||
class error:
|
||||
item_not_found = -25300
|
||||
keychain_denied = -128
|
||||
sec_auth_failed = -25293
|
||||
plist_missing = -67030
|
||||
sec_interaction_not_allowed = -25308
|
||||
|
||||
|
||||
_sec = ctypes.CDLL(find_library('Security'))
|
||||
_core = ctypes.CDLL(find_library('CoreServices'))
|
||||
_found = ctypes.CDLL(find_library('Foundation'))
|
||||
|
||||
CFDictionaryCreate = _found.CFDictionaryCreate
|
||||
CFDictionaryCreate.restype = c_void_p
|
||||
CFDictionaryCreate.argtypes = (
|
||||
c_void_p,
|
||||
c_void_p,
|
||||
c_void_p,
|
||||
c_int32,
|
||||
c_void_p,
|
||||
c_void_p,
|
||||
)
|
||||
|
||||
CFStringCreateWithCString = _found.CFStringCreateWithCString
|
||||
CFStringCreateWithCString.restype = c_void_p
|
||||
CFStringCreateWithCString.argtypes = [c_void_p, c_void_p, c_uint32]
|
||||
|
||||
CFNumberCreate = _found.CFNumberCreate
|
||||
CFNumberCreate.restype = c_void_p
|
||||
CFNumberCreate.argtypes = [c_void_p, c_uint32, ctypes.c_void_p]
|
||||
|
||||
SecItemAdd = _sec.SecItemAdd
|
||||
SecItemAdd.restype = OS_status
|
||||
SecItemAdd.argtypes = (c_void_p, c_void_p)
|
||||
|
||||
SecItemCopyMatching = _sec.SecItemCopyMatching
|
||||
SecItemCopyMatching.restype = OS_status
|
||||
SecItemCopyMatching.argtypes = (c_void_p, c_void_p)
|
||||
|
||||
SecItemDelete = _sec.SecItemDelete
|
||||
SecItemDelete.restype = OS_status
|
||||
SecItemDelete.argtypes = (c_void_p,)
|
||||
|
||||
CFDataGetBytePtr = _found.CFDataGetBytePtr
|
||||
CFDataGetBytePtr.restype = c_void_p
|
||||
CFDataGetBytePtr.argtypes = (c_void_p,)
|
||||
|
||||
CFDataGetLength = _found.CFDataGetLength
|
||||
CFDataGetLength.restype = c_int32
|
||||
CFDataGetLength.argtypes = (c_void_p,)
|
||||
|
||||
|
||||
def k_(s):
|
||||
return c_void_p.in_dll(_sec, s)
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def create_cf(ob):
|
||||
return ob
|
||||
|
||||
|
||||
# explicit bool and int required for Python 3.10 compatibility
|
||||
@create_cf.register(bool)
|
||||
@create_cf.register(int)
|
||||
def _(val: bool | int):
|
||||
if val.bit_length() > 31:
|
||||
raise OverflowError(val)
|
||||
int32 = 0x9
|
||||
return CFNumberCreate(None, int32, ctypes.byref(c_int32(val)))
|
||||
|
||||
|
||||
@create_cf.register
|
||||
def _(s: str):
|
||||
kCFStringEncodingUTF8 = 0x08000100
|
||||
return CFStringCreateWithCString(None, s.encode('utf8'), kCFStringEncodingUTF8)
|
||||
|
||||
|
||||
def create_query(**kwargs):
|
||||
return CFDictionaryCreate(
|
||||
None,
|
||||
(c_void_p * len(kwargs))(*map(k_, kwargs.keys())),
|
||||
(c_void_p * len(kwargs))(*map(create_cf, kwargs.values())),
|
||||
len(kwargs),
|
||||
_found.kCFTypeDictionaryKeyCallBacks,
|
||||
_found.kCFTypeDictionaryValueCallBacks,
|
||||
)
|
||||
|
||||
|
||||
def cfstr_to_str(data):
|
||||
return ctypes.string_at(CFDataGetBytePtr(data), CFDataGetLength(data)).decode(
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
@classmethod
|
||||
def raise_for_status(cls, status):
|
||||
if status == 0:
|
||||
return
|
||||
if status == error.item_not_found:
|
||||
raise NotFound(status, "Item not found")
|
||||
if status == error.keychain_denied:
|
||||
raise KeychainDenied(status, "Keychain Access Denied")
|
||||
if status == error.sec_auth_failed or status == error.plist_missing:
|
||||
raise SecAuthFailure(
|
||||
status,
|
||||
"Security Auth Failure: make sure "
|
||||
"executable is signed with codesign util",
|
||||
)
|
||||
raise cls(status, "Unknown Error")
|
||||
|
||||
|
||||
class NotFound(Error):
|
||||
pass
|
||||
|
||||
|
||||
class KeychainDenied(Error):
|
||||
pass
|
||||
|
||||
|
||||
class SecAuthFailure(Error):
|
||||
pass
|
||||
|
||||
|
||||
def find_generic_password(kc_name, service, username, not_found_ok=False):
|
||||
q = create_query(
|
||||
kSecClass=k_('kSecClassGenericPassword'),
|
||||
kSecMatchLimit=k_('kSecMatchLimitOne'),
|
||||
kSecAttrService=service,
|
||||
kSecAttrAccount=username,
|
||||
kSecReturnData=True,
|
||||
)
|
||||
|
||||
data = c_void_p()
|
||||
status = SecItemCopyMatching(q, byref(data))
|
||||
|
||||
if status == error.item_not_found and not_found_ok:
|
||||
return
|
||||
|
||||
Error.raise_for_status(status)
|
||||
|
||||
return cfstr_to_str(data)
|
||||
|
||||
|
||||
def set_generic_password(name, service, username, password):
|
||||
with contextlib.suppress(NotFound):
|
||||
delete_generic_password(name, service, username)
|
||||
|
||||
q = create_query(
|
||||
kSecClass=k_('kSecClassGenericPassword'),
|
||||
kSecAttrService=service,
|
||||
kSecAttrAccount=username,
|
||||
kSecValueData=password,
|
||||
)
|
||||
|
||||
status = SecItemAdd(q, None)
|
||||
Error.raise_for_status(status)
|
||||
|
||||
|
||||
def delete_generic_password(name, service, username):
|
||||
q = create_query(
|
||||
kSecClass=k_('kSecClassGenericPassword'),
|
||||
kSecAttrService=service,
|
||||
kSecAttrAccount=username,
|
||||
)
|
||||
|
||||
status = SecItemDelete(q)
|
||||
Error.raise_for_status(status)
|
||||
Reference in New Issue
Block a user