secretive/SecretAgent/Notifier.swift

123 lines
5.4 KiB
Swift
Raw Normal View History

2020-03-04 07:14:38 +00:00
import Foundation
import UserNotifications
2020-03-22 00:52:51 +00:00
import AppKit
2020-03-22 01:43:26 +00:00
import SecretKit
import SecretAgentKit
import Brief
2020-03-04 07:14:38 +00:00
class Notifier {
2020-05-16 06:19:00 +00:00
private let notificationDelegate = NotificationDelegate()
2020-03-22 01:43:26 +00:00
init() {
let updateAction = UNNotificationAction(identifier: Constants.updateActionIdentitifier, title: "Update", options: [])
let ignoreAction = UNNotificationAction(identifier: Constants.ignoreActionIdentitifier, title: "Ignore", options: [])
let updateCategory = UNNotificationCategory(identifier: Constants.updateCategoryIdentitifier, actions: [updateAction, ignoreAction], intentIdentifiers: [], options: [])
2020-09-22 06:12:50 +00:00
let criticalUpdateCategory = UNNotificationCategory(identifier: Constants.criticalUpdateCategoryIdentitifier, actions: [updateAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([updateCategory, criticalUpdateCategory])
2020-03-22 01:43:26 +00:00
UNUserNotificationCenter.current().delegate = notificationDelegate
}
2020-03-04 07:14:38 +00:00
func prompt() {
let notificationCenter = UNUserNotificationCenter.current()
2020-09-22 06:12:50 +00:00
notificationCenter.requestAuthorization(options: .alert) { _, _ in }
2020-03-04 07:14:38 +00:00
}
2020-03-17 07:56:55 +00:00
func notify(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) {
2020-03-04 07:14:38 +00:00
let notificationCenter = UNUserNotificationCenter.current()
let notificationContent = UNMutableNotificationContent()
2020-03-22 00:52:51 +00:00
notificationContent.title = "Signed Request from \(provenance.origin.name)"
2020-03-22 00:55:31 +00:00
notificationContent.subtitle = "Using secret \"\(secret.name)\""
2020-03-22 00:52:51 +00:00
if let iconURL = iconURL(for: provenance), let attachment = try? UNNotificationAttachment(identifier: "icon", url: iconURL, options: nil) {
notificationContent.attachments = [attachment]
}
2020-03-04 07:14:38 +00:00
let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: nil)
notificationCenter.add(request, withCompletionHandler: nil)
}
func notify(update: Release, ignore: ((Release) -> Void)?) {
2020-03-22 01:43:26 +00:00
notificationDelegate.release = update
notificationDelegate.ignore = ignore
2020-03-22 01:43:26 +00:00
let notificationCenter = UNUserNotificationCenter.current()
let notificationContent = UNMutableNotificationContent()
if update.critical {
notificationContent.title = "Critical Security Update - \(update.name)"
} else {
notificationContent.title = "Update Available - \(update.name)"
}
notificationContent.subtitle = "Click to Update"
notificationContent.body = update.body
notificationContent.categoryIdentifier = update.critical ? Constants.criticalUpdateCategoryIdentitifier : Constants.updateCategoryIdentitifier
2020-03-22 01:43:26 +00:00
let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: nil)
notificationCenter.add(request, withCompletionHandler: nil)
}
2020-03-04 07:14:38 +00:00
}
2020-03-22 00:52:51 +00:00
extension Notifier {
func iconURL(for provenance: SigningRequestProvenance) -> URL? {
do {
if let app = NSRunningApplication(processIdentifier: provenance.origin.pid), let icon = app.icon?.tiffRepresentation {
let temporaryURL = URL(fileURLWithPath: (NSTemporaryDirectory() as NSString).appendingPathComponent("\(UUID().uuidString).png"))
let bitmap = NSBitmapImageRep(data: icon)
try bitmap?.representation(using: .png, properties: [:])?.write(to: temporaryURL)
return temporaryURL
}
} catch {
}
return nil
}
}
extension Notifier: SigningWitness {
2020-03-19 03:04:24 +00:00
func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws {
}
2020-03-17 07:56:55 +00:00
func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws {
notify(accessTo: secret, by: provenance)
}
}
2020-03-22 01:43:26 +00:00
extension Notifier {
enum Constants {
static let updateCategoryIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update"
static let criticalUpdateCategoryIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.critical"
static let updateActionIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.updateaction"
static let ignoreActionIdentitifier = "com.maxgoedjen.Secretive.SecretAgent.update.ignoreaction"
2020-03-22 01:43:26 +00:00
}
}
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
fileprivate var release: Release?
fileprivate var ignore: ((Release) -> Void)?
2020-03-22 01:43:26 +00:00
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
guard let update = release else { return }
switch response.actionIdentifier {
case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier:
NSWorkspace.shared.open(update.html_url)
case Notifier.Constants.ignoreActionIdentitifier:
ignore?(update)
default:
fatalError()
}
2020-03-22 01:43:26 +00:00
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
2020-09-22 06:12:50 +00:00
completionHandler([.list, .banner])
2020-03-22 01:43:26 +00:00
}
}