Skip to content

Commit

Permalink
Enable keychain sharing between notification service and app (#789)
Browse files Browse the repository at this point in the history
and clean up auth handling a bit

Signed-off-by: Dan Cunningham <[email protected]>
  • Loading branch information
digitaldan authored Jul 11, 2024
1 parent 35b924a commit ce79b29
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 29 deletions.
4 changes: 4 additions & 0 deletions NotificationService/NotificationService.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
<array>
<string>group.org.openhab.app</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.openhab.app</string>
</array>
</dict>
</plist>
4 changes: 2 additions & 2 deletions NotificationService/NotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class NotificationService: UNNotificationServiceExtension {
}

private func downloadAndAttachMedia(url: String, completion: @escaping (UNNotificationAttachment?) -> Void) {
let client = HTTPClient(username: Preferences.username, password: Preferences.username) // lets not always send auth with this
let client = HTTPClient(username: Preferences.username, password: Preferences.username, alwaysSendBasicAuth: Preferences.alwaysSendCreds)

let downloadCompletionHandler: @Sendable (URL?, URLResponse?, Error?) -> Void = { (localURL, response, error) in
guard let localURL else {
Expand All @@ -161,7 +161,7 @@ class NotificationService: UNNotificationServiceExtension {

let itemName = String(itemURI.absoluteString.dropFirst(scheme.count + 1))

let client = HTTPClient(username: Preferences.username, password: Preferences.username, alwaysSendBasicAuth: Preferences.alwaysSendCreds)
let client = HTTPClient(username: Preferences.username, password: Preferences.password, alwaysSendBasicAuth: Preferences.alwaysSendCreds)
client.getItem(baseURLs: [Preferences.localUrl, Preferences.remoteUrl], itemName: itemName) { item, error in
guard let item else {
os_log("Could not find item %{PUBLIC}@", log: .default, type: .info, itemName)
Expand Down
80 changes: 60 additions & 20 deletions OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@
import Foundation
import os.log

public class HTTPClient: NSObject, URLSessionDelegate {
public class HTTPClient: NSObject, URLSessionDelegate, URLSessionTaskDelegate {
// MARK: - Properties

private var session: URLSession!
private let username: String
private let password: String
private let certManager: ClientCertificateManager
private let alwaysSendBasicAuth: Bool

public init(username: String, password: String, alwaysSendBasicAuth: Bool = false) {
self.username = username
self.password = password
certManager = ClientCertificateManager()
self.alwaysSendBasicAuth = alwaysSendBasicAuth
super.init()

Expand Down Expand Up @@ -283,28 +281,70 @@ public class HTTPClient: NSObject, URLSessionDelegate {
task.resume()
}

// MARK: - URLSessionDelegate for Client Certificates

// MARK: - URLSessionDelegate for Client Certificates and Basic Auth
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
let serverDistinguishedNames = challenge.protectionSpace.distinguishedNames
let identity = certManager.evaluateTrust(distinguishedNames: serverDistinguishedNames ?? [])
urlSessionInternal(session, task: nil, didReceive: challenge, completionHandler: completionHandler)
}

if let identity {
let credential = URLCredential(identity: identity, certificates: nil, persistence: .forSession)
completionHandler(.useCredential, credential)
public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
urlSessionInternal(session, task: task, didReceive: challenge, completionHandler: completionHandler)
}

private func urlSessionInternal(_ session: URLSession, task: URLSessionTask?, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
os_log("URLAuthenticationChallenge: %{public}@", log: .networking, type: .info, challenge.protectionSpace.authenticationMethod)
let authenticationMethod = challenge.protectionSpace.authenticationMethod
switch authenticationMethod {
case NSURLAuthenticationMethodServerTrust:
handleServerTrust(challenge: challenge, completionHandler: completionHandler)
case NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic:
if let task {
task.authAttemptCount += 1
if task.authAttemptCount > 1 {
completionHandler(.cancelAuthenticationChallenge, nil)
} else {
handleBasicAuth(challenge: challenge, completionHandler: completionHandler)
}
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
handleBasicAuth(challenge: challenge, completionHandler: completionHandler)
}
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let serverTrust = challenge.protectionSpace.serverTrust!
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
let credential = URLCredential(user: username, password: password, persistence: .forSession)
completionHandler(.useCredential, credential)
} else {
case NSURLAuthenticationMethodClientCertificate:
handleClientCertificateAuth(challenge: challenge, completionHandler: completionHandler)
default:
completionHandler(.performDefaultHandling, nil)
}
}

private func handleServerTrust(challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.performDefaultHandling, nil)
return
}
let credential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, credential)
}

private func handleBasicAuth(challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let credential = URLCredential(user: username, password: password, persistence: .forSession)
completionHandler(.useCredential, credential)
}

private func handleClientCertificateAuth(challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let certificateManager = ClientCertificateManager()
let (disposition, credential) = certificateManager.evaluateTrust(with: challenge)
completionHandler(disposition, credential)
}
}

extension URLSessionTask {
private static var authAttemptCountKey: UInt8 = 0

var authAttemptCount: Int {
get {
objc_getAssociatedObject(self, &URLSessionTask.authAttemptCountKey) as? Int ?? 0
}
set {
objc_setAssociatedObject(self, &URLSessionTask.authAttemptCountKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
9 changes: 2 additions & 7 deletions openHAB/OpenHABWebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -404,13 +404,8 @@ extension OpenHABWebViewController: WKUIDelegate {
extension OpenHABWebViewController: OpenHABTrackerDelegate {
func openHABTracked(_ openHABUrl: URL?, version: Int) {
os_log("OpenHABWebViewController openHAB URL = %{PUBLIC}@", log: .remoteAccess, type: .error, "\(openHABUrl!)")
if version >= 2 {
openHABTrackedRootUrl = openHABUrl?.absoluteString ?? ""
loadWebView(force: false)
} else {
showPopupMessage(seconds: 2, title: NSLocalizedString("select_sitemap", comment: ""), message: "", theme: .info)
showSideMenu()
}
openHABTrackedRootUrl = openHABUrl?.absoluteString ?? ""
loadWebView(force: false)
}

func openHABTrackingProgress(_ message: String?) {
Expand Down
4 changes: 4 additions & 0 deletions openHAB/openHAB.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)org.openhab.app</string>
</array>
</dict>
</plist>

0 comments on commit ce79b29

Please sign in to comment.