KeyringAttribute.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. # Copyright (c) 2021 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Type, TYPE_CHECKING
  4. import keyring
  5. from keyring.backend import KeyringBackend
  6. from keyring.errors import NoKeyringError, PasswordSetError
  7. from UM.Logger import Logger
  8. if TYPE_CHECKING:
  9. from cura.OAuth2.Models import BaseModel
  10. # Need to do some extra workarounds on windows:
  11. import sys
  12. from UM.Platform import Platform
  13. if Platform.isWindows() and hasattr(sys, "frozen"):
  14. import win32timezone
  15. from keyring.backends.Windows import WinVaultKeyring
  16. keyring.set_keyring(WinVaultKeyring())
  17. # Even if errors happen, we don't want this stored locally:
  18. DONT_EVER_STORE_LOCALLY = ["refresh_token"]
  19. class KeyringAttribute:
  20. """
  21. Descriptor for attributes that need to be stored in the keyring. With Fallback behaviour to the preference cfg file
  22. """
  23. def __get__(self, instance: Type["BaseModel"], owner: type) -> str:
  24. if self._store_secure:
  25. try:
  26. return keyring.get_password("cura", self._keyring_name)
  27. except NoKeyringError:
  28. self._store_secure = False
  29. Logger.logException("w", "No keyring backend present")
  30. return getattr(instance, self._name)
  31. else:
  32. return getattr(instance, self._name)
  33. def __set__(self, instance: Type["BaseModel"], value: str):
  34. if self._store_secure:
  35. setattr(instance, self._name, None)
  36. try:
  37. keyring.set_password("cura", self._keyring_name, value)
  38. except PasswordSetError:
  39. self._store_secure = False
  40. if self._name not in DONT_EVER_STORE_LOCALLY:
  41. setattr(instance, self._name, value)
  42. Logger.logException("w", "Keyring access denied")
  43. except NoKeyringError:
  44. self._store_secure = False
  45. if self._name not in DONT_EVER_STORE_LOCALLY:
  46. setattr(instance, self._name, value)
  47. Logger.logException("w", "No keyring backend present")
  48. except BaseException as e:
  49. # A BaseException can occur in Windows when the keyring attempts to write a token longer than 1024
  50. # characters in the Windows Credentials Manager.
  51. self._store_secure = False
  52. if self._name not in DONT_EVER_STORE_LOCALLY:
  53. setattr(instance, self._name, value)
  54. Logger.log("w", "Keyring failed: {}".format(e))
  55. else:
  56. setattr(instance, self._name, value)
  57. def __set_name__(self, owner: type, name: str):
  58. self._name = "_{}".format(name)
  59. self._keyring_name = name
  60. self._store_secure = False
  61. try:
  62. self._store_secure = KeyringBackend.viable
  63. except NoKeyringError:
  64. Logger.logException("w", "Could not use keyring")
  65. setattr(owner, self._name, None)