Nightscout token authentication

This commit is contained in:
Paul Plant 2021-12-19 12:11:47 +01:00
parent fe29ddc951
commit 4c52fe22a8
32 changed files with 206 additions and 114 deletions

View File

@ -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

View File

@ -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

View File

@ -2,4 +2,4 @@
"nightscouttestresult_verificationsuccessfulalertbody" = "تم التحقق من منصة نايسكاوت بنجاح";
"nightscouttestresult_verificationerroralerttitle" = "خطأ في التحقق";
"warningAPIKeyOrURLIsnil" = "يجب تعيين عنوان URL الخاص بـمنصة نايتسكاوت و API_SECRET قبل أن تتمكن من عمل الاختبار";
"nightScoutAPIKeyAndURLStarted" = "تم بدأ الاختبار";
"nightScoutAPIKeyAndURLStartedBody" = "تم بدأ الاختبار";

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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é";

View File

@ -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";

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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 /////

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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:";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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:";

View File

@ -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")
}()

View File

@ -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")
}()
}

View File

@ -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

View File

@ -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 {