Skip to content

Commit

Permalink
Merge pull request ZekeSnider#40 from ZekeSnider/zeke.better-ui-permi…
Browse files Browse the repository at this point in the history
…ssioning

Improve UI permissioning
  • Loading branch information
ZekeSnider authored Aug 17, 2020
2 parents c6a604c + e6147a7 commit e17e5d5
Show file tree
Hide file tree
Showing 18 changed files with 733 additions and 130 deletions.
19 changes: 16 additions & 3 deletions Jared.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
B39F2B391CB3BCA700C0D35C /* SendText.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B39F2B381CB3BCA700C0D35C /* SendText.scpt */; };
B3A4C61423B867E300B7F009 /* MessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A4C61323B867E300B7F009 /* MessageRequest.swift */; };
B3AECB5F2207CE33003E32F8 /* JaredFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F84B61F1CC506790059A82B /* JaredFramework.framework */; };
B3B7E52424E9EE2000A3FBD1 /* AutomationPermissionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B7E52324E9EE2000A3FBD1 /* AutomationPermissionState.swift */; };
B3B7E52524EA00BC00A3FBD1 /* JaredConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FB28F324E9BCBD003BA6CB /* JaredConstants.swift */; };
B3B844B824D7791800DC162A /* ActionTypeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B844B724D7791700DC162A /* ActionTypeTest.swift */; };
B3B844BA24D77A4900DC162A /* SendStyleTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B844B924D77A4800DC162A /* SendStyleTest.swift */; };
B3B844BC24D77BF200DC162A /* SendStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B844BB24D77BF200DC162A /* SendStyle.swift */; };
Expand Down Expand Up @@ -86,6 +88,8 @@
B3EF1DAE2206E19100953DE7 /* Bodies.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EF1DAD2206E19100953DE7 /* Bodies.swift */; };
B3EF1DB02206E4EB00953DE7 /* MessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EF1DAF2206E4EB00953DE7 /* MessageTests.swift */; };
B3F185F724E51C880051C696 /* CoreModule.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B3F185F524E51C880051C696 /* CoreModule.xcdatamodeld */; };
B3FB28F224E9BAF7003BA6CB /* PermissionsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FB28F124E9BAF7003BA6CB /* PermissionsHelper.swift */; };
B3FB28F424E9BCBD003BA6CB /* JaredConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FB28F324E9BCBD003BA6CB /* JaredConstants.swift */; };
E4911DF4614B81D99C27CFF5 /* Pods_JaredTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 468EA41747ED2E0CB098C6BD /* Pods_JaredTests.framework */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -190,6 +194,7 @@
B39EC17D1CB01FEF002C3161 /* CoreModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreModule.swift; sourceTree = "<group>"; };
B39F2B381CB3BCA700C0D35C /* SendText.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = SendText.scpt; sourceTree = "<group>"; };
B3A4C61323B867E300B7F009 /* MessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequest.swift; sourceTree = "<group>"; };
B3B7E52324E9EE2000A3FBD1 /* AutomationPermissionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationPermissionState.swift; sourceTree = "<group>"; };
B3B844B724D7791700DC162A /* ActionTypeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionTypeTest.swift; sourceTree = "<group>"; };
B3B844B924D77A4800DC162A /* SendStyleTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendStyleTest.swift; sourceTree = "<group>"; };
B3B844BB24D77BF200DC162A /* SendStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendStyle.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -217,6 +222,8 @@
B3EF1DAD2206E19100953DE7 /* Bodies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bodies.swift; sourceTree = "<group>"; };
B3EF1DAF2206E4EB00953DE7 /* MessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTests.swift; sourceTree = "<group>"; };
B3F185F624E51C880051C696 /* CoreModule.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreModule.xcdatamodel; sourceTree = "<group>"; };
B3FB28F124E9BAF7003BA6CB /* PermissionsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsHelper.swift; sourceTree = "<group>"; };
B3FB28F324E9BCBD003BA6CB /* JaredConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JaredConstants.swift; sourceTree = "<group>"; };
B85E2F9206C873C167385410 /* Pods-JaredUI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JaredUI.debug.xcconfig"; path = "Pods/Target Support Files/Pods-JaredUI/Pods-JaredUI.debug.xcconfig"; sourceTree = "<group>"; };
C31CB7445EAF3431D6AA3FD4 /* Pods_JaredUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JaredUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -375,7 +382,6 @@
B39EC1741CB01F55002C3161 /* Jared */ = {
isa = PBXGroup;
children = (
B353946124E64DDE00F9424A /* PersistentContainer.swift */,
B30F765724C574FD00ADF57B /* JaredRelease.entitlements */,
B3774D4E2424AFEB00F02F62 /* JaredDebug.entitlements */,
7FF077EA1CBCFFCB008E33CB /* SendImage.scpt */,
Expand All @@ -384,11 +390,15 @@
B30F765424C5740800ADF57B /* SendTextUI.scpt */,
B37E82811FA85B210069E15D /* SendTextSingleBuddy.scpt */,
B361EEBA1F1DE24C0041113C /* config.json */,
B353946124E64DDE00F9424A /* PersistentContainer.swift */,
B3BF17C1244EBE9A00CC44C5 /* Router.swift */,
B39A65DF1CC1FB1B003E26B0 /* PluginManager.swift */,
B3327DC522066427009DD882 /* WebHookManager.swift */,
B3327DC32206607B009DD882 /* ContactHelper.swift */,
B3FB28F124E9BAF7003BA6CB /* PermissionsHelper.swift */,
B3FB28F324E9BCBD003BA6CB /* JaredConstants.swift */,
B3C59283224DD17900116ECB /* JaredWebServer.swift */,
B3B7E52324E9EE2000A3FBD1 /* AutomationPermissionState.swift */,
B3A4C61323B867E300B7F009 /* MessageRequest.swift */,
B35CE7142196AD4E002F52A7 /* DatabaseHandler.swift */,
7F0E931C1D02FC250096BABE /* Global.swift */,
Expand Down Expand Up @@ -567,10 +577,9 @@
};
buildConfigurationList = B39EC16D1CB01F55002C3161 /* Build configuration list for PBXProject "Jared" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
ja,
Expand Down Expand Up @@ -781,6 +790,7 @@
B3327DC42206607B009DD882 /* ContactHelper.swift in Sources */,
B332787C1CB2CB1300BBDD3A /* ViewController.swift in Sources */,
B3327DC622066427009DD882 /* WebHookManager.swift in Sources */,
B3FB28F224E9BAF7003BA6CB /* PermissionsHelper.swift in Sources */,
B3B844CB24DA5D1700DC162A /* DiskAccessDelegate.swift in Sources */,
B332787A1CB2CB1300BBDD3A /* AppDelegate.swift in Sources */,
B3BF17C4244EBF4900CC44C5 /* InternalModule.swift in Sources */,
Expand All @@ -789,7 +799,9 @@
7F0E931D1D02FC250096BABE /* Global.swift in Sources */,
B353946224E64DDE00F9424A /* PersistentContainer.swift in Sources */,
B3B844C424D9288000DC162A /* RouterDelegate.swift in Sources */,
B3B7E52424E9EE2000A3FBD1 /* AutomationPermissionState.swift in Sources */,
B3C59284224DD17900116ECB /* JaredWebServer.swift in Sources */,
B3FB28F424E9BCBD003BA6CB /* JaredConstants.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -807,6 +819,7 @@
B3B844C824D92C2200DC162A /* DatabaseHandler.swift in Sources */,
B3B844C024D8F91900DC162A /* DatabaseHandlerTest.swift in Sources */,
B32DE51824D5231800AB3A71 /* PluginManagerDelegate.swift in Sources */,
B3B7E52524EA00BC00A3FBD1 /* JaredConstants.swift in Sources */,
B32DE51624D522DA00AB3A71 /* Router.swift in Sources */,
B3BF17CA244FDA3F00CC44C5 /* MessageDelegate.swift in Sources */,
B3B844C524D9288000DC162A /* RouterDelegate.swift in Sources */,
Expand Down
17 changes: 17 additions & 0 deletions Jared/AutomationPermissionState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// AutomationPermissionState.swift
// Jared
//
// Created by Zeke Snider on 8/16/20.
// Copyright © 2020 Zeke Snider. All rights reserved.
//

import Foundation

enum AutomationPermissionState: Int {
case declined
case authorized
case notDetermined
case notRunning
case unknown
}
1 change: 0 additions & 1 deletion Jared/ContactHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class ContactHelper {
CNContactGivenNameKey as CNKeyDescriptor,
CNContactEmailAddressesKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor])

if (contacts.count == 1) {
return contacts[0]
}
Expand Down
1 change: 0 additions & 1 deletion Jared/CoreModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ class CoreModule: RoutingModule {
return sender.send("Wrong arguments.", to: message.RespondTo())
}


guard (CNContactStore.authorizationStatus(for: CNEntityType.contacts) == .authorized) else {
return sender.send("Sorry, I do not have access to contacts.", to: message.RespondTo())
}
Expand Down
10 changes: 4 additions & 6 deletions Jared/DatabaseHandler.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// sqlitetest.swift
// DatabaseHandler.swift
// JaredUI
//
// Created by Zeke Snider on 11/9/18.
Expand All @@ -9,11 +9,8 @@
internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

import Cocoa
import JaredFramework
import Foundation
import SQLite3
import Contacts

class DatabaseHandler {
private static let groupQuery = """
Expand Down Expand Up @@ -50,7 +47,6 @@ class DatabaseHandler {
var querySinceID: String?
var shouldExitThread = false
var refreshSeconds = 5.0
var authorizationError = false
var statement: OpaquePointer? = nil
var router: RouterDelegate?

Expand All @@ -59,12 +55,14 @@ class DatabaseHandler {

if sqlite3_open(databaseLocation.path, &db) != SQLITE_OK {
NSLog("Error opening SQLite database. Likely Full disk access error.")
UserDefaults.standard.set(false, forKey: JaredConstants.fullDiskAccess)
diskAccessDelegate?.displayAccessError()
authorizationError = true
return
}
UserDefaults.standard.set(true, forKey: JaredConstants.fullDiskAccess)

querySinceID = getCurrentMaxRecordID()
start()
}

deinit {
Expand Down
4 changes: 2 additions & 2 deletions Jared/InternalModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ class InternalModule: RoutingModule {
}

func enable(_ message: Message) -> Void {
defaults.set(false, forKey: "JaredIsDisabled")
defaults.set(false, forKey: JaredConstants.jaredIsDisabled)
sender.send(NSLocalizedString("enabledMessage"), to: message.RespondTo())
}

func disable(_ message: Message) -> Void {
defaults.set(true, forKey: "JaredIsDisabled")
defaults.set(true, forKey: JaredConstants.jaredIsDisabled)
sender.send(NSLocalizedString("disabledMessage"), to: message.RespondTo())
}

Expand Down
2 changes: 1 addition & 1 deletion Jared/Jared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class Jared: MessageSender {
let defaults = UserDefaults.standard

//Don't send the message if Jared is currently disabled.
guard !defaults.bool(forKey: "JaredIsDisabled") else {
guard !defaults.bool(forKey: JaredConstants.jaredIsDisabled) else {
return
}

Expand Down
21 changes: 21 additions & 0 deletions Jared/JaredConstants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// JaredConstants.swift
// Jared
//
// Created by Zeke Snider on 8/16/20.
// Copyright © 2020 Zeke Snider. All rights reserved.
//

import Foundation

struct JaredConstants {
static let restApiIsDisabled = "RestApiIsDisabled"
static let jaredIsDisabled = "JaredIsDisabled"
static let contactsAccess = "ContactsAccess"
static let sendMessageAccess = "SendMessageAccess"
static let fullDiskAccess = "FullDiskAccess"
static let fullDiskAcccessUrl = "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
static let contactsAccessUrl = "x-apple.systempreferences:com.apple.preference.security?Privacy_Contacts"
static let automationAccessUrl = "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation"
static let messagesUrl = "messages://"
}
2 changes: 2 additions & 0 deletions Jared/JaredDebug.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
Expand Down
2 changes: 2 additions & 0 deletions Jared/JaredRelease.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
Expand Down
10 changes: 6 additions & 4 deletions Jared/JaredWebServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ class JaredWebServer: NSObject {

port = assignPort()

defaults.addObserver(self, forKeyPath: "RestApiIsDisabled", options: .new, context: nil)
defaults.addObserver(self, forKeyPath: JaredConstants.restApiIsDisabled, options: .new, context: nil)
updateServerState()
}

deinit {
UserDefaults.standard.removeObserver(self, forKeyPath: "JaredIsDisabled")
stop()
UserDefaults.standard.removeObserver(self, forKeyPath: JaredConstants.jaredIsDisabled)
}

// Attempt to pull the port number from the config
Expand Down Expand Up @@ -58,17 +59,18 @@ class JaredWebServer: NSObject {
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == "RestApiIsDisabled") {
if (keyPath == JaredConstants.restApiIsDisabled) {
updateServerState()
}
}

func updateServerState() {
if (defaults.bool(forKey: "RestApiIsDisabled")) {
if (defaults.bool(forKey: JaredConstants.restApiIsDisabled)) {
stop()
} else {
start()
}

}

func start() {
Expand Down
56 changes: 56 additions & 0 deletions Jared/PermissionsHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// PermissionsHelper.swift
// Jared
//
// Created by Zeke Snider on 8/16/20.
// Copyright © 2020 Zeke Snider. All rights reserved.
//

import Foundation
import Contacts

class PermissionsHelper {
static func requestMessageAutomation() {
let _ = canSendMessages(shouldPrompt: true)
}

static func canSendMessages(shouldPrompt: Bool = false) -> AutomationPermissionState {
let target = NSAppleEventDescriptor(bundleIdentifier: "com.apple.iChat")
if #available(OSX 10.14, *) {
let permission = AEDeterminePermissionToAutomateTarget(target.aeDesc, typeWildCard, typeWildCard, shouldPrompt)
var permissionEnum: AutomationPermissionState
switch (permission) {
case -1743:
permissionEnum = .declined
case -1744:
permissionEnum = .notDetermined
case 0:
permissionEnum = .authorized
case -600:
permissionEnum = .notRunning
default:
permissionEnum = .unknown
}

UserDefaults.standard.set(permissionEnum.rawValue, forKey: JaredConstants.sendMessageAccess)
return permissionEnum
} else {
UserDefaults.standard.set(AutomationPermissionState.authorized.rawValue, forKey: JaredConstants.sendMessageAccess)
return .authorized
}
}

static func getContactsStatus() -> CNAuthorizationStatus {
let status = CNContactStore.authorizationStatus(for: CNEntityType.contacts)
UserDefaults.standard.set(status.rawValue, forKey: JaredConstants.contactsAccess)
return status
}

static func requestContactsAccess() {
if(CNContactStore.authorizationStatus(for: CNEntityType.contacts) == .notDetermined) {
CNContactStore().requestAccess(for: CNEntityType.contacts, completionHandler: {enabled, _ in
let _ = getContactsStatus()
})
}
}
}
2 changes: 1 addition & 1 deletion Jared/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Router : RouterDelegate {

let defaults = UserDefaults.standard

guard !defaults.bool(forKey: "JaredIsDisabled") || myLowercaseMessage == "/enable" else {
guard !defaults.bool(forKey: JaredConstants.jaredIsDisabled) || myLowercaseMessage == "/enable" else {
return
}

Expand Down
1 change: 0 additions & 1 deletion JaredTests/DatabaseHandlerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class DatabaseHandlerTest: XCTestCase {
helper = DatabaseTestHelper(databaseLocation: testDatabaseLocation)
router = MockRouter()
databaseHandler = DatabaseHandler(router: router, databaseLocation: testDatabaseLocation, diskAccessDelegate: nil)
databaseHandler.start()
}

override func tearDown() {
Expand Down
20 changes: 10 additions & 10 deletions JaredUI/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var sender: Jared
var router: PluginManager
var server: JaredWebServer
var databaseHelper: DatabaseHandler!
override init() {
UserDefaults.standard.register(defaults: [
JaredConstants.jaredIsDisabled: false,
JaredConstants.restApiIsDisabled: true,
JaredConstants.contactsAccess: CNAuthorizationStatus.notDetermined.rawValue,
JaredConstants.fullDiskAccess: true
])
let _ = PermissionsHelper.getContactsStatus()

let configurationURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
.appendingPathComponent("Jared")
.appendingPathComponent("config.json")
Expand All @@ -33,16 +42,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let messageDatabaseURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
.appendingPathComponent("Messages").appendingPathComponent("chat.db")
let viewController = NSApplication.shared.keyWindow?.contentViewController as? ViewController
let dbHandler = DatabaseHandler(router: router.router, databaseLocation: messageDatabaseURL, diskAccessDelegate: viewController)
if (!dbHandler.authorizationError) {
dbHandler.start()
}

// If this is the first run of the application, request access
// to contacts to pull sender info
if(CNContactStore.authorizationStatus(for: CNEntityType.contacts) == .notDetermined) {
CNContactStore().requestAccess(for: CNEntityType.contacts, completionHandler: {_,_ in })
}
databaseHelper = DatabaseHandler(router: router.router, databaseLocation: messageDatabaseURL, diskAccessDelegate: viewController)
}

func applicationWillTerminate(_ aNotification: Notification) {
Expand Down
Loading

0 comments on commit e17e5d5

Please sign in to comment.