From 4c52fe22a88f36d882cec045c7086d8f7d3a6248 Mon Sep 17 00:00:00 2001 From: Paul Plant <37302780+paulplant@users.noreply.github.com> Date: Sun, 19 Dec 2021 12:11:47 +0100 Subject: [PATCH] Nightscout token authentication --- .../DexcomShareUploadManager.swift | 4 +- .../NightScout/NightScoutUploadManager.swift | 130 +++++++++++------- .../ar.lproj/NightScoutTestResult.strings | 2 +- .../de.lproj/NightScoutTestResult.strings | 2 +- .../de.lproj/SettingsViews.strings | 2 +- .../en.lproj/NightScoutTestResult.strings | 5 +- .../en.lproj/SettingsViews.strings | 2 +- .../es.lproj/NightScoutTestResult.strings | 5 +- .../es.lproj/SettingsViews.strings | 2 +- .../fi.lproj/NightScoutTestResult.strings | 2 +- .../fr.lproj/NightScoutTestResult.strings | 2 +- .../fr.lproj/SettingsViews.strings | 2 +- .../it.lproj/NightScoutTestResult.strings | 4 +- .../it.lproj/SettingsViews.strings | 2 +- .../nl.lproj/NightScoutTestResult.strings | 2 +- .../nl.lproj/SettingsViews.strings | 2 +- .../pl-PL.lproj/NightScoutTestResult.strings | 4 +- .../pl-PL.lproj/SettingsViews.strings | 2 +- .../pt.lproj/NightScoutTestResult.strings | 2 +- .../pt.lproj/SettingsViews.strings | 2 +- .../ru.lproj/NightScoutTestResult.strings | 4 +- .../ru.lproj/SettingsViews.strings | 2 +- .../sl.lproj/NightScoutTestResult.strings | 4 +- .../sl.lproj/SettingsViews.strings | 2 +- .../sv.lproj/NightScoutTestResult.strings | 2 +- .../sv.lproj/SettingsViews.strings | 2 +- .../zh.lproj/NightScoutTestResult.strings | 4 +- .../zh.lproj/SettingsViews.strings | 2 +- xdrip/Texts/TextsDexcomShareTestResult.swift | 4 +- xdrip/Texts/TextsNightScoutTestResult.swift | 14 +- xdrip/Texts/TextsSettingsView.swift | 4 +- ...tingsViewNightScoutSettingsViewModel.swift | 96 +++++++++---- 32 files changed, 206 insertions(+), 114 deletions(-) diff --git a/xdrip/Managers/DexcomShare/DexcomShareUploadManager.swift b/xdrip/Managers/DexcomShare/DexcomShareUploadManager.swift index 84df3945..8f066ade 100644 --- a/xdrip/Managers/DexcomShare/DexcomShareUploadManager.swift +++ b/xdrip/Managers/DexcomShare/DexcomShareUploadManager.swift @@ -666,7 +666,7 @@ class DexcomShareUploadManager:NSObject { /// calls messageHandler with title and errorMessage. Title text will depend on success. private func callMessageHandler(withCredentialVerificationResult success:Bool, errorMessage:String?) { // define the title text - var title = Texts_DexcomShareTestResult.verificationSuccessFulAlertTitle + var title = Texts_DexcomShareTestResult.verificationSuccessfulAlertTitle if !success { title = Texts_DexcomShareTestResult.verificationErrorAlertTitle } @@ -675,7 +675,7 @@ class DexcomShareUploadManager:NSObject { // define the message text - var message = Texts_DexcomShareTestResult.verificationSuccessFulAlertBody + var message = Texts_DexcomShareTestResult.verificationSuccessfulAlertBody if !success { if let errorMessage = errorMessage { message = errorMessage diff --git a/xdrip/Managers/NightScout/NightScoutUploadManager.swift b/xdrip/Managers/NightScout/NightScoutUploadManager.swift index 0f557abc..863f9aed 100644 --- a/xdrip/Managers/NightScout/NightScoutUploadManager.swift +++ b/xdrip/Managers/NightScout/NightScoutUploadManager.swift @@ -70,6 +70,7 @@ public class NightScoutUploadManager:NSObject { UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutEnabled.rawValue, options: .new, context: nil) UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutUseSchedule.rawValue, options: .new, context: nil) UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutSchedule.rawValue, options: .new, context: nil) + UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightscoutToken.rawValue, options: .new, context: nil) } // MARK: - public functions @@ -79,14 +80,32 @@ public class NightScoutUploadManager:NSObject { /// - lastConnectionStatusChangeTimeStamp : when was the last transmitter dis/reconnect public func upload(lastConnectionStatusChangeTimeStamp: Date?) { - // check if NightScout is enabled + var apiKey: String = "" + var token: String = "" + + // check that NightScout is enabled guard UserDefaults.standard.nightScoutEnabled else {return} - // check if master is enabled + // check that master is enabled guard UserDefaults.standard.isMaster else {return} - // check if siteUrl and apiKey exist - guard let siteURL = UserDefaults.standard.nightScoutUrl, let apiKey = UserDefaults.standard.nightScoutAPIKey else {return} + // check that either the API_SECRET or Token exists + if UserDefaults.standard.nightScoutAPIKey == nil && UserDefaults.standard.nightscoutToken == nil { + return + } + + // check that siteUrl exists and if so, assign it + guard let siteURL = UserDefaults.standard.nightScoutUrl else {return} + + // assign apiKey if it isn't nil + if let string = UserDefaults.standard.nightScoutAPIKey { + apiKey = string + } + + // assign token if it isn't nil + if let string = UserDefaults.standard.nightscoutToken { + token = string + } // if schedule is on, check if upload is needed according to schedule if UserDefaults.standard.nightScoutUseSchedule { @@ -98,9 +117,10 @@ public class NightScoutUploadManager:NSObject { } // upload readings - uploadBgReadingsToNightScout(siteURL: siteURL, apiKey: apiKey, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp) + uploadBgReadingsToNightScout(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp) + // upload calibrations - uploadCalibrationsToNightScout(siteURL: siteURL, apiKey: apiKey) + uploadCalibrationsToNightScout() // upload activeSensor if needed if UserDefaults.standard.uploadSensorStartTimeToNS, let activeSensor = sensorsAccessor.fetchActiveSensor() { @@ -109,7 +129,7 @@ public class NightScoutUploadManager:NSObject { trace("in upload, activeSensor not yet uploaded to NS", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) - uploadActiveSensorToNightScout(siteURL: siteURL, apiKey: apiKey, sensor: activeSensor) + uploadActiveSensorToNightScout(sensor: activeSensor) } } @@ -120,7 +140,7 @@ public class NightScoutUploadManager:NSObject { if let transmitterBatteryInfo = UserDefaults.standard.transmitterBatteryInfo { - uploadTransmitterBatteryInfoToNightScout(siteURL: siteURL, apiKey: apiKey, transmitterBatteryInfo: transmitterBatteryInfo) + uploadTransmitterBatteryInfoToNightScout(transmitterBatteryInfo: transmitterBatteryInfo) } @@ -137,15 +157,15 @@ public class NightScoutUploadManager:NSObject { if let keyPathEnum = UserDefaults.Key(rawValue: keyPath) { switch keyPathEnum { - case UserDefaults.Key.nightScoutUrl, UserDefaults.Key.nightScoutAPIKey, UserDefaults.Key.nightScoutPort : + case UserDefaults.Key.nightScoutUrl, UserDefaults.Key.nightScoutAPIKey, UserDefaults.Key.nightscoutToken, UserDefaults.Key.nightScoutPort : // apikey or nightscout api key change is triggered by user, should not be done within 200 ms if (keyValueObserverTimeKeeper.verifyKey(forKey: keyPathEnum.rawValue, withMinimumDelayMilliSeconds: 200)) { - // if apiKey and siteURL are set and if master then test credentials - if let apiKey = UserDefaults.standard.nightScoutAPIKey, let siteUrl = UserDefaults.standard.nightScoutUrl, UserDefaults.standard.isMaster { + // if master is set, siteURL exists and either API_SECRET or a token is entered, then test credentials + if UserDefaults.standard.nightScoutUrl != "" && UserDefaults.standard.isMaster && (UserDefaults.standard.nightScoutAPIKey != "" || UserDefaults.standard.nightscoutToken != "") { - testNightScoutCredentials(apiKey: apiKey, siteURL: siteUrl, { (success, error) in + testNightScoutCredentials({ (success, error) in DispatchQueue.main.async { self.callMessageHandler(withCredentialVerificationResult: success, error: error) if success { @@ -166,27 +186,24 @@ public class NightScoutUploadManager:NSObject { // if changing to enabled, then do a credentials test and if ok start upload, in case of failure don't give warning, that's the only difference with previous cases if (keyValueObserverTimeKeeper.verifyKey(forKey: keyPathEnum.rawValue, withMinimumDelayMilliSeconds: 200)) { - if UserDefaults.standard.nightScoutEnabled { + // if master is set, siteURL exists and either API_SECRET or a token is entered, then test credentials + if UserDefaults.standard.nightScoutUrl != "" && UserDefaults.standard.isMaster && (UserDefaults.standard.nightScoutAPIKey != "" || UserDefaults.standard.nightscoutToken != "") { - // if apiKey and siteURL are set and if master then test credentials - if let apiKey = UserDefaults.standard.nightScoutAPIKey, let siteUrl = UserDefaults.standard.nightScoutUrl, UserDefaults.standard.isMaster { - - testNightScoutCredentials(apiKey: apiKey, siteURL: siteUrl, { (success, error) in - DispatchQueue.main.async { - if success { - - // set lastConnectionStatusChangeTimeStamp to as late as possible, to make sure that the most recent reading is uploaded if user is testing the credentials - self.upload(lastConnectionStatusChangeTimeStamp: Date()) - - } else { - trace("in observeValue, NightScout credential check failed", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) - } + testNightScoutCredentials({ (success, error) in + DispatchQueue.main.async { + if success { + + // set lastConnectionStatusChangeTimeStamp to as late as possible, to make sure that the most recent reading is uploaded if user is testing the credentials + self.upload(lastConnectionStatusChangeTimeStamp: Date()) + + } else { + trace("in observeValue, NightScout credential check failed", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) } - }) - } + } + }) } } - + default: break } @@ -201,7 +218,7 @@ public class NightScoutUploadManager:NSObject { /// - siteURL : nightscout site url /// - apiKey : nightscout api key /// - transmitterBatteryInfosensor: setransmitterBatteryInfosensornsor to upload - private func uploadTransmitterBatteryInfoToNightScout(siteURL:String, apiKey:String, transmitterBatteryInfo: TransmitterBatteryInfo) { + private func uploadTransmitterBatteryInfoToNightScout(transmitterBatteryInfo: TransmitterBatteryInfo) { trace("in uploadTransmitterBatteryInfoToNightScout, transmitterBatteryInfo not yet uploaded to NS", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -233,7 +250,7 @@ public class NightScoutUploadManager:NSObject { } - uploadData(dataToUpload: dataToUpload, traceString: "uploadTransmitterBatteryInfoToNightScout", siteURL: siteURL, path: nightScoutDeviceStatusPath, apiKey: apiKey, completionHandler: { + uploadData(dataToUpload: dataToUpload, traceString: "uploadTransmitterBatteryInfoToNightScout", path: nightScoutDeviceStatusPath, completionHandler: { // sensor successfully uploaded, change value in coredata trace("in uploadTransmitterBatteryInfoToNightScout, transmitterBatteryInfo uploaded to NS", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -251,7 +268,7 @@ public class NightScoutUploadManager:NSObject { /// - siteURL : nightscout site url /// - apiKey : nightscout api key /// - sensor: sensor to upload - private func uploadActiveSensorToNightScout(siteURL:String, apiKey:String, sensor: Sensor) { + private func uploadActiveSensorToNightScout(sensor: Sensor) { trace("in uploadActiveSensorToNightScout, activeSensor not yet uploaded to NS", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -259,10 +276,10 @@ public class NightScoutUploadManager:NSObject { "_id": sensor.id, "eventType": "Sensor Start", "created_at": sensor.startDate.ISOStringFromDate(), - "enteredBy": "xDrip iOS" + "enteredBy": ConstantsHomeView.applicationName ] - uploadData(dataToUpload: dataToUpload, traceString: "uploadActiveSensorToNightScout", siteURL: siteURL, path: nightScoutTreatmentPath, apiKey: apiKey, completionHandler: { + uploadData(dataToUpload: dataToUpload, traceString: "uploadActiveSensorToNightScout", path: nightScoutTreatmentPath, completionHandler: { // sensor successfully uploaded, change value in coredata trace("in uploadActiveSensorToNightScout, activeSensor uploaded to NS", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -282,7 +299,7 @@ public class NightScoutUploadManager:NSObject { /// - parameters: /// - siteURL : nightscout site url /// - apiKey : nightscout api key - private func uploadBgReadingsToNightScout(siteURL:String, apiKey:String, lastConnectionStatusChangeTimeStamp: Date?) { + private func uploadBgReadingsToNightScout(lastConnectionStatusChangeTimeStamp: Date?) { trace("in uploadBgReadingsToNightScout", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -319,7 +336,7 @@ public class NightScoutUploadManager:NSObject { // store the timestamp of the last reading to upload, here in the main thread, because we use a bgReading for it, which is retrieved in the main mangedObjectContext let timeStampLastReadingToUpload = bgReadingsToUpload.first != nil ? bgReadingsToUpload.first!.timeStamp : nil - uploadData(dataToUpload: bgReadingsDictionaryRepresentation, traceString: "uploadBgReadingsToNightScout", siteURL: siteURL, path: nightScoutEntriesPath, apiKey: apiKey, completionHandler: { + uploadData(dataToUpload: bgReadingsDictionaryRepresentation, traceString: "uploadBgReadingsToNightScout", path: nightScoutEntriesPath, completionHandler: { // change timeStampLatestNightScoutUploadedBgReading if let timeStampLastReadingToUpload = timeStampLastReadingToUpload { @@ -335,7 +352,7 @@ public class NightScoutUploadManager:NSObject { // do this in the main thread because the readings are fetched with the main mainManagedObjectContext DispatchQueue.main.async { - self.uploadBgReadingsToNightScout(siteURL: siteURL, apiKey: apiKey, lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp) + self.uploadBgReadingsToNightScout(lastConnectionStatusChangeTimeStamp: lastConnectionStatusChangeTimeStamp) } @@ -355,7 +372,7 @@ public class NightScoutUploadManager:NSObject { /// - parameters: /// - siteURL : nightscout site url /// - apiKey : nightscout api key - private func uploadCalibrationsToNightScout(siteURL:String, apiKey:String) { + private func uploadCalibrationsToNightScout() { trace("in uploadCalibrationsToNightScout", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info) @@ -382,7 +399,7 @@ public class NightScoutUploadManager:NSObject { // store the timestamp of the last calibration to upload, here in the main thread, because we use a Calibration for it, which is retrieved in the main mangedObjectContext let timeStampLastCalibrationToUpload = calibrationsToUpload.first != nil ? calibrationsToUpload.first!.timeStamp : nil - uploadData(dataToUpload: calibrationsDictionaryRepresentation, traceString: "uploadCalibrationsToNightScout", siteURL: siteURL, path: nightScoutEntriesPath, apiKey: apiKey, completionHandler: { + uploadData(dataToUpload: calibrationsDictionaryRepresentation, traceString: "uploadCalibrationsToNightScout", path: nightScoutEntriesPath, completionHandler: { // change timeStampLatestNightScoutUploadedCalibration if let timeStampLastCalibrationToUpload = timeStampLastCalibrationToUpload { @@ -408,7 +425,7 @@ public class NightScoutUploadManager:NSObject { /// - completionHandler : will be executed if upload was successful /// - siteURL : nightscout site url /// - apiKey : nightscout api key - private func uploadData(dataToUpload: Any, traceString: String, siteURL: String, path:String, apiKey: String, completionHandler: (() -> ())?) { + private func uploadData(dataToUpload: Any, traceString: String, path: String, completionHandler: (() -> ())?) { trace("in uploadData, %{public}@", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info, traceString) @@ -419,12 +436,18 @@ public class NightScoutUploadManager:NSObject { trace(" size of data to upload : %{public}@", log: self.oslog, category: ConstantsLog.categoryNightScoutUploadManager, type: .info, dateToUploadAsJSON.count.description) - if let url = URL(string: siteURL), var uRLComponents = URLComponents(url: url.appendingPathComponent(path), resolvingAgainstBaseURL: false) { + if let url = URL(string: UserDefaults.standard.nightScoutUrl!), var uRLComponents = URLComponents(url: url.appendingPathComponent(path), resolvingAgainstBaseURL: false) { if UserDefaults.standard.nightScoutPort != 0 { uRLComponents.port = UserDefaults.standard.nightScoutPort } + // if token not nil, then add also the token + if let token = UserDefaults.standard.nightscoutToken { + let queryItems = [URLQueryItem(name: "token", value: token)] + uRLComponents.queryItems = queryItems + } + if let url = uRLComponents.url { // Create Request @@ -432,7 +455,10 @@ public class NightScoutUploadManager:NSObject { request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Accept") - request.setValue(apiKey.sha1(), forHTTPHeaderField: "api-secret") + + if let apiKey = UserDefaults.standard.nightScoutAPIKey { + request.setValue(apiKey.sha1(), forHTTPHeaderField:"api-secret") + } // Create upload Task let task = URLSession.shared.uploadTask(with: request, from: dateToUploadAsJSON, completionHandler: { (data, response, error) -> Void in @@ -535,9 +561,9 @@ public class NightScoutUploadManager:NSObject { } - private func testNightScoutCredentials(apiKey:String, siteURL:String, _ completion: @escaping (_ success: Bool, _ error: Error?) -> Void) { + private func testNightScoutCredentials(_ completion: @escaping (_ success: Bool, _ error: Error?) -> Void) { - if let url = URL(string: siteURL), var uRLComponents = URLComponents(url: url.appendingPathComponent(nightScoutAuthTestPath), resolvingAgainstBaseURL: false) { + if let url = URL(string: UserDefaults.standard.nightScoutUrl!), var uRLComponents = URLComponents(url: url.appendingPathComponent(nightScoutAuthTestPath), resolvingAgainstBaseURL: false) { if UserDefaults.standard.nightScoutPort != 0 { uRLComponents.port = UserDefaults.standard.nightScoutPort @@ -548,7 +574,17 @@ public class NightScoutUploadManager:NSObject { var request = URLRequest(url: url) request.setValue("application/json", forHTTPHeaderField:"Content-Type") request.setValue("application/json", forHTTPHeaderField:"Accept") - request.setValue(apiKey.sha1(), forHTTPHeaderField:"api-secret") + + // if the API_SECRET is present, then hash it and pass it via http header. If it's missing but there is a token, then send this as plain text to allow the authentication check. + if let apiKey = UserDefaults.standard.nightScoutAPIKey { + + request.setValue(apiKey.sha1(), forHTTPHeaderField:"api-secret") + + } else if let token = UserDefaults.standard.nightscoutToken { + + request.setValue(token, forHTTPHeaderField:"api-secret") + + } let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in @@ -579,13 +615,13 @@ public class NightScoutUploadManager:NSObject { private func callMessageHandler(withCredentialVerificationResult success:Bool, error:Error?) { // define the title text - var title = Texts_NightScoutTestResult.verificationSuccessFulAlertTitle + var title = Texts_NightScoutTestResult.verificationSuccessfulAlertTitle if !success { title = Texts_NightScoutTestResult.verificationErrorAlertTitle } // define the message text - var message = Texts_NightScoutTestResult.verificationSuccessFulAlertBody + var message = Texts_NightScoutTestResult.verificationSuccessfulAlertBody if !success { if let error = error { message = error.localizedDescription diff --git a/xdrip/Storyboards/ar.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/ar.lproj/NightScoutTestResult.strings index faa5ca3c..899bd42b 100644 --- a/xdrip/Storyboards/ar.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/ar.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "تم التحقق من منصة نايسكاوت بنجاح"; "nightscouttestresult_verificationerroralerttitle" = "خطأ في التحقق"; "warningAPIKeyOrURLIsnil" = "يجب تعيين عنوان URL الخاص بـمنصة نايتسكاوت و API_SECRET قبل أن تتمكن من عمل الاختبار"; -"nightScoutAPIKeyAndURLStarted" = "تم بدأ الاختبار"; +"nightScoutAPIKeyAndURLStartedBody" = "تم بدأ الاختبار"; diff --git a/xdrip/Storyboards/de.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/de.lproj/NightScoutTestResult.strings index 7c50bb86..b733f9dc 100644 --- a/xdrip/Storyboards/de.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/de.lproj/NightScoutTestResult.strings @@ -3,7 +3,7 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Ihre Nightscout Anmeldung war erfolgreich"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Verbindung testen"; +"nightScoutAPIKeyAndURLStartedBody" = "Verbindung testen"; /// POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the title "nightscouttestresult_verificationsuccessfulalerttitle" = "Überprüfung erfolgreich"; diff --git a/xdrip/Storyboards/de.lproj/SettingsViews.strings b/xdrip/Storyboards/de.lproj/SettingsViews.strings index 5d53f6e4..24334322 100644 --- a/xdrip/Storyboards/de.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/de.lproj/SettingsViews.strings @@ -129,7 +129,7 @@ "schedule" = "Upload Plan:"; /// nightscout settings, port to use -"nightScoutPort" = "Port (Optional):"; +"nightScoutPort" = "Port:"; /// When clicking the interval setting, a pop up asks for minimum number of minutes between two readings, this is the pop up message - this is used for setting the interval between two readings in BG notifications, Speak readings, Apple Watch "settingsviews_IntervalTitle" = "Intervall:"; diff --git a/xdrip/Storyboards/en.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/en.lproj/NightScoutTestResult.strings index 820cba52..544f6d4b 100644 --- a/xdrip/Storyboards/en.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/en.lproj/NightScoutTestResult.strings @@ -1,5 +1,6 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verification Successful"; "nightscouttestresult_verificationsuccessfulalertbody" = "Your Nightscout site was verified successfully"; "nightscouttestresult_verificationerroralerttitle" = "Verification Error"; -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; +"nightScoutAPIKeyAndURLStartedTitle" = "Verifying..."; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; diff --git a/xdrip/Storyboards/en.lproj/SettingsViews.strings b/xdrip/Storyboards/en.lproj/SettingsViews.strings index 63d63322..8dfc534c 100644 --- a/xdrip/Storyboards/en.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/en.lproj/SettingsViews.strings @@ -104,7 +104,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; "debugLevel" = "Include Debug Level"; "warningChangeFromMasterToFollower" = "Switching from master to follower will stop your current sensor. Do you want to continue?"; -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; "developerSettings" = "Developer Settings"; "smoothLibreValues" = "Smooth Libre Values?"; "nslog" = "NSLog"; diff --git a/xdrip/Storyboards/es.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/es.lproj/NightScoutTestResult.strings index af59a0f8..e92ae8cd 100644 --- a/xdrip/Storyboards/es.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/es.lproj/NightScoutTestResult.strings @@ -1,5 +1,6 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verificación Realizada con Exito"; "nightscouttestresult_verificationsuccessfulalertbody" = "Tu sitio Nightscout ha sido verificado con éxito"; "nightscouttestresult_verificationerroralerttitle" = "Error de Verificación"; -"warningAPIKeyOrURLIsnil" = "Tu URL Nightscout y API_SECRET deben estar configurados antes de realizar la prueba"; -"nightScoutAPIKeyAndURLStarted" = "Prueba Iniciada"; +"warningAPIKeyOrURLIsnil" = "Tu URL Nightscout (y opcionalmente tu API_SECRET o Token) deben estar configurados antes de realizar la prueba"; +"nightScoutAPIKeyAndURLStartedTitle" = "Verificando..."; +"nightScoutAPIKeyAndURLStartedBody" = "Prueba de Verificación de Nightscout iniciada\n\nEste mensaje debe desaparecer de forma automática en breve"; diff --git a/xdrip/Storyboards/es.lproj/SettingsViews.strings b/xdrip/Storyboards/es.lproj/SettingsViews.strings index 0c4549b7..506b858f 100644 --- a/xdrip/Storyboards/es.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/es.lproj/SettingsViews.strings @@ -95,7 +95,7 @@ "volumeTestiOSSound" = "Comprobar Volumen (volumen actual del iPhone)"; "debugLevel" = "Incluir Datos Nivel Debug"; "warningChangeFromMasterToFollower" = "Si cambias el modo de Master a Seguidor se parará la sesión del sensor actual. Quieres continuar?"; -"nightScoutPort" = "Puerto (opcional):"; +"nightScoutPort" = "Puerto:"; "volumeTestSoundPlayerExplanation" = "Se está reproduciendo una alamra con el mismo volumen que usar un Tipo de Alarma con la opción de 'Ignorar Mode Silencio?' habilitado\n\nCambiar el volume con los botones del iPhone y apretar OK para confirmar"; "settingsviews_nightScoutAPIKey" = "API_SECRET:"; "settingsviews_nightScoutUrl" = "URL:"; diff --git a/xdrip/Storyboards/fi.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/fi.lproj/NightScoutTestResult.strings index 7f3a4cad..b30bbcff 100644 --- a/xdrip/Storyboards/fi.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/fi.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Nightscout tilisi vahvistettu onnistuneesti"; "nightscouttestresult_verificationerroralerttitle" = "Vahvistaminen epäonnistui"; "warningAPIKeyOrURLIsnil" = "Nightscout URL ja API_SECRET pitää olla asetettu ennen testin suorittamista"; -"nightScoutAPIKeyAndURLStarted" = "Testi aloitettu"; +"nightScoutAPIKeyAndURLStartedBody" = "Testi aloitettu"; diff --git a/xdrip/Storyboards/fr.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/fr.lproj/NightScoutTestResult.strings index 0e599b08..016edd1a 100644 --- a/xdrip/Storyboards/fr.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/fr.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Votre site Nightscout est correctement accessible"; "nightscouttestresult_verificationerroralerttitle" = "Echec de vérification "; "warningAPIKeyOrURLIsnil" = "Votre URL Nightscout URL et API_SECRET doit être remplie avant de lancer le test"; -"nightScoutAPIKeyAndURLStarted" = "Test lancé"; +"nightScoutAPIKeyAndURLStartedBody" = "Test lancé"; diff --git a/xdrip/Storyboards/fr.lproj/SettingsViews.strings b/xdrip/Storyboards/fr.lproj/SettingsViews.strings index aa9c6295..f5826d07 100644 --- a/xdrip/Storyboards/fr.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/fr.lproj/SettingsViews.strings @@ -86,7 +86,7 @@ "failedToSendEmail" = "Echec d'envoi du Email"; /// nightscout settings, port to use -"nightScoutPort" = "Port (Optionnel):"; +"nightScoutPort" = "Port:"; /// home screen settings, urgent high value "settingsviews_urgentHighValue" = "Urgent Valeur Haute"; diff --git a/xdrip/Storyboards/it.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/it.lproj/NightScoutTestResult.strings index bc97047e..c617cdb5 100644 --- a/xdrip/Storyboards/it.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/it.lproj/NightScoutTestResult.strings @@ -7,13 +7,13 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verification Successful"; /// in settings screen, user tries to test url and API Key but one of them is not set -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; /// POP up after verifying nightscout credentials, to say that verification of url and api key was not successful - this is the title "nightscouttestresult_verificationerroralerttitle" = "Verification Error"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; /// POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the body "nightscouttestresult_verificationsuccessfulalertbody" = "Your Nightscout site was verified successfully"; diff --git a/xdrip/Storyboards/it.lproj/SettingsViews.strings b/xdrip/Storyboards/it.lproj/SettingsViews.strings index 08965af1..05ae1f0c 100644 --- a/xdrip/Storyboards/it.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/it.lproj/SettingsViews.strings @@ -10,7 +10,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; /// nightscout settings, port to use -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; /// When clicking the interval setting, a pop up asks for minimum number of minutes between two readings, this is the pop up title - this is used for setting the interval between two readings in BG notifications, Speak readings, Apple Watch "settingsviews_IntervalTitle" = "Interval:"; diff --git a/xdrip/Storyboards/nl.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/nl.lproj/NightScoutTestResult.strings index d6410bef..d3748db3 100644 --- a/xdrip/Storyboards/nl.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/nl.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Je Nightscout site werd succesvol geverifieerd"; "nightscouttestresult_verificationerroralerttitle" = "Verificatie Fout"; "warningAPIKeyOrURLIsnil" = "Je Nightscout URL en API_SECRET moeten worden ingesteld voordat de test kan worden uitgevoerd"; -"nightScoutAPIKeyAndURLStarted" = "Test Gestart"; +"nightScoutAPIKeyAndURLStartedBody" = "Test Gestart"; diff --git a/xdrip/Storyboards/nl.lproj/SettingsViews.strings b/xdrip/Storyboards/nl.lproj/SettingsViews.strings index 945d09f9..7abe982a 100644 --- a/xdrip/Storyboards/nl.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/nl.lproj/SettingsViews.strings @@ -100,7 +100,7 @@ "volumeTestiOSSound" = "Test Volume (Huidig iPhone Volume)"; "debugLevel" = "Inclusief Debug Niveau"; "warningChangeFromMasterToFollower" = "Wisselen van Master naar Volger stopt de huidige sensor. Wil je doorgaan ?"; -"nightScoutPort" = "Port (Optioneel):"; +"nightScoutPort" = "Port:"; ///////////////////////////////////////////////////////////////////////////////////////////// ///// Translation needed - remove this header after translation ///// diff --git a/xdrip/Storyboards/pl-PL.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/pl-PL.lproj/NightScoutTestResult.strings index bc97047e..c617cdb5 100644 --- a/xdrip/Storyboards/pl-PL.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/pl-PL.lproj/NightScoutTestResult.strings @@ -7,13 +7,13 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verification Successful"; /// in settings screen, user tries to test url and API Key but one of them is not set -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; /// POP up after verifying nightscout credentials, to say that verification of url and api key was not successful - this is the title "nightscouttestresult_verificationerroralerttitle" = "Verification Error"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; /// POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the body "nightscouttestresult_verificationsuccessfulalertbody" = "Your Nightscout site was verified successfully"; diff --git a/xdrip/Storyboards/pl-PL.lproj/SettingsViews.strings b/xdrip/Storyboards/pl-PL.lproj/SettingsViews.strings index 08965af1..05ae1f0c 100644 --- a/xdrip/Storyboards/pl-PL.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/pl-PL.lproj/SettingsViews.strings @@ -10,7 +10,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; /// nightscout settings, port to use -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; /// When clicking the interval setting, a pop up asks for minimum number of minutes between two readings, this is the pop up title - this is used for setting the interval between two readings in BG notifications, Speak readings, Apple Watch "settingsviews_IntervalTitle" = "Interval:"; diff --git a/xdrip/Storyboards/pt.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/pt.lproj/NightScoutTestResult.strings index 6ad9139e..bf970609 100644 --- a/xdrip/Storyboards/pt.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/pt.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Site Nightscout verificado com sucesso"; "nightscouttestresult_verificationerroralerttitle" = "Erro na Verificação"; "warningAPIKeyOrURLIsnil" = "URL Nightscout e API_SECRET têm que ser definidos antes de correr o teste"; -"nightScoutAPIKeyAndURLStarted" = "Teste Iniciado"; +"nightScoutAPIKeyAndURLStartedBody" = "Teste Iniciado"; diff --git a/xdrip/Storyboards/pt.lproj/SettingsViews.strings b/xdrip/Storyboards/pt.lproj/SettingsViews.strings index 9fd1b776..a19f893c 100644 --- a/xdrip/Storyboards/pt.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/pt.lproj/SettingsViews.strings @@ -102,7 +102,7 @@ "volumeTestiOSSound" = "Testar Volume (Volume actual do iPhone)"; "debugLevel" = "Incluir Nível de Depuração"; "warningChangeFromMasterToFollower" = "Aterar de Mestre para Seguidor vai parar o actual sensor. Deseja continuar?"; -"nightScoutPort" = "Porto (opcional):"; +"nightScoutPort" = "Porto:"; "smoothLibreValues" = "Suavizar Valores do Libre?"; "oslog" = "OSLog"; "nslog" = "NSLog"; diff --git a/xdrip/Storyboards/ru.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/ru.lproj/NightScoutTestResult.strings index bc97047e..c617cdb5 100644 --- a/xdrip/Storyboards/ru.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/ru.lproj/NightScoutTestResult.strings @@ -7,13 +7,13 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verification Successful"; /// in settings screen, user tries to test url and API Key but one of them is not set -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; /// POP up after verifying nightscout credentials, to say that verification of url and api key was not successful - this is the title "nightscouttestresult_verificationerroralerttitle" = "Verification Error"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; /// POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the body "nightscouttestresult_verificationsuccessfulalertbody" = "Your Nightscout site was verified successfully"; diff --git a/xdrip/Storyboards/ru.lproj/SettingsViews.strings b/xdrip/Storyboards/ru.lproj/SettingsViews.strings index 08965af1..05ae1f0c 100644 --- a/xdrip/Storyboards/ru.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/ru.lproj/SettingsViews.strings @@ -10,7 +10,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; /// nightscout settings, port to use -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; /// When clicking the interval setting, a pop up asks for minimum number of minutes between two readings, this is the pop up title - this is used for setting the interval between two readings in BG notifications, Speak readings, Apple Watch "settingsviews_IntervalTitle" = "Interval:"; diff --git a/xdrip/Storyboards/sl.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/sl.lproj/NightScoutTestResult.strings index bc97047e..c617cdb5 100644 --- a/xdrip/Storyboards/sl.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/sl.lproj/NightScoutTestResult.strings @@ -7,13 +7,13 @@ "nightscouttestresult_verificationsuccessfulalerttitle" = "Verification Successful"; /// in settings screen, user tries to test url and API Key but one of them is not set -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; /// POP up after verifying nightscout credentials, to say that verification of url and api key was not successful - this is the title "nightscouttestresult_verificationerroralerttitle" = "Verification Error"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; /// POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the body "nightscouttestresult_verificationsuccessfulalertbody" = "Your Nightscout site was verified successfully"; diff --git a/xdrip/Storyboards/sl.lproj/SettingsViews.strings b/xdrip/Storyboards/sl.lproj/SettingsViews.strings index 3d9d7706..c900f835 100644 --- a/xdrip/Storyboards/sl.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/sl.lproj/SettingsViews.strings @@ -10,7 +10,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; /// nightscout settings, port to use -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; /// When clicking the interval setting, a pop up asks for minimum number of minutes between two readings, this is the pop up message - this is used for setting the interval between two readings in BG notifications, Speak readings, Apple Watch "settingsviews_IntervalTitle" = "Interval:"; diff --git a/xdrip/Storyboards/sv.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/sv.lproj/NightScoutTestResult.strings index 64932c7f..bb90d968 100644 --- a/xdrip/Storyboards/sv.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/sv.lproj/NightScoutTestResult.strings @@ -2,4 +2,4 @@ "nightscouttestresult_verificationsuccessfulalertbody" = "Din Nightscout-sida har verifierats framångsrikt"; "nightscouttestresult_verificationerroralerttitle" = "Verifikationsfel"; "warningAPIKeyOrURLIsnil" = "Din Nightscout URL och din API_SECRET måste ställas in innan du kan köra testet"; -"nightScoutAPIKeyAndURLStarted" = "Testet har startat"; +"nightScoutAPIKeyAndURLStartedBody" = "Testet har startat"; diff --git a/xdrip/Storyboards/sv.lproj/SettingsViews.strings b/xdrip/Storyboards/sv.lproj/SettingsViews.strings index ee1ca2e8..96f01065 100644 --- a/xdrip/Storyboards/sv.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/sv.lproj/SettingsViews.strings @@ -99,7 +99,7 @@ "volumeTestiOSSound" = "Testa volym (nuvarande iPhone-volym)"; "debugLevel" = "Inkludera felsökningsnivå"; "warningChangeFromMasterToFollower" = "Byte från användare till följare kommer att stoppa nuvarande sensor. Vill du fortsätta?"; -"nightScoutPort" = "Port (inte obligatorisk):"; +"nightScoutPort" = "Port:"; "developerSettings" = "Inställningar för utvecklare"; "smoothLibreValues" = "Utjämna Libre-värden?"; "nslog" = "NSLog"; diff --git a/xdrip/Storyboards/zh.lproj/NightScoutTestResult.strings b/xdrip/Storyboards/zh.lproj/NightScoutTestResult.strings index d1872493..7ccec3d8 100644 --- a/xdrip/Storyboards/zh.lproj/NightScoutTestResult.strings +++ b/xdrip/Storyboards/zh.lproj/NightScoutTestResult.strings @@ -7,7 +7,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////// /// in settings screen, user tries to test url and API Key but one of them is not set -"warningAPIKeyOrURLIsnil" = "Your Nightscout URL and API_SECRET must be set before you can run the test"; +"warningAPIKeyOrURLIsnil" = "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test"; /// in settings screen, user clicked test button for nightscout url and apikey -"nightScoutAPIKeyAndURLStarted" = "Test Started"; +"nightScoutAPIKeyAndURLStartedBody" = "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly"; diff --git a/xdrip/Storyboards/zh.lproj/SettingsViews.strings b/xdrip/Storyboards/zh.lproj/SettingsViews.strings index 1a513edb..b34f0f49 100644 --- a/xdrip/Storyboards/zh.lproj/SettingsViews.strings +++ b/xdrip/Storyboards/zh.lproj/SettingsViews.strings @@ -46,7 +46,7 @@ "volumeTestiOSSound" = "Test Volume (Current iPhone Volume)"; /// nightscout settings, port to use -"nightScoutPort" = "Port (optional):"; +"nightScoutPort" = "Port:"; /// home screen settings, urgent high value "settingsviews_urgentHighValue" = "Urgent High Value:"; diff --git a/xdrip/Texts/TextsDexcomShareTestResult.swift b/xdrip/Texts/TextsDexcomShareTestResult.swift index b9dd59d3..f40e382a 100644 --- a/xdrip/Texts/TextsDexcomShareTestResult.swift +++ b/xdrip/Texts/TextsDexcomShareTestResult.swift @@ -4,11 +4,11 @@ import Foundation class Texts_DexcomShareTestResult { static private let filename = "DexcomShareTestResult" - static let verificationSuccessFulAlertTitle: String = { + static let verificationSuccessfulAlertTitle: String = { return NSLocalizedString("dexcomsharetestresult_verificationsuccessfulalerttitle", tableName: filename, bundle: Bundle.main, value: "Verification Successful", comment: "POP up after verifying DexcomShare credentials, to say that verification of url and password key were successful - this is the title") }() - static let verificationSuccessFulAlertBody: String = { + static let verificationSuccessfulAlertBody: String = { return NSLocalizedString("dexcomsharetestresult_verificationsuccessfulalertbody", tableName: filename, bundle: Bundle.main, value: "Your Dexcom Share account was verified successfully", comment: "POP up after verifying DexcomShare credentials, to say that verification of url and password were successful - this is the body") }() diff --git a/xdrip/Texts/TextsNightScoutTestResult.swift b/xdrip/Texts/TextsNightScoutTestResult.swift index 135c8825..5b7f5e64 100644 --- a/xdrip/Texts/TextsNightScoutTestResult.swift +++ b/xdrip/Texts/TextsNightScoutTestResult.swift @@ -4,11 +4,11 @@ import Foundation class Texts_NightScoutTestResult { static private let filename = "NightScoutTestResult" - static let verificationSuccessFulAlertTitle: String = { + static let verificationSuccessfulAlertTitle: String = { return NSLocalizedString("nightscouttestresult_verificationsuccessfulalerttitle", tableName: filename, bundle: Bundle.main, value: "Verification Successful", comment: "POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the title") }() - static let verificationSuccessFulAlertBody: String = { + static let verificationSuccessfulAlertBody: String = { return NSLocalizedString("nightscouttestresult_verificationsuccessfulalertbody", tableName: filename, bundle: Bundle.main, value: "Your Nightscout site was verified successfully.", comment: "POP up after verifying nightscout credentials, to say that verification of url and api key were successful - this is the body") }() @@ -18,10 +18,14 @@ class Texts_NightScoutTestResult { static let warningAPIKeyOrURLIsnil: String = { - return NSLocalizedString("warningAPIKeyOrURLIsnil", tableName: filename, bundle: Bundle.main, value: "Your Nightscout URL and API_SECRET must be set before you can run the test", comment: "in settings screen, user tries to test url and API Key but one of them is not set") + return NSLocalizedString("warningAPIKeyOrURLIsnil", tableName: filename, bundle: Bundle.main, value: "Your Nightscout URL (and optionally API_SECRET or Token) must be set before you can run the test", comment: "in settings screen, user tries to test url and API Key but one of them is not set") }() - static let nightScoutAPIKeyAndURLStarted : String = { - return NSLocalizedString("nightScoutAPIKeyAndURLStarted", tableName: filename, bundle: Bundle.main, value: "Test Started", comment: "in settings screen, user clicked test button for nightscout url and apikey") + static let nightScoutAPIKeyAndURLStartedTitle : String = { + return NSLocalizedString("nightScoutAPIKeyAndURLStartedTitle", tableName: filename, bundle: Bundle.main, value: "Verifying...", comment: "in settings screen, user clicked test button for nightscout url and apikey - this is the title") + }() + + static let nightScoutAPIKeyAndURLStartedBody : String = { + return NSLocalizedString("nightScoutAPIKeyAndURLStartedBody", tableName: filename, bundle: Bundle.main, value: "Nightscout Verification Test Started\n\nThis message should automatically disappear shortly", comment: "in settings screen, user clicked test button for nightscout url and apikey - this is the body") }() } diff --git a/xdrip/Texts/TextsSettingsView.swift b/xdrip/Texts/TextsSettingsView.swift index ea8345ba..fe832c30 100644 --- a/xdrip/Texts/TextsSettingsView.swift +++ b/xdrip/Texts/TextsSettingsView.swift @@ -300,11 +300,11 @@ class Texts_SettingsView { }() static let nightScoutPort: String = { - return NSLocalizedString("nightScoutPort", tableName: filename, bundle: Bundle.main, value: "Port (optional):", comment: "nightscout settings, port to use") + return NSLocalizedString("nightScoutPort", tableName: filename, bundle: Bundle.main, value: "Port:", comment: "nightscout settings, port to use") }() static let nightscoutToken: String = { - return NSLocalizedString("nightscoutToken", tableName: filename, bundle: Bundle.main, value: "Token (optional):", comment: "nightscout settings, token to use") + return NSLocalizedString("nightscoutToken", tableName: filename, bundle: Bundle.main, value: "Token:", comment: "nightscout settings, token to use") }() // MARK: - Section Speak diff --git a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewNightScoutSettingsViewModel.swift b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewNightScoutSettingsViewModel.swift index 41daf8c1..7dd13b20 100644 --- a/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewNightScoutSettingsViewModel.swift +++ b/xdrip/View Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewNightScoutSettingsViewModel.swift @@ -13,11 +13,11 @@ fileprivate enum Setting:Int, CaseIterable { /// nightscout api key case nightScoutAPIKey = 2 - /// port - case port = 3 - /// nightscout api key - case token = 4 + case token = 3 + + /// port + case port = 4 /// to allow testing explicitly case testUrlAndAPIKey = 5 @@ -60,14 +60,22 @@ class SettingsViewNightScoutSettingsViewModel { guard let siteUrl = UserDefaults.standard.nightScoutUrl else {return} if let url = URL(string: siteUrl) { + let testURL = url.appendingPathComponent(nightScoutAuthTestPath) var request = URLRequest(url: testURL) request.setValue("application/json", forHTTPHeaderField:"Content-Type") request.setValue("application/json", forHTTPHeaderField:"Accept") + // if the API_SECRET is present, then hash it and pass it via http header. If it's missing but there is a token, then send this as plain text to allow the authentication check. if let apiKey = UserDefaults.standard.nightScoutAPIKey { + request.setValue(apiKey.sha1(), forHTTPHeaderField:"api-secret") + + } else if let token = UserDefaults.standard.nightscoutToken { + + request.setValue(token, forHTTPHeaderField:"api-secret") + } let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in @@ -78,11 +86,11 @@ class SettingsViewNightScoutSettingsViewModel { if error.localizedDescription.hasPrefix("A server with the specified hostname could not be found") { - print("in testNightScoutCredentials, error = Hostname/URL not found!") + print("in testNightScoutCredentials, error = URL/Hostname not found!") trace("in testNightScoutCredentials, error = %{public}@", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info, error.localizedDescription) - self.callMessageHandlerInMainThread(title: "Hostname/URL not found!", message: error.localizedDescription) + self.callMessageHandlerInMainThread(title: "URL/Hostname not found!", message: error.localizedDescription) return @@ -107,7 +115,9 @@ class SettingsViewNightScoutSettingsViewModel { trace("in testNightScoutCredentials, successful", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info) - self.callMessageHandlerInMainThread(title: Texts_NightScoutTestResult.verificationSuccessFulAlertTitle, message: Texts_NightScoutTestResult.verificationSuccessFulAlertBody) + self.callMessageHandlerInMainThread(title: Texts_NightScoutTestResult.verificationSuccessfulAlertTitle, message: Texts_NightScoutTestResult.verificationSuccessfulAlertBody) + + case (400): @@ -119,15 +129,21 @@ class SettingsViewNightScoutSettingsViewModel { if UserDefaults.standard.nightScoutAPIKey != nil { - trace("in testNightScoutCredentials, error = %{public}@", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info, errorMessage) + trace("in testNightScoutCredentials, API_SECRET is not valid, error = %{public}@", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info, errorMessage) - self.callMessageHandlerInMainThread(title: "API_SECRET incorrect", message: errorMessage) + self.callMessageHandlerInMainThread(title: "API_SECRET is not valid", message: errorMessage) + + } else if UserDefaults.standard.nightScoutAPIKey == nil && UserDefaults.standard.nightscoutToken != nil { + + trace("in testNightScoutCredentials, Token is not valid, error = %{public}@", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info, errorMessage) + + self.callMessageHandlerInMainThread(title: "Token is not valid", message: errorMessage) } else { - trace("in testNightScoutCredentials, URL responds OK but API_SECRET is missing", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info) + trace("in testNightScoutCredentials, URL responds OK but authentication method is missing and cannot be checked", log: self.log, category: ConstantsLog.categoryNightScoutSettingsViewModel, type: .info) - self.callMessageHandlerInMainThread(title: Texts_NightScoutTestResult.verificationSuccessFulAlertTitle, message: "URL is verified but API_SECRET is missing and cannot be authenticated!") + self.callMessageHandlerInMainThread(title: Texts_NightScoutTestResult.verificationSuccessfulAlertTitle, message: "URL responds OK but authentication method is missing and cannot be checked!") } @@ -243,7 +259,7 @@ extension SettingsViewNightScoutSettingsViewModel: SettingsViewModelProtocol { UserDefaults.standard.nightscoutToken = token.toNilIfLength0() } - // finally, let's make a clean with just the scheme and host. We don't need to add anything else as this is basically the only thing we were asking for in the first place. + // finally, let's make a clean URL with just the scheme and host. We don't need to add anything else as this is basically the only thing we were asking for in the first place. var nighscoutURLComponents = URLComponents() nighscoutURLComponents.scheme = "https" nighscoutURLComponents.host = enteredURLComponents.host?.lowercased() @@ -274,23 +290,15 @@ extension SettingsViewNightScoutSettingsViewModel: SettingsViewModelProtocol { case .testUrlAndAPIKey: - //if UserDefaults.standard.nightScoutAPIKey != nil && UserDefaults.standard.nightScoutUrl != nil { - // show info that test is started, through the messageHandler if let messageHandler = messageHandler { - messageHandler(Texts_HomeView.info, Texts_NightScoutTestResult.nightScoutAPIKeyAndURLStarted) + messageHandler(Texts_NightScoutTestResult.nightScoutAPIKeyAndURLStartedTitle, Texts_NightScoutTestResult.nightScoutAPIKeyAndURLStartedBody) } self.testNightScoutCredentials() return .nothing -// } else { -// -// return .showInfoText(title: Texts_Common.warning, message: Texts_NightScoutTestResult.warningAPIKeyOrURLIsnil) -// -// } - case .useSchedule: return .nothing @@ -388,11 +396,11 @@ extension SettingsViewNightScoutSettingsViewModel: SettingsViewModelProtocol { case .nightScoutUrl: return UserDefaults.standard.nightScoutUrl case .nightScoutAPIKey: - return UserDefaults.standard.nightScoutAPIKey != nil ? "***********" : nil + return UserDefaults.standard.nightScoutAPIKey != nil ? obscureString(stringToObscure: UserDefaults.standard.nightScoutAPIKey) : nil case .port: return UserDefaults.standard.nightScoutPort != 0 ? UserDefaults.standard.nightScoutPort.description : nil case .token: - return UserDefaults.standard.nightscoutToken != nil ? UserDefaults.standard.nightscoutToken?.description : nil + return UserDefaults.standard.nightscoutToken != nil ? obscureString(stringToObscure: UserDefaults.standard.nightscoutToken) : nil case .useSchedule: return nil case .schedule: @@ -439,6 +447,48 @@ extension SettingsViewNightScoutSettingsViewModel: SettingsViewModelProtocol { } } + /// use this to partially obscure the API-SECRET and Token values. We want the user to see that "something" is there that makes sense to them, but it won't reveal any private information if they screenshot it + func obscureString(stringToObscure: String?) -> String { + + // make sure that something useful has been passed to the function + guard var obscuredString = stringToObscure else { return "" } + + let stringLength: Int = obscuredString.count + + // in order to avoid strange layouts if somebody uses a really long API_SECRET, then let's limit the displayed string size to something more manageable + let maxStringSizeToShow: Int = 12 + + // the characters we will use to obscure the sensitive data + let maskingCharacter: String = "*" + + // based upon the length of the string, we will show more, or less, of the original characters at the beginning. This gives more context whilst maintaining privacy + var startCharsNotToObscure: Int = 0 + + switch stringLength { + case 0...3: + startCharsNotToObscure = 0 + case 4...5: + startCharsNotToObscure = 1 + case 6...7: + startCharsNotToObscure = 2 + case 8...10: + startCharsNotToObscure = 3 + case 11...50: + startCharsNotToObscure = 4 + default: + startCharsNotToObscure = 0 + } + + // remove the characters that we want to obscure + obscuredString.removeLast(stringLength - startCharsNotToObscure) + + // now "fill up" the string with the masking character up to the original string size. If it is longer than the maxStingSizeToShow then trim it down to make everything fit in a clean way + obscuredString += String(repeating: maskingCharacter, count: stringLength > maxStringSizeToShow ? maxStringSizeToShow - obscuredString.count : stringLength - obscuredString.count) + + return obscuredString + + } + } extension SettingsViewNightScoutSettingsViewModel: TimeSchedule {