From db63ad4985b756d4b565fed58935b0d58eae97ca Mon Sep 17 00:00:00 2001 From: kevforget Date: Mon, 22 Nov 2021 16:25:03 +0100 Subject: [PATCH 1/2] feat: fitbit better user disconnect management --- tapiriik/services/Fitbit/fitbit.py | 34 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tapiriik/services/Fitbit/fitbit.py b/tapiriik/services/Fitbit/fitbit.py index 3f117e0a..949c644e 100644 --- a/tapiriik/services/Fitbit/fitbit.py +++ b/tapiriik/services/Fitbit/fitbit.py @@ -306,25 +306,29 @@ def _requestWithAuth(self, reqLambda, serviceRecord): }) if response.status_code != 200: - raise APIException("No authorization to refresh token", block=True, - user_exception=UserException(UserExceptionType.Authorization, - intervention_required=True)) + if response.status_code == 401 or response.status_code == 403: + raise APIException("%i - No authorization to refresh token for the user with FITBIT ID : %s" %(response.status_code, serviceRecord.ExternalID), block=True, + user_exception=UserException(UserExceptionType.Authorization, + intervention_required=True)) + else: + raise APIException("%i - Can't refresh token (for an undefined reason) for the user with FITBIT ID : %s" %(response.status_code, serviceRecord.ExternalID)) + else: - data = response.json() + data = response.json() - now = datetime.now(timezone.utc) - endDate = now + timedelta(seconds=data['expires_in']) + now = datetime.now(timezone.utc) + endDate = now + timedelta(seconds=data['expires_in']) - authorizationData = { - "AccessToken": data["access_token"], - "AccessTokenRequestedAt": now, - "AccessTokenExpiresAt": endDate, - "RefreshToken": data["refresh_token"], - 'TokenType': data['token_type'] - } + authorizationData = { + "AccessToken": data["access_token"], + "AccessTokenRequestedAt": now, + "AccessTokenExpiresAt": endDate, + "RefreshToken": data["refresh_token"], + 'TokenType': data['token_type'] + } - serviceRecord.Authorization.update(authorizationData) - db.connections.update({"_id": serviceRecord._id}, {"$set": {"Authorization": authorizationData}}) + serviceRecord.Authorization.update(authorizationData) + db.connections.update({"_id": serviceRecord._id}, {"$set": {"Authorization": authorizationData}}) #session.headers.update({"Authorization": "access_token %s" % serviceRecord.Authorization["AccessToken"]}) return reqLambda(session) From 228ee2b15e4c1bf322f0220de58978dfc3f7851c Mon Sep 17 00:00:00 2001 From: kevforget Date: Tue, 23 Nov 2021 15:51:31 +0100 Subject: [PATCH 2/2] feat: sending if user connections has auth error through api on providers endpoint --- tapiriik/services/service_record.py | 6 ++++++ tapiriik/web/views/api/providers.py | 22 ++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tapiriik/services/service_record.py b/tapiriik/services/service_record.py index 65b7b48c..f711ee1b 100644 --- a/tapiriik/services/service_record.py +++ b/tapiriik/services/service_record.py @@ -40,6 +40,12 @@ def HasExtendedAuthorizationDetails(self, persisted_only=False): return False return cachedb.extendedAuthDetails.find({"ID": self._id}).limit(1).count() + def HasAuthSyncError(self): + if hasattr(self, "SyncErrors"): + return next((se for se in self.SyncErrors if se["UserException"]["InterventionRequired"] and se["Block"] and se["UserException"]["Type"] == "auth"), False) is not False + else: + return False + def SetPartialSyncTriggerSubscriptionState(self, subscribed): db.connections.update_one({"_id": self._id}, {"$set": {"PartialSyncTriggerSubscribed": subscribed}}) diff --git a/tapiriik/web/views/api/providers.py b/tapiriik/web/views/api/providers.py index 926fceb7..28ba8850 100644 --- a/tapiriik/web/views/api/providers.py +++ b/tapiriik/web/views/api/providers.py @@ -7,8 +7,26 @@ @ensure_csrf_cookie def providers(req): if req.user != None: - user_connections = [conns.get("Service") for conns in req.user.get("ConnectedServices")] - active_providers = [{"id": x.ID, "displayName": x.DisplayName, "isReceiver": x.ReceivesActivities, "isSupplier": x.ProvidesActivities, "isConnected": True if x.ID in user_connections else False, "authURI": x.UserAuthorizationURL} for x in Service.List() if x.ID not in WITHDRAWN_SERVICES and x.ID != "decathlon"] + user_connections = req.user.get("ConnectedServices") + user_connections_name = [connection["Service"] for connection in user_connections] + user_connections_with_auth_error = [ + connection["Service"] + for connection in user_connections + if Service.GetServiceRecordByID(connection["ID"]).HasAuthSyncError() + ] + + active_providers = [ + { + "id": x.ID, + "displayName": x.DisplayName, + "mustReconnect": x.ID in user_connections_with_auth_error, + "isReceiver": x.ReceivesActivities, + "isSupplier": x.ProvidesActivities, + "isConnected": True if x.ID in user_connections_name else False, + "authURI": x.UserAuthorizationURL + } for x in Service.List() if x.ID not in WITHDRAWN_SERVICES and x.ID != "decathlon" + ] + return JsonResponse({"providers": active_providers}) else: return HttpResponse(content="

Unauthorized

" ,status=403)