diff --git a/cura/OAuth2/AuthorizationHelpers.py b/cura/OAuth2/AuthorizationHelpers.py index 569ba80659..3e7dabaf08 100644 --- a/cura/OAuth2/AuthorizationHelpers.py +++ b/cura/OAuth2/AuthorizationHelpers.py @@ -16,6 +16,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To downlo catalog = i18nCatalog("cura") TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S" +REQUEST_TIMEOUT = 5 # Seconds class AuthorizationHelpers: @@ -53,7 +54,8 @@ class AuthorizationHelpers: data = urllib.parse.urlencode(data).encode("UTF-8"), headers_dict = headers, callback = lambda response: self.parseTokenResponse(response, callback), - error_callback = lambda response, _: self.parseTokenResponse(response, callback) + error_callback = lambda response, _: self.parseTokenResponse(response, callback), + timeout = REQUEST_TIMEOUT ) def getAccessTokenUsingRefreshToken(self, refresh_token: str, callback: Callable[[AuthenticationResponse], None]) -> None: @@ -77,7 +79,9 @@ class AuthorizationHelpers: data = urllib.parse.urlencode(data).encode("UTF-8"), headers_dict = headers, callback = lambda response: self.parseTokenResponse(response, callback), - error_callback = lambda response, _: self.parseTokenResponse(response, callback) + error_callback = lambda response, _: self.parseTokenResponse(response, callback), + urgent = True, + timeout = REQUEST_TIMEOUT ) def parseTokenResponse(self, token_response: QNetworkReply, callback: Callable[[AuthenticationResponse], None]) -> None: @@ -122,7 +126,8 @@ class AuthorizationHelpers: check_token_url, headers_dict = headers, callback = lambda reply: self._parseUserProfile(reply, success_callback, failed_callback), - error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None + error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None, + timeout = REQUEST_TIMEOUT ) def _parseUserProfile(self, reply: QNetworkReply, success_callback: Optional[Callable[[UserProfile], None]], failed_callback: Optional[Callable[[], None]] = None) -> None: diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 06478e911b..4e8d28173a 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2024 UltiMaker # Cura is released under the terms of the LGPLv3 or higher. import json @@ -6,13 +6,14 @@ from datetime import datetime, timedelta from typing import Callable, Dict, Optional, TYPE_CHECKING, Union from urllib.parse import urlencode, quote_plus -from PyQt6.QtCore import QUrl +from PyQt6.QtCore import QUrl, QTimer from PyQt6.QtGui import QDesktopServices from UM.Logger import Logger from UM.Message import Message from UM.Signal import Signal from UM.i18n import i18nCatalog +from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To download log-in tokens. from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers, TOKEN_TIMESTAMP_FORMAT from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer from cura.OAuth2.Models import AuthenticationResponse, BaseModel @@ -25,6 +26,8 @@ if TYPE_CHECKING: MYCLOUD_LOGOFF_URL = "https://account.ultimaker.com/logoff?utm_source=cura&utm_medium=software&utm_campaign=change-account-before-adding-printers" +REFRESH_TOKEN_MAX_RETRIES = 15 +REFRESH_TOKEN_RETRY_INTERVAL = 1000 class AuthorizationService: """The authorization service is responsible for handling the login flow, storing user credentials and providing @@ -57,6 +60,12 @@ class AuthorizationService: self.onAuthStateChanged.connect(self._authChanged) + self._refresh_token_retries = 0 + self._refresh_token_retry_timer = QTimer() + self._refresh_token_retry_timer.setInterval(REFRESH_TOKEN_RETRY_INTERVAL) + self._refresh_token_retry_timer.setSingleShot(True) + self._refresh_token_retry_timer.timeout.connect(self.refreshAccessToken) + def _authChanged(self, logged_in): if logged_in and self._unable_to_get_data_message is not None: self._unable_to_get_data_message.hide() @@ -167,16 +176,29 @@ class AuthorizationService: return def process_auth_data(response: AuthenticationResponse) -> None: + self._currently_refreshing_token = False + if response.success: + self._refresh_token_retries = 0 self._storeAuthData(response) + HttpRequestManager.getInstance().setDelayRequests(False) self.onAuthStateChanged.emit(logged_in = True) else: - Logger.warning("Failed to get a new access token from the server.") - self.onAuthStateChanged.emit(logged_in = False) + if self._refresh_token_retries >= REFRESH_TOKEN_MAX_RETRIES: + self._refresh_token_retries = 0 + Logger.warning("Failed to get a new access token from the server, giving up.") + HttpRequestManager.getInstance().setDelayRequests(False) + self.onAuthStateChanged.emit(logged_in = False) + else: + # Retry a bit later, network may be offline right now and will hopefully be back soon + Logger.warning("Failed to get a new access token from the server, retrying later.") + self._refresh_token_retries += 1 + self._refresh_token_retry_timer.start() if self._currently_refreshing_token: Logger.debug("Was already busy refreshing token. Do not start a new request.") return + HttpRequestManager.getInstance().setDelayRequests(True) self._currently_refreshing_token = True self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token, process_auth_data) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 20a8a698a2..2f63bf5798 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5135,7 +5135,7 @@ "unit": "mm", "type": "float", "minimum_value": "0", - "maximum_value_warning": "machine_nozzle_size", + "maximum_value_warning": "5*layer_height", "default_value": 0.1, "limit_to_extruder": "support_interface_extruder_nr if support_interface_enable else support_infill_extruder_nr", "enabled": "support_enable or support_meshes_present",