mirror of
https://github.com/EvolutionAPI/evolution-client-python.git
synced 2025-12-21 12:52:18 -06:00
initial commit
This commit is contained in:
120
env/lib/python3.10/site-packages/keyring/backends/SecretService.py
vendored
Normal file
120
env/lib/python3.10/site-packages/keyring/backends/SecretService.py
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import logging
|
||||
from contextlib import closing
|
||||
|
||||
from jaraco.context import ExceptionTrap
|
||||
|
||||
from .. import backend
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
from ..credentials import SimpleCredential
|
||||
from ..errors import (
|
||||
InitError,
|
||||
KeyringLocked,
|
||||
PasswordDeleteError,
|
||||
)
|
||||
|
||||
try:
|
||||
import secretstorage
|
||||
import secretstorage.exceptions as exceptions
|
||||
except ImportError:
|
||||
pass
|
||||
except AttributeError:
|
||||
# See https://github.com/jaraco/keyring/issues/296
|
||||
pass
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Keyring(backend.SchemeSelectable, KeyringBackend):
|
||||
"""Secret Service Keyring"""
|
||||
|
||||
appid = 'Python keyring library'
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
with ExceptionTrap() as exc:
|
||||
secretstorage.__name__ # noqa: B018
|
||||
if exc:
|
||||
raise RuntimeError("SecretStorage required")
|
||||
if secretstorage.__version_tuple__ < (3, 2):
|
||||
raise RuntimeError("SecretStorage 3.2 or newer required")
|
||||
try:
|
||||
with closing(secretstorage.dbus_init()) as connection:
|
||||
if not secretstorage.check_service_availability(connection):
|
||||
raise RuntimeError(
|
||||
"The Secret Service daemon is neither running nor "
|
||||
"activatable through D-Bus"
|
||||
)
|
||||
except exceptions.SecretStorageException as e:
|
||||
raise RuntimeError(f"Unable to initialize SecretService: {e}") from e
|
||||
return 5
|
||||
|
||||
def get_preferred_collection(self):
|
||||
"""If self.preferred_collection contains a D-Bus path,
|
||||
the collection at that address is returned. Otherwise,
|
||||
the default collection is returned.
|
||||
"""
|
||||
bus = secretstorage.dbus_init()
|
||||
try:
|
||||
if hasattr(self, 'preferred_collection'):
|
||||
collection = secretstorage.Collection(bus, self.preferred_collection)
|
||||
else:
|
||||
collection = secretstorage.get_default_collection(bus)
|
||||
except exceptions.SecretStorageException as e:
|
||||
raise InitError(f"Failed to create the collection: {e}.") from e
|
||||
if collection.is_locked():
|
||||
collection.unlock()
|
||||
if collection.is_locked(): # User dismissed the prompt
|
||||
raise KeyringLocked("Failed to unlock the collection!")
|
||||
return collection
|
||||
|
||||
def unlock(self, item):
|
||||
if hasattr(item, 'unlock'):
|
||||
item.unlock()
|
||||
if item.is_locked(): # User dismissed the prompt
|
||||
raise KeyringLocked('Failed to unlock the item!')
|
||||
|
||||
def get_password(self, service, username):
|
||||
"""Get password of the username for the service"""
|
||||
collection = self.get_preferred_collection()
|
||||
with closing(collection.connection):
|
||||
items = collection.search_items(self._query(service, username))
|
||||
for item in items:
|
||||
self.unlock(item)
|
||||
return item.get_secret().decode('utf-8')
|
||||
|
||||
def set_password(self, service, username, password):
|
||||
"""Set password for the username of the service"""
|
||||
collection = self.get_preferred_collection()
|
||||
attributes = self._query(service, username, application=self.appid)
|
||||
label = f"Password for '{username}' on '{service}'"
|
||||
with closing(collection.connection):
|
||||
collection.create_item(label, attributes, password, replace=True)
|
||||
|
||||
def delete_password(self, service, username):
|
||||
"""Delete the stored password (only the first one)"""
|
||||
collection = self.get_preferred_collection()
|
||||
with closing(collection.connection):
|
||||
items = collection.search_items(self._query(service, username))
|
||||
for item in items:
|
||||
return item.delete()
|
||||
raise PasswordDeleteError("No such password!")
|
||||
|
||||
def get_credential(self, service, username):
|
||||
"""Gets the first username and password for a service.
|
||||
Returns a Credential instance
|
||||
|
||||
The username can be omitted, but if there is one, it will use get_password
|
||||
and return a SimpleCredential containing the username and password
|
||||
Otherwise, it will return the first username and password combo that it finds.
|
||||
"""
|
||||
scheme = self.schemes[self.scheme]
|
||||
query = self._query(service, username)
|
||||
collection = self.get_preferred_collection()
|
||||
|
||||
with closing(collection.connection):
|
||||
items = collection.search_items(query)
|
||||
for item in items:
|
||||
self.unlock(item)
|
||||
username = item.get_attributes().get(scheme['username'])
|
||||
return SimpleCredential(username, item.get_secret().decode('utf-8'))
|
||||
168
env/lib/python3.10/site-packages/keyring/backends/Windows.py
vendored
Normal file
168
env/lib/python3.10/site-packages/keyring/backends/Windows.py
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from jaraco.context import ExceptionTrap
|
||||
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
from ..credentials import SimpleCredential
|
||||
from ..errors import PasswordDeleteError
|
||||
|
||||
with ExceptionTrap() as missing_deps:
|
||||
try:
|
||||
# prefer pywin32-ctypes
|
||||
from win32ctypes.pywin32 import pywintypes, win32cred
|
||||
|
||||
# force demand import to raise ImportError
|
||||
win32cred.__name__ # noqa: B018
|
||||
except ImportError:
|
||||
# fallback to pywin32
|
||||
import pywintypes
|
||||
import win32cred
|
||||
|
||||
# force demand import to raise ImportError
|
||||
win32cred.__name__ # noqa: B018
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Persistence:
|
||||
def __get__(self, keyring, type=None):
|
||||
return getattr(keyring, '_persist', win32cred.CRED_PERSIST_ENTERPRISE)
|
||||
|
||||
def __set__(self, keyring, value):
|
||||
"""
|
||||
Set the persistence value on the Keyring. Value may be
|
||||
one of the win32cred.CRED_PERSIST_* constants or a
|
||||
string representing one of those constants. For example,
|
||||
'local machine' or 'session'.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
attr = 'CRED_PERSIST_' + value.replace(' ', '_').upper()
|
||||
value = getattr(win32cred, attr)
|
||||
keyring._persist = value
|
||||
|
||||
|
||||
class DecodingCredential(dict):
|
||||
@property
|
||||
def value(self):
|
||||
"""
|
||||
Attempt to decode the credential blob as UTF-16 then UTF-8.
|
||||
"""
|
||||
cred = self['CredentialBlob']
|
||||
try:
|
||||
return cred.decode('utf-16')
|
||||
except UnicodeDecodeError:
|
||||
decoded_cred_utf8 = cred.decode('utf-8')
|
||||
log.warning(
|
||||
"Retrieved a UTF-8 encoded credential. Please be aware that "
|
||||
"this library only writes credentials in UTF-16."
|
||||
)
|
||||
return decoded_cred_utf8
|
||||
|
||||
|
||||
class WinVaultKeyring(KeyringBackend):
|
||||
"""
|
||||
WinVaultKeyring stores encrypted passwords using the Windows Credential
|
||||
Manager.
|
||||
|
||||
Requires pywin32
|
||||
|
||||
This backend does some gymnastics to simulate multi-user support,
|
||||
which WinVault doesn't support natively. See
|
||||
https://github.com/jaraco/keyring/issues/47#issuecomment-75763152
|
||||
for details on the implementation, but here's the gist:
|
||||
|
||||
Passwords are stored under the service name unless there is a collision
|
||||
(another password with the same service name but different user name),
|
||||
in which case the previous password is moved into a compound name:
|
||||
{username}@{service}
|
||||
"""
|
||||
|
||||
persist = Persistence()
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
"""
|
||||
If available, the preferred backend on Windows.
|
||||
"""
|
||||
if missing_deps:
|
||||
raise RuntimeError("Requires Windows and pywin32")
|
||||
return 5
|
||||
|
||||
@staticmethod
|
||||
def _compound_name(username, service):
|
||||
return f'{username}@{service}'
|
||||
|
||||
def get_password(self, service, username):
|
||||
res = self._resolve_credential(service, username)
|
||||
return res and res.value
|
||||
|
||||
def _resolve_credential(
|
||||
self, service: str, username: str | None
|
||||
) -> DecodingCredential | None:
|
||||
# first attempt to get the password under the service name
|
||||
res = self._read_credential(service)
|
||||
if not res or username and res['UserName'] != username:
|
||||
# It wasn't found so attempt to get it with the compound name
|
||||
res = self._read_credential(self._compound_name(username, service))
|
||||
return res
|
||||
|
||||
def _read_credential(self, target):
|
||||
try:
|
||||
res = win32cred.CredRead(
|
||||
Type=win32cred.CRED_TYPE_GENERIC, TargetName=target
|
||||
)
|
||||
except pywintypes.error as e:
|
||||
if e.winerror == 1168 and e.funcname == 'CredRead': # not found
|
||||
return None
|
||||
raise
|
||||
return DecodingCredential(res)
|
||||
|
||||
def set_password(self, service, username, password):
|
||||
existing_pw = self._read_credential(service)
|
||||
if existing_pw:
|
||||
# resave the existing password using a compound target
|
||||
existing_username = existing_pw['UserName']
|
||||
target = self._compound_name(existing_username, service)
|
||||
self._set_password(
|
||||
target,
|
||||
existing_username,
|
||||
existing_pw.value,
|
||||
)
|
||||
self._set_password(service, username, str(password))
|
||||
|
||||
def _set_password(self, target, username, password):
|
||||
credential = dict(
|
||||
Type=win32cred.CRED_TYPE_GENERIC,
|
||||
TargetName=target,
|
||||
UserName=username,
|
||||
CredentialBlob=password,
|
||||
Comment="Stored using python-keyring",
|
||||
Persist=self.persist,
|
||||
)
|
||||
win32cred.CredWrite(credential, 0)
|
||||
|
||||
def delete_password(self, service, username):
|
||||
compound = self._compound_name(username, service)
|
||||
deleted = False
|
||||
for target in service, compound:
|
||||
existing_pw = self._read_credential(target)
|
||||
if existing_pw and existing_pw['UserName'] == username:
|
||||
deleted = True
|
||||
self._delete_password(target)
|
||||
if not deleted:
|
||||
raise PasswordDeleteError(service)
|
||||
|
||||
def _delete_password(self, target):
|
||||
try:
|
||||
win32cred.CredDelete(Type=win32cred.CRED_TYPE_GENERIC, TargetName=target)
|
||||
except pywintypes.error as e:
|
||||
if e.winerror == 1168 and e.funcname == 'CredDelete': # not found
|
||||
return
|
||||
raise
|
||||
|
||||
def get_credential(self, service, username):
|
||||
res = self._resolve_credential(service, username)
|
||||
return res and SimpleCredential(res['UserName'], res.value)
|
||||
0
env/lib/python3.10/site-packages/keyring/backends/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/keyring/backends/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/SecretService.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/SecretService.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/Windows.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/Windows.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/chainer.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/chainer.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/fail.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/fail.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/kwallet.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/kwallet.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/libsecret.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/libsecret.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/null.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/keyring/backends/__pycache__/null.cpython-310.pyc
vendored
Normal file
Binary file not shown.
71
env/lib/python3.10/site-packages/keyring/backends/chainer.py
vendored
Normal file
71
env/lib/python3.10/site-packages/keyring/backends/chainer.py
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Keyring Chainer - iterates over other viable backends to
|
||||
discover passwords in each.
|
||||
"""
|
||||
|
||||
from .. import backend
|
||||
from ..compat import properties
|
||||
from . import fail
|
||||
|
||||
|
||||
class ChainerBackend(backend.KeyringBackend):
|
||||
"""
|
||||
>>> ChainerBackend()
|
||||
<keyring.backends.chainer.ChainerBackend object at ...>
|
||||
"""
|
||||
|
||||
# override viability as 'priority' cannot be determined
|
||||
# until other backends have been constructed
|
||||
viable = True
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
"""
|
||||
If there are backends to chain, high priority
|
||||
Otherwise very low priority since our operation when empty
|
||||
is the same as null.
|
||||
"""
|
||||
return 10 if len(cls.backends) > 1 else (fail.Keyring.priority - 1)
|
||||
|
||||
@properties.classproperty
|
||||
def backends(cls):
|
||||
"""
|
||||
Discover all keyrings for chaining.
|
||||
"""
|
||||
|
||||
def allow(keyring):
|
||||
limit = backend._limit or bool
|
||||
return (
|
||||
not isinstance(keyring, ChainerBackend)
|
||||
and limit(keyring)
|
||||
and keyring.priority > 0
|
||||
)
|
||||
|
||||
allowed = filter(allow, backend.get_all_keyring())
|
||||
return sorted(allowed, key=backend.by_priority, reverse=True)
|
||||
|
||||
def get_password(self, service, username):
|
||||
for keyring in self.backends:
|
||||
password = keyring.get_password(service, username)
|
||||
if password is not None:
|
||||
return password
|
||||
|
||||
def set_password(self, service, username, password):
|
||||
for keyring in self.backends:
|
||||
try:
|
||||
return keyring.set_password(service, username, password)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
def delete_password(self, service, username):
|
||||
for keyring in self.backends:
|
||||
try:
|
||||
return keyring.delete_password(service, username)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
def get_credential(self, service, username):
|
||||
for keyring in self.backends:
|
||||
credential = keyring.get_credential(service, username)
|
||||
if credential is not None:
|
||||
return credential
|
||||
30
env/lib/python3.10/site-packages/keyring/backends/fail.py
vendored
Normal file
30
env/lib/python3.10/site-packages/keyring/backends/fail.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
from ..errors import NoKeyringError
|
||||
|
||||
|
||||
class Keyring(KeyringBackend):
|
||||
"""
|
||||
Keyring that raises error on every operation.
|
||||
|
||||
>>> kr = Keyring()
|
||||
>>> kr.get_password('svc', 'user')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
keyring.errors.NoKeyringError: ...No recommended backend...
|
||||
"""
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
return 0
|
||||
|
||||
def get_password(self, service, username, password=None):
|
||||
msg = (
|
||||
"No recommended backend was available. Install a recommended 3rd "
|
||||
"party backend package; or, install the keyrings.alt package if "
|
||||
"you want to use the non-recommended backends. See "
|
||||
"https://pypi.org/project/keyring for details."
|
||||
)
|
||||
raise NoKeyringError(msg)
|
||||
|
||||
set_password = delete_password = get_password
|
||||
164
env/lib/python3.10/site-packages/keyring/backends/kwallet.py
vendored
Normal file
164
env/lib/python3.10/site-packages/keyring/backends/kwallet.py
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
from ..credentials import SimpleCredential
|
||||
from ..errors import InitError, KeyringLocked, PasswordDeleteError, PasswordSetError
|
||||
|
||||
try:
|
||||
import dbus
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
except ImportError:
|
||||
pass
|
||||
except AttributeError:
|
||||
# See https://github.com/jaraco/keyring/issues/296
|
||||
pass
|
||||
|
||||
|
||||
def _id_from_argv():
|
||||
"""
|
||||
Safely infer an app id from sys.argv.
|
||||
"""
|
||||
allowed = AttributeError, IndexError, TypeError
|
||||
with contextlib.suppress(allowed):
|
||||
return sys.argv[0]
|
||||
|
||||
|
||||
class DBusKeyring(KeyringBackend):
|
||||
"""
|
||||
KDE KWallet 5 via D-Bus
|
||||
"""
|
||||
|
||||
appid = _id_from_argv() or 'Python keyring library'
|
||||
wallet = None
|
||||
bus_name = 'org.kde.kwalletd5'
|
||||
object_path = '/modules/kwalletd5'
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
if 'dbus' not in globals():
|
||||
raise RuntimeError('python-dbus not installed')
|
||||
try:
|
||||
bus = dbus.SessionBus(mainloop=DBusGMainLoop())
|
||||
except dbus.DBusException as exc:
|
||||
raise RuntimeError(exc.get_dbus_message()) from exc
|
||||
if not (
|
||||
bus.name_has_owner(cls.bus_name)
|
||||
or cls.bus_name in bus.list_activatable_names()
|
||||
):
|
||||
raise RuntimeError(
|
||||
"The KWallet daemon is neither running nor activatable through D-Bus"
|
||||
)
|
||||
if "KDE" in os.getenv("XDG_CURRENT_DESKTOP", "").split(":"):
|
||||
return 5.1
|
||||
return 4.9
|
||||
|
||||
def __init__(self, *arg, **kw):
|
||||
super().__init__(*arg, **kw)
|
||||
self.handle = -1
|
||||
|
||||
def _migrate(self, service):
|
||||
old_folder = 'Python'
|
||||
entry_list = []
|
||||
if self.iface.hasFolder(self.handle, old_folder, self.appid):
|
||||
entry_list = self.iface.readPasswordList(
|
||||
self.handle, old_folder, '*@*', self.appid
|
||||
)
|
||||
|
||||
for entry in entry_list.items():
|
||||
key = entry[0]
|
||||
password = entry[1]
|
||||
|
||||
username, service = key.rsplit('@', 1)
|
||||
ret = self.iface.writePassword(
|
||||
self.handle, service, username, password, self.appid
|
||||
)
|
||||
if ret == 0:
|
||||
self.iface.removeEntry(self.handle, old_folder, key, self.appid)
|
||||
|
||||
entry_list = self.iface.readPasswordList(
|
||||
self.handle, old_folder, '*', self.appid
|
||||
)
|
||||
if not entry_list:
|
||||
self.iface.removeFolder(self.handle, old_folder, self.appid)
|
||||
|
||||
def connected(self, service):
|
||||
if self.handle >= 0:
|
||||
if self.iface.isOpen(self.handle):
|
||||
return True
|
||||
|
||||
bus = dbus.SessionBus(mainloop=DBusGMainLoop())
|
||||
wId = 0
|
||||
try:
|
||||
remote_obj = bus.get_object(self.bus_name, self.object_path)
|
||||
self.iface = dbus.Interface(remote_obj, 'org.kde.KWallet')
|
||||
self.handle = self.iface.open(self.iface.networkWallet(), wId, self.appid)
|
||||
except dbus.DBusException as e:
|
||||
raise InitError(f'Failed to open keyring: {e}.') from e
|
||||
|
||||
if self.handle < 0:
|
||||
return False
|
||||
self._migrate(service)
|
||||
return True
|
||||
|
||||
def get_password(self, service, username):
|
||||
"""Get password of the username for the service"""
|
||||
if not self.connected(service):
|
||||
# the user pressed "cancel" when prompted to unlock their keyring.
|
||||
raise KeyringLocked("Failed to unlock the keyring!")
|
||||
if not self.iface.hasEntry(self.handle, service, username, self.appid):
|
||||
return None
|
||||
password = self.iface.readPassword(self.handle, service, username, self.appid)
|
||||
return str(password)
|
||||
|
||||
def get_credential(self, service, username):
|
||||
"""Gets the first username and password for a service.
|
||||
Returns a Credential instance
|
||||
|
||||
The username can be omitted, but if there is one, it will forward to
|
||||
get_password.
|
||||
Otherwise, it will return the first username and password combo that it finds.
|
||||
"""
|
||||
if username is not None:
|
||||
return super().get_credential(service, username)
|
||||
|
||||
if not self.connected(service):
|
||||
# the user pressed "cancel" when prompted to unlock their keyring.
|
||||
raise KeyringLocked("Failed to unlock the keyring!")
|
||||
|
||||
for username in self.iface.entryList(self.handle, service, self.appid):
|
||||
password = self.iface.readPassword(
|
||||
self.handle, service, username, self.appid
|
||||
)
|
||||
return SimpleCredential(str(username), str(password))
|
||||
|
||||
def set_password(self, service, username, password):
|
||||
"""Set password for the username of the service"""
|
||||
if not self.connected(service):
|
||||
# the user pressed "cancel" when prompted to unlock their keyring.
|
||||
raise PasswordSetError("Cancelled by user")
|
||||
self.iface.writePassword(self.handle, service, username, password, self.appid)
|
||||
|
||||
def delete_password(self, service, username):
|
||||
"""Delete the password for the username of the service."""
|
||||
if not self.connected(service):
|
||||
# the user pressed "cancel" when prompted to unlock their keyring.
|
||||
raise PasswordDeleteError("Cancelled by user")
|
||||
if not self.iface.hasEntry(self.handle, service, username, self.appid):
|
||||
raise PasswordDeleteError("Password not found")
|
||||
self.iface.removeEntry(self.handle, service, username, self.appid)
|
||||
|
||||
|
||||
class DBusKeyringKWallet4(DBusKeyring):
|
||||
"""
|
||||
KDE KWallet 4 via D-Bus
|
||||
"""
|
||||
|
||||
bus_name = 'org.kde.kwalletd'
|
||||
object_path = '/modules/kwalletd'
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls):
|
||||
return super().priority - 1
|
||||
155
env/lib/python3.10/site-packages/keyring/backends/libsecret.py
vendored
Normal file
155
env/lib/python3.10/site-packages/keyring/backends/libsecret.py
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
import logging
|
||||
|
||||
from .. import backend
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
from ..credentials import SimpleCredential
|
||||
from ..errors import (
|
||||
KeyringLocked,
|
||||
PasswordDeleteError,
|
||||
PasswordSetError,
|
||||
)
|
||||
|
||||
available = False
|
||||
try:
|
||||
import gi
|
||||
from gi.repository import Gio, GLib
|
||||
|
||||
gi.require_version('Secret', '1')
|
||||
from gi.repository import Secret
|
||||
|
||||
available = True
|
||||
except (AttributeError, ImportError, ValueError):
|
||||
pass
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Keyring(backend.SchemeSelectable, KeyringBackend):
|
||||
"""libsecret Keyring"""
|
||||
|
||||
appid = 'Python keyring library'
|
||||
|
||||
@property
|
||||
def schema(self):
|
||||
return Secret.Schema.new(
|
||||
"org.freedesktop.Secret.Generic",
|
||||
Secret.SchemaFlags.NONE,
|
||||
self._query(
|
||||
Secret.SchemaAttributeType.STRING,
|
||||
Secret.SchemaAttributeType.STRING,
|
||||
application=Secret.SchemaAttributeType.STRING,
|
||||
),
|
||||
)
|
||||
|
||||
@properties.NonDataProperty
|
||||
def collection(self):
|
||||
return Secret.COLLECTION_DEFAULT
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
if not available:
|
||||
raise RuntimeError("libsecret required")
|
||||
|
||||
# Make sure there is actually a secret service running
|
||||
try:
|
||||
Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION, None)
|
||||
except GLib.Error as error:
|
||||
raise RuntimeError("Can't open a session to the secret service") from error
|
||||
|
||||
return 4.8
|
||||
|
||||
def get_password(self, service, username):
|
||||
"""Get password of the username for the service"""
|
||||
attributes = self._query(service, username, application=self.appid)
|
||||
try:
|
||||
items = Secret.password_search_sync(
|
||||
self.schema, attributes, Secret.SearchFlags.UNLOCK, None
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('g-io-error-quark')
|
||||
if error.matches(quark, Gio.IOErrorEnum.FAILED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
for item in items:
|
||||
try:
|
||||
return item.retrieve_secret_sync().get_text()
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('secret-error')
|
||||
if error.matches(quark, Secret.Error.IS_LOCKED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
|
||||
def set_password(self, service, username, password):
|
||||
"""Set password for the username of the service"""
|
||||
attributes = self._query(service, username, application=self.appid)
|
||||
label = f"Password for '{username}' on '{service}'"
|
||||
try:
|
||||
stored = Secret.password_store_sync(
|
||||
self.schema, attributes, self.collection, label, password, None
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('secret-error')
|
||||
if error.matches(quark, Secret.Error.IS_LOCKED):
|
||||
raise KeyringLocked("Failed to unlock the collection!") from error
|
||||
quark = GLib.quark_try_string('g-io-error-quark')
|
||||
if error.matches(quark, Gio.IOErrorEnum.FAILED):
|
||||
raise KeyringLocked("Failed to unlock the collection!") from error
|
||||
raise
|
||||
if not stored:
|
||||
raise PasswordSetError("Failed to store password!")
|
||||
|
||||
def delete_password(self, service, username):
|
||||
"""Delete the stored password (only the first one)"""
|
||||
attributes = self._query(service, username, application=self.appid)
|
||||
try:
|
||||
items = Secret.password_search_sync(
|
||||
self.schema, attributes, Secret.SearchFlags.UNLOCK, None
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('g-io-error-quark')
|
||||
if error.matches(quark, Gio.IOErrorEnum.FAILED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
for item in items:
|
||||
try:
|
||||
removed = Secret.password_clear_sync(
|
||||
self.schema, item.get_attributes(), None
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('secret-error')
|
||||
if error.matches(quark, Secret.Error.IS_LOCKED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
return removed
|
||||
raise PasswordDeleteError("No such password!")
|
||||
|
||||
def get_credential(self, service, username):
|
||||
"""Get the first username and password for a service.
|
||||
Return a Credential instance
|
||||
|
||||
The username can be omitted, but if there is one, it will use get_password
|
||||
and return a SimpleCredential containing the username and password
|
||||
Otherwise, it will return the first username and password combo that it finds.
|
||||
"""
|
||||
query = self._query(service, username)
|
||||
try:
|
||||
items = Secret.password_search_sync(
|
||||
self.schema, query, Secret.SearchFlags.UNLOCK, None
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('g-io-error-quark')
|
||||
if error.matches(quark, Gio.IOErrorEnum.FAILED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
for item in items:
|
||||
username = item.get_attributes().get("username")
|
||||
try:
|
||||
return SimpleCredential(
|
||||
username, item.retrieve_secret_sync().get_text()
|
||||
)
|
||||
except GLib.Error as error:
|
||||
quark = GLib.quark_try_string('secret-error')
|
||||
if error.matches(quark, Secret.Error.IS_LOCKED):
|
||||
raise KeyringLocked('Failed to unlock the item!') from error
|
||||
raise
|
||||
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)
|
||||
20
env/lib/python3.10/site-packages/keyring/backends/null.py
vendored
Normal file
20
env/lib/python3.10/site-packages/keyring/backends/null.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from ..backend import KeyringBackend
|
||||
from ..compat import properties
|
||||
|
||||
|
||||
class Keyring(KeyringBackend):
|
||||
"""
|
||||
Keyring that return None on every operation.
|
||||
|
||||
>>> kr = Keyring()
|
||||
>>> kr.get_password('svc', 'user')
|
||||
"""
|
||||
|
||||
@properties.classproperty
|
||||
def priority(cls) -> float:
|
||||
return -1
|
||||
|
||||
def get_password(self, service, username, password=None):
|
||||
pass
|
||||
|
||||
set_password = delete_password = get_password
|
||||
Reference in New Issue
Block a user