mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-07 02:39:01 +08:00

When Cura is starting up, it reads the authentication data from the preferences (cura.cfg). If the auth tokens have previously been stored in the keyring, it means that their values will be null in the cura.cfg file. Therefore, on startup, Cura reads the tokens as none from the preferences and then sets the empty values in the keyring as tokens. This leads to the user being signed off every time Cura restarts on Mac. On Windows, the access token was still stored in the preferences, so on startup it was safe. The refresh token, on the other hand, had the same issue as on Mac, which means that on startup it was read as None from the cura.cfg and was stored in the keyring as an empty string. This meant that, even though on startup (on windows) the user was kept signed in, the next time Cura was attempting to refresh the access token (after 7-8 minutes), it wouldn't be able, since its refresh token was read as "" from the keyring. Also, if the user would close Cura and reopen it after 10 minutes (so after the access token had expired) then they would be signed off on windows too. This commit fixes that by making sure that if the given value of the refresh and access tokens are empty, then they will not be stored in the keyring. CURA-8178
80 lines
3.2 KiB
Python
80 lines
3.2 KiB
Python
# Copyright (c) 2021 Ultimaker B.V.
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
from typing import Type, TYPE_CHECKING, Optional, List
|
|
|
|
import keyring
|
|
from keyring.backend import KeyringBackend
|
|
from keyring.errors import NoKeyringError, PasswordSetError
|
|
|
|
from UM.Logger import Logger
|
|
|
|
if TYPE_CHECKING:
|
|
from cura.OAuth2.Models import BaseModel
|
|
|
|
# Need to do some extra workarounds on windows:
|
|
import sys
|
|
from UM.Platform import Platform
|
|
if Platform.isWindows() and hasattr(sys, "frozen"):
|
|
import win32timezone
|
|
from keyring.backends.Windows import WinVaultKeyring
|
|
keyring.set_keyring(WinVaultKeyring())
|
|
if Platform.isOSX() and hasattr(sys, "frozen"):
|
|
from keyring.backends.macOS import Keyring
|
|
keyring.set_keyring(Keyring())
|
|
|
|
# Even if errors happen, we don't want this stored locally:
|
|
DONT_EVER_STORE_LOCALLY: List[str] = ["refresh_token"]
|
|
|
|
|
|
class KeyringAttribute:
|
|
"""
|
|
Descriptor for attributes that need to be stored in the keyring. With Fallback behaviour to the preference cfg file
|
|
"""
|
|
def __get__(self, instance: "BaseModel", owner: type) -> Optional[str]:
|
|
if self._store_secure: # type: ignore
|
|
try:
|
|
value = keyring.get_password("cura", self._keyring_name)
|
|
return value if value != "" else None
|
|
except NoKeyringError:
|
|
self._store_secure = False
|
|
Logger.logException("w", "No keyring backend present")
|
|
return getattr(instance, self._name)
|
|
else:
|
|
return getattr(instance, self._name)
|
|
|
|
def __set__(self, instance: "BaseModel", value: Optional[str]):
|
|
if self._store_secure:
|
|
setattr(instance, self._name, None)
|
|
if value is not None:
|
|
try:
|
|
keyring.set_password("cura", self._keyring_name, value)
|
|
except PasswordSetError:
|
|
self._store_secure = False
|
|
if self._name not in DONT_EVER_STORE_LOCALLY:
|
|
setattr(instance, self._name, value)
|
|
Logger.logException("w", "Keyring access denied")
|
|
except NoKeyringError:
|
|
self._store_secure = False
|
|
if self._name not in DONT_EVER_STORE_LOCALLY:
|
|
setattr(instance, self._name, value)
|
|
Logger.logException("w", "No keyring backend present")
|
|
except BaseException as e:
|
|
# A BaseException can occur in Windows when the keyring attempts to write a token longer than 1024
|
|
# characters in the Windows Credentials Manager.
|
|
self._store_secure = False
|
|
if self._name not in DONT_EVER_STORE_LOCALLY:
|
|
setattr(instance, self._name, value)
|
|
Logger.log("w", "Keyring failed: {}".format(e))
|
|
else:
|
|
setattr(instance, self._name, value)
|
|
|
|
def __set_name__(self, owner: type, name: str):
|
|
self._name = "_{}".format(name)
|
|
self._keyring_name = name
|
|
self._store_secure = False
|
|
try:
|
|
self._store_secure = KeyringBackend.viable
|
|
except NoKeyringError:
|
|
Logger.logException("w", "Could not use keyring")
|
|
setattr(owner, self._name, None)
|