ShareService.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. //
  2. // ShareService.swift
  3. // Loop
  4. //
  5. // Created by Nate Racklyeft on 7/2/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import Foundation
  9. import LoopKit
  10. // Encapsulates the Dexcom Share client service and its authentication
  11. public class ShareService: ServiceAuthentication {
  12. public var credentialValues: [String?]
  13. public let title: String = LocalizedString("Dexcom Share", comment: "The title of the Dexcom Share service")
  14. public init(username: String?, password: String?, url: URL?) {
  15. credentialValues = [
  16. username,
  17. password,
  18. url?.absoluteString
  19. ]
  20. /*
  21. To enable Loop to use a custom share server, change the value of customServer
  22. and remove the comment markers on line 55 and 62.
  23. You can find installation instructions for one such custom share server at
  24. https://github.com/dabear/NightscoutShareServer
  25. */
  26. /*
  27. let customServer = "https://REPLACEME"
  28. let customServerTitle = "Custom"
  29. credentials[2].options?.append(
  30. (title: LocalizedString(customServerTitle, comment: "Custom share server option title"),
  31. value: customServer))
  32. */
  33. if let username = username, let password = password, let url = url {
  34. isAuthorized = true
  35. client = ShareClient(username: username, password: password, shareServer: url.absoluteString)
  36. }
  37. }
  38. // The share client, if credentials are present
  39. private(set) var client: ShareClient?
  40. public var username: String? {
  41. return credentialValues[0]
  42. }
  43. var password: String? {
  44. return credentialValues[1]
  45. }
  46. var url: URL? {
  47. guard let urlString = credentialValues[2] else {
  48. return nil
  49. }
  50. return URL(string: urlString)
  51. }
  52. public var isAuthorized: Bool = false
  53. public func verify(_ completion: @escaping (_ success: Bool, _ error: Error?) -> Void) {
  54. guard let username = username, let password = password, let url = url else {
  55. completion(false, nil)
  56. return
  57. }
  58. let client = ShareClient(username: username, password: password, shareServer: url.absoluteString)
  59. client.fetchLast(1) { (error, _) in
  60. completion(error == nil, error)
  61. }
  62. self.client = client
  63. }
  64. public func reset() {
  65. isAuthorized = false
  66. client = nil
  67. }
  68. }
  69. private let DexcomShareURL = URL(string: KnownShareServers.US.rawValue)!
  70. private let DexcomShareServiceLabel = "DexcomShare2"
  71. extension KeychainManager {
  72. func setDexcomShareUsername(_ username: String?, password: String?, url: URL?) throws {
  73. let credentials: InternetCredentials?
  74. if let username = username, let password = password, let url = url {
  75. credentials = InternetCredentials(username: username, password: password, url: url)
  76. } else {
  77. credentials = nil
  78. }
  79. // Replace the legacy URL-keyed credentials
  80. try replaceInternetCredentials(nil, forURL: DexcomShareURL)
  81. try replaceInternetCredentials(credentials, forLabel: DexcomShareServiceLabel)
  82. }
  83. func getDexcomShareCredentials() -> (username: String, password: String, url: URL)? {
  84. do { // Silence all errors and return nil
  85. do {
  86. let credentials = try getInternetCredentials(label: DexcomShareServiceLabel)
  87. return (username: credentials.username, password: credentials.password, url: credentials.url)
  88. } catch KeychainManagerError.copy {
  89. // Fetch and replace the legacy URL-keyed credentials
  90. let credentials = try getInternetCredentials(url: DexcomShareURL)
  91. try setDexcomShareUsername(credentials.username, password: credentials.password, url: credentials.url)
  92. return (username: credentials.username, password: credentials.password, url: credentials.url)
  93. }
  94. } catch {
  95. return nil
  96. }
  97. }
  98. }
  99. extension ShareService {
  100. public convenience init(keychainManager: KeychainManager = KeychainManager()) {
  101. if let (username, password, url) = keychainManager.getDexcomShareCredentials() {
  102. self.init(username: username, password: password, url: url)
  103. } else {
  104. self.init(username: nil, password: nil, url: nil)
  105. }
  106. }
  107. }