From 2a3a4ddc85bd8557b5800afb7b43754be64bf62c Mon Sep 17 00:00:00 2001 From: James Stout Date: Mon, 7 Dec 2020 01:26:30 +0800 Subject: [PATCH 01/37] WIP --- Source/Controllers/DataExport/SPExportController.m | 5 ++++- .../ConnectionView/SPConnectionController.m | 8 +++++++- .../Controllers/Preferences/Panes/SPFilePreferencePane.m | 3 ++- .../Preferences/Panes/SPNetworkPreferencePane.m | 3 ++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index 41d205108..bdb06da79 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -3157,10 +3157,13 @@ - (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err NSString *tmpStr = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; if(dict2[tmpStr] != nil){ + + BOOL bookmarkDataIsStale; + self.userChosenDirectory = [NSURL URLByResolvingBookmarkData:dict2[tmpStr] options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil - bookmarkDataIsStale:nil + bookmarkDataIsStale:&bookmarkDataIsStale error:&error]; *stop = YES; } diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index 7eeba9f72..c295c80cf 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -3372,17 +3372,23 @@ -(void)reRequestSecureAccess{ [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { NSError *error = nil; + + BOOL bookmarkDataIsStale; NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil - bookmarkDataIsStale:nil + bookmarkDataIsStale:&bookmarkDataIsStale error:&error]; if(!error){ [tmpURL startAccessingSecurityScopedResource]; [resolvedBookmarks addObject:tmpURL]; } + else if(bookmarkDataIsStale == YES){ + SPLog("The bookmark is outdated and needs to be regenerated - %@", key); +// _ = saveBookmarkForSelectedURL() + } else{ SPLog(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); } diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 6d908b846..fd9e28a10 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -136,11 +136,12 @@ -(void)reRequestSecureAccess{ [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { NSError *error = nil; + BOOL bookmarkDataIsStale; NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil - bookmarkDataIsStale:nil + bookmarkDataIsStale:&bookmarkDataIsStale error:&error]; if(!error){ diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 4431ac3c0..15b93c96e 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -125,11 +125,12 @@ -(void)reRequestSecureAccess{ [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { NSError *error = nil; + BOOL bookmarkDataIsStale; NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil - bookmarkDataIsStale:nil + bookmarkDataIsStale:&bookmarkDataIsStale error:&error]; if(!error){ From 2b6a52c9405cfb3fdaba738e09f0b44456233cdb Mon Sep 17 00:00:00 2001 From: James Stout Date: Mon, 7 Dec 2020 08:01:16 +0800 Subject: [PATCH 02/37] WIP --- .../ConnectionView/SPConnectionController.m | 76 ++--- Source/Controllers/Other/SecureBookmark.swift | 27 ++ .../Other/SecureBookmarkManager.swift | 260 ++++++++++++++++++ Source/Controllers/SPAppController.m | 3 + Source/Other/Data/SPConstants.h | 1 + Source/Other/Data/SPConstants.m | 2 + sequel-ace.xcodeproj/project.pbxproj | 8 + 7 files changed, 343 insertions(+), 34 deletions(-) create mode 100644 Source/Controllers/Other/SecureBookmark.swift create mode 100644 Source/Controllers/Other/SecureBookmarkManager.swift diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index c295c80cf..90a18423f 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -463,42 +463,50 @@ - (IBAction)chooseKeyLocation:(NSButton *)sender NSString *selectedFilePath=[[self->keySelectionPanel URL] path]; NSError *err=nil; - if([self->keySelectionPanel.URL startAccessingSecurityScopedResource] == YES){ + SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - NSLog(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); - - // a bit of duplicated code here, - // same code is in the export controler - //TODO: put this in a utility/helper class - BOOL __block beenHereBefore = NO; - - // have we been here before? - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - if(dict[self->keySelectionPanel.URL.absoluteString] != nil){ - NSLog(@"beenHereBefore: %@", dict[self->keySelectionPanel.URL.absoluteString]); - beenHereBefore = YES; - *stop = YES; - } - }]; - - if(beenHereBefore == NO){ - // create a bookmark - NSError *error = nil; - // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess - NSData *tmpAppScopedBookmark = [self->keySelectionPanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope - | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - // save to prefs - if(tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - } - + if([sharedSecureBookmarkManager addBookMarkForUrl:self->keySelectionPanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + + SPLog(@"addBookMarkForUrl success"); + } + +// if([self->keySelectionPanel.URL startAccessingSecurityScopedResource] == YES){ +// +// NSLog(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); +// +// // a bit of duplicated code here, +// // same code is in the export controler +// //TODO: put this in a utility/helper class +// BOOL __block beenHereBefore = NO; +// +// // have we been here before? +// [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { +// +// if(dict[self->keySelectionPanel.URL.absoluteString] != nil){ +// NSLog(@"beenHereBefore: %@", dict[self->keySelectionPanel.URL.absoluteString]); +// beenHereBefore = YES; +// *stop = YES; +// } +// }]; +// +// if(beenHereBefore == NO){ +// // create a bookmark +// NSError *error = nil; +// // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess +// NSData *tmpAppScopedBookmark = [self->keySelectionPanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope +// | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) +// includingResourceValuesForKeys:nil +// relativeToURL:nil +// error:&error]; +// // save to prefs +// if(tmpAppScopedBookmark && !error) { +// [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; +// [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; +// } +// } +// +// } // SSH key file selection if (sender == self->sshSSHKeyButton) { if (returnCode == NSModalResponseCancel) { diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift new file mode 100644 index 000000000..3b0b48cb2 --- /dev/null +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -0,0 +1,27 @@ +// +// SecureBookmark.swift +// Sequel Ace +// +// Created by James on 7/12/2020. +// Copyright © 2020 Sequel-Ace. All rights reserved. +// + +import Foundation + +public struct SecureBookmark: Identifiable, Codable { + enum CodingKeys: String, CodingKey { + case id + case bookmarkData = "bookmarkData" + case options = "options" + } + + public let id: String + let bookmarkData: Data + let options: UInt + + public init(id: String, bookmarkData: Data, options: UInt) { + self.id = id + self.bookmarkData = bookmarkData + self.options = options + } +} diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift new file mode 100644 index 000000000..3080fda3b --- /dev/null +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -0,0 +1,260 @@ +// +// SecureBookmarkManager.swift +// Sequel Ace +// +// Created by James on 7/12/2020. +// Copyright © 2020 Sequel-Ace. All rights reserved. +// + +import Foundation +import os.log +import Firebase + + +/* +reRequestSecureAccess +revokeBookmark +addBookmark +handle bookmarkDataIsStale + +*/ + + +@objc final class SecureBookmarkManager: NSObject { + @objc static let sharedInstance = SecureBookmarkManager() + + @objc public var bookmarks: [Dictionary] = [] + @objc public var resolvedBookmarks: [URL] = [] + @objc public var staleBookmarks: [URL] = [] + private var migratedToNewFormat: Bool + + private let log: OSLog + private let prefs: UserDefaults = UserDefaults.standard + + override private init() { + log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") + + migratedToNewFormat = prefs.bool(forKey: SPMigratedBookmarksToNewFormat) + super.init() + + if migratedToNewFormat == false { + migrateToNewFormat() + } + + // error handle? + bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] + + print(bookmarks.count) + + if(bookmarks.count == 1){ + os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) + Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") + bookmarks.removeAll() + return + } + + os_log("bookmarks = %@", log: log, type: .info, bookmarks) + + + // i think part of init of this manager should be to re-request access + + reRequestSecureAccessToBookmarks() + + os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) + os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) + + + } + + private func migrateToNewFormat() { + + // error handle? + bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] + + print(bookmarks.count) + + if(bookmarks.count == 1){ + os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) + Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") + bookmarks.removeAll() + return + } + + os_log("bookmarks = %@", log: log, type: .info, bookmarks) + + let bmCopy = bookmarks + + for (index, bookmarkDict) in bmCopy.enumerated(){ + + print("Found \(bookmarkDict) at position \(index)") + + for (key, urlData) in bookmarkDict { + + os_log("JIMMY key = %@", log: log, type: .info, key) + + let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) + + bookmarks.remove(at: index) // + + let encoder = JSONEncoder() + + do { + let data = try encoder.encode(tmpBM) + + + let tmpDict = [key : data] + bookmarks.append(tmpDict) + + } + catch{ + os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) + Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(key). Error: \(error.localizedDescription)") + } + } + + } + os_log("migrated prefs query hist to db", log: log, type: .info) + migratedToNewFormat = true + prefs.set(true, forKey: SPMigratedBookmarksToNewFormat) + } + + /// reRequestSecureAccessToBookmarks + @objc public func reRequestSecureAccessToBookmarks() { + + let bmCopy = bookmarks + + for (index, bookmarkDict) in bmCopy.enumerated(){ + + print("Found \(bookmarkDict) at position \(index)") + + for (key, urlData) in bookmarkDict { + + os_log("JIMMY key = %@", log: log, type: .info, key) +// os_log("JIMMY value = %@", log: log, type: .info, urlData as CVarArg) + + do { + + let decoder = JSONDecoder() + + let dec = try? decoder.decode(SecureBookmark.self, from: urlData) + + var bookmarkDataIsStale = false + + let urlForBookmark = try URL(resolvingBookmarkData: dec!.bookmarkData , options: URL.BookmarkResolutionOptions(rawValue: dec!.options), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + + // a bookmark might be "stale" because the app hasn't been used + // in many months, macOS has been upgraded, the app was + // re-installed, the app's preferences .plist file was deleted, etc. + if bookmarkDataIsStale { + os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) + if regenerateBookmarkFor(url: urlForBookmark) == true { + bookmarks.remove(at: index) // + } + else{ + staleBookmarks.append(URL(fileURLWithPath: key)) + } + } + else { + os_log("Resolved bookmark: key = %@", log: log, type: .info, key) + _ = urlForBookmark.startAccessingSecurityScopedResource() + resolvedBookmarks.append(urlForBookmark) + } + + } catch { + os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) + Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") + } + } + } + + prefs.set(bookmarks, forKey: SPSecureBookmarks) + } + + // MAY NOT WORK - See https://stackoverflow.com/a/25247535 + /// regenerateBookmarkFor a URL. + /// - Parameters: + /// - url: file URL to generate secure bookmark for + /// - Returns: Bool on success or fail + private func regenerateBookmarkFor(url: URL) -> Bool { + os_log("regenerateBookmarkFor", log: log, type: .debug) + + do { + let bookmarkData = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) + +// let bookmarkData = Data() + + let tmpDict = [url.absoluteString : bookmarkData] + + // i dont think this will work ... + let tmpURL = URL(dataRepresentation: bookmarkData, relativeTo: nil) + _ = tmpURL?.startAccessingSecurityScopedResource() + + bookmarks.append(tmpDict) + prefs.set(bookmarks, forKey: SPSecureBookmarks) + + return true + + } catch { + os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) + Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(url.absoluteString). Error: \(error.localizedDescription)") + return false + } + } + + /// addBookMark + /// - Parameters: + /// - url: file URL to generate secure bookmark for + /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions + /// - Returns: Bool on success or fail + @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { + + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) + + if url.startAccessingSecurityScopedResource() { + + for (index, bookmarkDict) in bookmarks.enumerated(){ + + print("Found \(bookmarkDict) at position \(index)") + + if bookmarkDict[url.absoluteString] != nil { + os_log("beenHereBefore", log: log, type: .debug) + return true + } + } + + do { + + let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + + let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) + + let encoder = JSONEncoder() + + let data = try encoder.encode(tmpBM) + + let decoder = JSONDecoder() + + let dec = try? decoder.decode(SecureBookmark.self, from: data) + + + let optStr = String(format: "%u", options) + + let tmpDict = [url.absoluteString : data] + bookmarks.append(tmpDict) + prefs.set(bookmarks, forKey: SPSecureBookmarks) + + return true + + } catch { + os_log("Error creating secure Bookmark For: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) + Crashlytics.crashlytics().log("Error creating secure Bookmark For: key = \(url.absoluteString). Error: \(error.localizedDescription)") + return false + } + } + else{ + os_log("Error startAccessingSecurityScopedResource For: key = %@.", log: log, type: .error, url.absoluteString) + Crashlytics.crashlytics().log("Error startAccessingSecurityScopedResource For: key = \(url.absoluteString).") + return false + } + } +} diff --git a/Source/Controllers/SPAppController.m b/Source/Controllers/SPAppController.m index 6ecd501ae..bfeac0918 100644 --- a/Source/Controllers/SPAppController.m +++ b/Source/Controllers/SPAppController.m @@ -118,6 +118,9 @@ - (instancetype)init } [NSApp setDelegate:self]; + + SecureBookmarkManager __unused *secureBookmarkManager = SecureBookmarkManager.sharedInstance; + } return self; diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 585021bd6..313dbe628 100644 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -660,6 +660,7 @@ typedef NS_ENUM(NSInteger, SPBundleRedirectAction) { }; extern NSString *SPMigratedQueriesFromPrefs; +extern NSString *SPMigratedBookmarksToNewFormat; extern NSString *SPTraceSQLiteExecutions; // URL scheme diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index efc1de04b..2c9adfd72 100644 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -408,6 +408,8 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPBundleVersionKey = @"bundleVersion"; const long SPBundleCurrentVersion = 2; +NSString *SPMigratedBookmarksToNewFormat = @"MigratedBookmarksToNewFormat"; + NSString *SPBundleFileName = @"command.plist"; NSString *SPBundleTaskInputFilePath = @"~/tmp/SP_BUNDLE_INPUT"; NSString *SPBundleTaskOutputFilePath = @"~/tmp/SP_BUNDLE_OUTPUT"; diff --git a/sequel-ace.xcodeproj/project.pbxproj b/sequel-ace.xcodeproj/project.pbxproj index 5bdf65e4d..e4e3fd910 100644 --- a/sequel-ace.xcodeproj/project.pbxproj +++ b/sequel-ace.xcodeproj/project.pbxproj @@ -142,6 +142,8 @@ 1AB925922551550200063446 /* NumberFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1EE9572551249C0056FECD /* NumberFormatterExtension.swift */; }; 1ADEA5A324BF1C4800D2140B /* SPDateAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADEA5A224BF1C4800D2140B /* SPDateAdditionsTests.m */; }; 1ADEA5A424BF1FFB00D2140B /* SPDateAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 58DF9F3215AB26C2003B4330 /* SPDateAdditions.m */; }; + 1AE63070257D53A500D1EFEE /* SecureBookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE6306F257D53A500D1EFEE /* SecureBookmarkManager.swift */; }; + 1AE6307C257D9A0500D1EFEE /* SecureBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE6307B257D9A0500D1EFEE /* SecureBookmark.swift */; }; 1AF5A261250AC401009885DF /* SPBracketHighlighter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF5A25E250AC401009885DF /* SPBracketHighlighter.m */; }; 1AF5A262250AC401009885DF /* SPBrackets.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AF5A25F250AC401009885DF /* SPBrackets.m */; }; 265446DF24A1616900376B48 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 38C613721C8977E600B3B6EF /* libz.tbd */; }; @@ -757,6 +759,8 @@ 1AB0689924A355B500E2AAC2 /* client-key-crlf.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "client-key-crlf.pem"; sourceTree = ""; }; 1AB068B224A3575600E2AAC2 /* SPValidateKeyAndCertFiles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPValidateKeyAndCertFiles.m; sourceTree = ""; }; 1ADEA5A224BF1C4800D2140B /* SPDateAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPDateAdditionsTests.m; sourceTree = ""; }; + 1AE6306F257D53A500D1EFEE /* SecureBookmarkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBookmarkManager.swift; sourceTree = ""; }; + 1AE6307B257D9A0500D1EFEE /* SecureBookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBookmark.swift; sourceTree = ""; }; 1AF5A25D250AC401009885DF /* SPBracketHighlighter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPBracketHighlighter.h; sourceTree = ""; }; 1AF5A25E250AC401009885DF /* SPBracketHighlighter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPBracketHighlighter.m; sourceTree = ""; }; 1AF5A25F250AC401009885DF /* SPBrackets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPBrackets.m; sourceTree = ""; }; @@ -1523,6 +1527,8 @@ 5806B76211A991EC00813A88 /* SPDocumentController.h */, 5806B76311A991EC00813A88 /* SPDocumentController.m */, 1A9EB9AD25651F5000FE60FF /* SQLiteHistoryManager.swift */, + 1AE6306F257D53A500D1EFEE /* SecureBookmarkManager.swift */, + 1AE6307B257D9A0500D1EFEE /* SecureBookmark.swift */, ); path = Other; sourceTree = ""; @@ -3076,6 +3082,7 @@ 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */, 58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */, 1A2711CF2539D9B10066ED58 /* SPReachability.m in Sources */, + 1AE6307C257D9A0500D1EFEE /* SecureBookmark.swift in Sources */, 172A65110F7BED7A001E861A /* SPConsoleMessage.m in Sources */, 179F15060F7C433C00579954 /* SPEditorTokens.l in Sources */, B5E92F1C0F75B2E800012500 /* SPExportController.m in Sources */, @@ -3187,6 +3194,7 @@ 17D3C671128AD8160047709F /* SPSingleton.m in Sources */, 17D3C6D3128B1C900047709F /* SPFavoritesOutlineView.m in Sources */, 50D3C3521A77135F00B5429C /* SPParserUtils.c in Sources */, + 1AE63070257D53A500D1EFEE /* SecureBookmarkManager.swift in Sources */, BC68BFC7128D4EAE004907D9 /* SPBundleEditorController.m in Sources */, BC1944D01297291800A236CD /* SPBundleCommandTextView.m in Sources */, BC77C5E4129AA69E009AD832 /* SPBundleHTMLOutputController.m in Sources */, From 19e0871afed029111b42bcc3c7037723df027692 Mon Sep 17 00:00:00 2001 From: James Stout Date: Mon, 7 Dec 2020 18:39:27 +0800 Subject: [PATCH 03/37] WIP --- Source/Controllers/Other/SecureBookmark.swift | 44 ++++--- .../Other/SecureBookmarkManager.swift | 107 ++++++++++++------ Source/Other/Extensions/DataExtension.swift | 9 ++ 3 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 Source/Other/Extensions/DataExtension.swift diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 3b0b48cb2..8db361ca3 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -8,20 +8,38 @@ import Foundation -public struct SecureBookmark: Identifiable, Codable { - enum CodingKeys: String, CodingKey { - case id - case bookmarkData = "bookmarkData" - case options = "options" - } +//public struct SecureBookmark: Identifiable, Codable { +// enum CodingKeys: String, CodingKey { +// case id = "theId" +// case bookmarkData = "theBookmarkData" +// case options = "theOptions" +// } +// +// public let id: String +// let bookmarkData: Data +// let options: UInt +// +// public init(id: String, bookmarkData: Data, options: UInt) { +// self.id = id +// self.bookmarkData = bookmarkData +// self.options = options +// } +//} - public let id: String +struct SecureBookmark: Encodable, Decodable { + + let id: String let bookmarkData: Data let options: UInt - - public init(id: String, bookmarkData: Data, options: UInt) { - self.id = id - self.bookmarkData = bookmarkData - self.options = options - } } + + +// +//public struct SecureBookmark: Codable { +// +// let id: String +// let bookmarkData: Data +// let options: UInt +// +// +//} diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 3080fda3b..70582bfc9 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -60,8 +60,8 @@ handle bookmarkDataIsStale reRequestSecureAccessToBookmarks() - os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) - os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) +// os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) +// os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) } @@ -79,32 +79,61 @@ handle bookmarkDataIsStale bookmarks.removeAll() return } - + + + os_log("bookmarks = %@", log: log, type: .info, bookmarks) let bmCopy = bookmarks + + var indiciesToRemove : [Int] = [] for (index, bookmarkDict) in bmCopy.enumerated(){ print("Found \(bookmarkDict) at position \(index)") - + for (key, urlData) in bookmarkDict { - + + do { + let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) + + let plist = try PropertyListEncoder().encode(tmpBM) + + os_log("JIMMY key = %@", log: log, type: .info, plist as CVarArg) + + + } catch { + print(error) + } + let d2Str = urlData.hexEncodedString() + + let d2Data = NSKeyedArchiver.archivedData(withRootObject: urlData) + os_log("JIMMY key = %@", log: log, type: .info, key) - let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) - - bookmarks.remove(at: index) // - - let encoder = JSONEncoder() - +// let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) + + let json = """ + { + "id" : "\(key)", + "bookmarkData" : "\(d2Str)", + "options" : \(URL.BookmarkCreationOptions.withSecurityScope.rawValue) + } + """ + + let jsonData = json.data(using: .utf8)! + +// let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) + +// let encoder = JSONEncoder() + do { - let data = try encoder.encode(tmpBM) - - - let tmpDict = [key : data] +// let data = try encoder.encode(json) + // + let tmpDict = [key : jsonData] bookmarks.append(tmpDict) - + indiciesToRemove.append(index) + } catch{ os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) @@ -113,14 +142,24 @@ handle bookmarkDataIsStale } } - os_log("migrated prefs query hist to db", log: log, type: .info) + os_log("migrated bookmarks to new format", log: log, type: .info) migratedToNewFormat = true prefs.set(true, forKey: SPMigratedBookmarksToNewFormat) + + for indexToRemove in indiciesToRemove.reversed(){ + print(indexToRemove) + bookmarks.remove(at: indexToRemove) + } + + prefs.set(bookmarks, forKey: SPSecureBookmarks2) } /// reRequestSecureAccessToBookmarks @objc public func reRequestSecureAccessToBookmarks() { - + + // re-read + bookmarks = prefs.array(forKey: SPSecureBookmarks2) as? [[String: Data]] ?? [["": Data()]] + let bmCopy = bookmarks for (index, bookmarkDict) in bmCopy.enumerated(){ @@ -134,17 +173,16 @@ handle bookmarkDataIsStale do { - let decoder = JSONDecoder() - let dec = try? decoder.decode(SecureBookmark.self, from: urlData) + let blogPost: SecureBookmark = try! JSONDecoder().decode(SecureBookmark.self, from: urlData) var bookmarkDataIsStale = false - let urlForBookmark = try URL(resolvingBookmarkData: dec!.bookmarkData , options: URL.BookmarkResolutionOptions(rawValue: dec!.options), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + let urlForBookmark = try URL(resolvingBookmarkData: blogPost.bookmarkData , options: URL.BookmarkResolutionOptions(rawValue: blogPost.options), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) - // a bookmark might be "stale" because the app hasn't been used - // in many months, macOS has been upgraded, the app was - // re-installed, the app's preferences .plist file was deleted, etc. +// a bookmark might be "stale" because the app hasn't been used +// in many months, macOS has been upgraded, the app was +// re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) if regenerateBookmarkFor(url: urlForBookmark) == true { @@ -225,20 +263,23 @@ handle bookmarkDataIsStale do { let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - - let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) + + + let json = """ + { + "id": \(url.absoluteString), + "bookmarkData": \(bookmarkData), + "options": \(options) + } + """ + +// let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) let encoder = JSONEncoder() - let data = try encoder.encode(tmpBM) + let data = try encoder.encode(json) - let decoder = JSONDecoder() - - let dec = try? decoder.decode(SecureBookmark.self, from: data) - - let optStr = String(format: "%u", options) - let tmpDict = [url.absoluteString : data] bookmarks.append(tmpDict) prefs.set(bookmarks, forKey: SPSecureBookmarks) diff --git a/Source/Other/Extensions/DataExtension.swift b/Source/Other/Extensions/DataExtension.swift new file mode 100644 index 000000000..f1b77aaaf --- /dev/null +++ b/Source/Other/Extensions/DataExtension.swift @@ -0,0 +1,9 @@ +// +// DataExtension.swift +// sequel-ace +// +// Created by James on 7/12/2020. +// Copyright © 2020 Sequel-Ace. All rights reserved. +// + +import Foundation From c1d6aee7a7853862246af55adcee09fba56736d9 Mon Sep 17 00:00:00 2001 From: James Stout Date: Mon, 7 Dec 2020 21:59:28 +0800 Subject: [PATCH 04/37] WIP --- Source/Controllers/Other/SecureBookmark.swift | 2 +- .../Other/SecureBookmarkManager.swift | 227 +++++++----------- Source/Other/Data/SPConstants.h | 3 +- Source/Other/Data/SPConstants.m | 4 +- sequel-ace.xcodeproj/project.pbxproj | 6 + 5 files changed, 99 insertions(+), 143 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 8db361ca3..a943a241b 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -29,7 +29,7 @@ import Foundation struct SecureBookmark: Encodable, Decodable { let id: String - let bookmarkData: Data + let bookmarkData: String let options: UInt } diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 70582bfc9..8ea2a8bbb 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -24,55 +24,56 @@ handle bookmarkDataIsStale @objc static let sharedInstance = SecureBookmarkManager() @objc public var bookmarks: [Dictionary] = [] + private var bookmarkOptions: [Dictionary] = [] @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] - private var migratedToNewFormat: Bool - + private var createdBookmarkOptions: Bool + private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard - + override private init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") - - migratedToNewFormat = prefs.bool(forKey: SPMigratedBookmarksToNewFormat) + + createdBookmarkOptions = prefs.bool(forKey: SPCreatedBookmarksOptions) super.init() - if migratedToNewFormat == false { - migrateToNewFormat() + if createdBookmarkOptions == false { + createBookmarkOptions() } // error handle? bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] - + bookmarkOptions = prefs.array(forKey: SPSecureBookmarkOptions) as? [[String: UInt]] ?? [["": 0]] + print(bookmarks.count) - + if(bookmarks.count == 1){ os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") bookmarks.removeAll() return } - + os_log("bookmarks = %@", log: log, type: .info, bookmarks) - - + + // i think part of init of this manager should be to re-request access - reRequestSecureAccessToBookmarks() - + // os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) // os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) - + } - - private func migrateToNewFormat() { - + + private func createBookmarkOptions() { + // error handle? bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] - + print(bookmarks.count) - + if(bookmarks.count == 1){ os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") @@ -80,115 +81,74 @@ handle bookmarkDataIsStale return } - - os_log("bookmarks = %@", log: log, type: .info, bookmarks) - - let bmCopy = bookmarks - var indiciesToRemove : [Int] = [] - - for (index, bookmarkDict) in bmCopy.enumerated(){ - - print("Found \(bookmarkDict) at position \(index)") + for (index, bookmarkDict) in bookmarks.enumerated(){ - for (key, urlData) in bookmarkDict { + os_log("Found %@ at position %i", log: log, type: .info, bookmarkDict, index) - do { - let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) + for (key, _) in bookmarkDict { + let opts = URL.BookmarkResolutionOptions.withSecurityScope.rawValue // default to this as we don't know what they were created with + bookmarkOptions.append([key : opts]) + } - let plist = try PropertyListEncoder().encode(tmpBM) + } - os_log("JIMMY key = %@", log: log, type: .info, plist as CVarArg) + os_log("createdBookmarkOptions for existing bookmarks", log: log, type: .info) + createdBookmarkOptions = true + prefs.set(true, forKey: SPCreatedBookmarksOptions) + prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) + } + /// reRequestSecureAccessToBookmarks + @objc public func reRequestSecureAccessToBookmarks() { - } catch { - print(error) - } - let d2Str = urlData.hexEncodedString() + //re-read ? + bookmarkOptions = prefs.array(forKey: SPSecureBookmarkOptions) as? [[String: UInt]] ?? [["": 0]] - let d2Data = NSKeyedArchiver.archivedData(withRootObject: urlData) + let bmCopy = bookmarks - os_log("JIMMY key = %@", log: log, type: .info, key) - -// let tmpBM : SecureBookmark = SecureBookmark(id: key, bookmarkData: urlData, options: URL.BookmarkCreationOptions.withSecurityScope.rawValue) - - let json = """ - { - "id" : "\(key)", - "bookmarkData" : "\(d2Str)", - "options" : \(URL.BookmarkCreationOptions.withSecurityScope.rawValue) - } - """ + for (index, bookmarkDict) in bmCopy.enumerated(){ - let jsonData = json.data(using: .utf8)! + os_log("Found %@ at position %i", log: log, type: .info, bookmarkDict, index) -// let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) + for (key, urlData) in bookmarkDict { -// let encoder = JSONEncoder() + os_log("JIMMY key = %@", log: log, type: .info, key) do { -// let data = try encoder.encode(json) - // - let tmpDict = [key : jsonData] - bookmarks.append(tmpDict) - indiciesToRemove.append(index) - } - catch{ - os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) - Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(key). Error: \(error.localizedDescription)") - } - } - - } - os_log("migrated bookmarks to new format", log: log, type: .info) - migratedToNewFormat = true - prefs.set(true, forKey: SPMigratedBookmarksToNewFormat) + var bookmarkOptionsForKey : UInt = 1024 - for indexToRemove in indiciesToRemove.reversed(){ - print(indexToRemove) - bookmarks.remove(at: indexToRemove) - } + for (_, bookmarkOptionsDict) in bookmarkOptions.enumerated(){ + for (optKey, bmOptions) in bookmarkOptionsDict { - prefs.set(bookmarks, forKey: SPSecureBookmarks2) - } - - /// reRequestSecureAccessToBookmarks - @objc public func reRequestSecureAccessToBookmarks() { - - // re-read - bookmarks = prefs.array(forKey: SPSecureBookmarks2) as? [[String: Data]] ?? [["": Data()]] + os_log("JIMMY key = %@", log: log, type: .info, key) + os_log("JIMMY optKey = %@", log: log, type: .info, optKey) - let bmCopy = bookmarks - - for (index, bookmarkDict) in bmCopy.enumerated(){ - - print("Found \(bookmarkDict) at position \(index)") - - for (key, urlData) in bookmarkDict { - - os_log("JIMMY key = %@", log: log, type: .info, key) -// os_log("JIMMY value = %@", log: log, type: .info, urlData as CVarArg) - - do { - + if key == optKey { + bookmarkOptionsForKey = bmOptions + os_log("JIMMY bookmarkOptionsForKey = %i", log: log, type: .info, bookmarkOptionsForKey) + break + } + } + } - let blogPost: SecureBookmark = try! JSONDecoder().decode(SecureBookmark.self, from: urlData) - var bookmarkDataIsStale = false - let urlForBookmark = try URL(resolvingBookmarkData: blogPost.bookmarkData , options: URL.BookmarkResolutionOptions(rawValue: blogPost.options), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) - + let urlForBookmark = try URL(resolvingBookmarkData: urlData , options: [URL.BookmarkResolutionOptions(rawValue: bookmarkOptionsForKey)], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + // a bookmark might be "stale" because the app hasn't been used // in many months, macOS has been upgraded, the app was // re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) - if regenerateBookmarkFor(url: urlForBookmark) == true { + if regenerateBookmarkFor(url: urlForBookmark, options: bookmarkOptionsForKey) == true { + os_log("Stale bookmark regenerated: key = %@", log: log, type: .error, key) bookmarks.remove(at: index) // } else{ + os_log("Regen failed! Stale bookmark added: key = %@", log: log, type: .error, key) staleBookmarks.append(URL(fileURLWithPath: key)) } } @@ -197,95 +157,84 @@ handle bookmarkDataIsStale _ = urlForBookmark.startAccessingSecurityScopedResource() resolvedBookmarks.append(urlForBookmark) } - + } catch { + staleBookmarks.append(URL(fileURLWithPath: key)) os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") } } } - + prefs.set(bookmarks, forKey: SPSecureBookmarks) } - + // MAY NOT WORK - See https://stackoverflow.com/a/25247535 /// regenerateBookmarkFor a URL. /// - Parameters: /// - url: file URL to generate secure bookmark for /// - Returns: Bool on success or fail - private func regenerateBookmarkFor(url: URL) -> Bool { + private func regenerateBookmarkFor(url: URL, options: UInt) -> Bool { os_log("regenerateBookmarkFor", log: log, type: .debug) - + + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) + + do { - let bookmarkData = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) - + let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + // let bookmarkData = Data() - + let tmpDict = [url.absoluteString : bookmarkData] - + // i dont think this will work ... let tmpURL = URL(dataRepresentation: bookmarkData, relativeTo: nil) _ = tmpURL?.startAccessingSecurityScopedResource() - + bookmarks.append(tmpDict) prefs.set(bookmarks, forKey: SPSecureBookmarks) - + return true - + } catch { os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(url.absoluteString). Error: \(error.localizedDescription)") return false } } - + /// addBookMark /// - Parameters: /// - url: file URL to generate secure bookmark for /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions /// - Returns: Bool on success or fail @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { - + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) - + if url.startAccessingSecurityScopedResource() { - + for (index, bookmarkDict) in bookmarks.enumerated(){ - + print("Found \(bookmarkDict) at position \(index)") - + if bookmarkDict[url.absoluteString] != nil { os_log("beenHereBefore", log: log, type: .debug) return true } } - - do { - - let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + do { - let json = """ - { - "id": \(url.absoluteString), - "bookmarkData": \(bookmarkData), - "options": \(options) - } - """ - -// let tmpBM : SecureBookmark = SecureBookmark(id: url.absoluteString, bookmarkData: bookmarkData, options: options) - - let encoder = JSONEncoder() - - let data = try encoder.encode(json) - + let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - let tmpDict = [url.absoluteString : data] - bookmarks.append(tmpDict) + bookmarks.append([url.absoluteString : bookmarkData]) + bookmarkOptions.append([url.absoluteString : options]) prefs.set(bookmarks, forKey: SPSecureBookmarks) - + prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) + return true - + } catch { os_log("Error creating secure Bookmark For: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) Crashlytics.crashlytics().log("Error creating secure Bookmark For: key = \(url.absoluteString). Error: \(error.localizedDescription)") diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 313dbe628..2319b73a3 100644 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -425,6 +425,7 @@ extern NSString *SPFileName24HourTimeTokenName; extern NSString *SPFileNameFavoriteTokenName; extern NSString *SPFileNameTableTokenName; extern NSString *SPSecureBookmarks; +extern NSString *SPSecureBookmarkOptions; // Misc extern NSString *SPContentFilters; @@ -660,7 +661,7 @@ typedef NS_ENUM(NSInteger, SPBundleRedirectAction) { }; extern NSString *SPMigratedQueriesFromPrefs; -extern NSString *SPMigratedBookmarksToNewFormat; +extern NSString *SPCreatedBookmarksOptions; extern NSString *SPTraceSQLiteExecutions; // URL scheme diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 2c9adfd72..0db3bc90d 100644 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -220,7 +220,7 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPImportClipboardTempFileNamePrefix = @"~/tmp/_SP_ClipBoard_Import_File_"; NSString *SPLastExportSettings = @"LastExportSettings"; NSString *SPSecureBookmarks = @"SPSecureBookmarks"; - +NSString *SPSecureBookmarkOptions = @"SPSecureBookmarkOptions"; // Export filename tokens NSString *SPFileNameDatabaseTokenName = @"database"; NSString *SPFileNameHostTokenName = @"host"; @@ -408,7 +408,7 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPBundleVersionKey = @"bundleVersion"; const long SPBundleCurrentVersion = 2; -NSString *SPMigratedBookmarksToNewFormat = @"MigratedBookmarksToNewFormat"; +NSString *SPCreatedBookmarksOptions = @"CreatedBookmarksOptions"; NSString *SPBundleFileName = @"command.plist"; NSString *SPBundleTaskInputFilePath = @"~/tmp/SP_BUNDLE_INPUT"; diff --git a/sequel-ace.xcodeproj/project.pbxproj b/sequel-ace.xcodeproj/project.pbxproj index e4e3fd910..a763f6d4f 100644 --- a/sequel-ace.xcodeproj/project.pbxproj +++ b/sequel-ace.xcodeproj/project.pbxproj @@ -118,6 +118,8 @@ 1A2711CF2539D9B10066ED58 /* SPReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A2711CE2539D9B10066ED58 /* SPReachability.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 1A2711E82539E2BB0066ED58 /* local-connection.html in Resources */ = {isa = PBXBuildFile; fileRef = 1A2711E72539E2BB0066ED58 /* local-connection.html */; }; 1A3FA7962495FC2D00B7291A /* SPMainThreadTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 589582141154F8F400EDCC28 /* SPMainThreadTrampoline.m */; }; + 1A416A7A257E3582005A902B /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A416A79257E3582005A902B /* DataExtension.swift */; }; + 1A416A7B257E3582005A902B /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A416A79257E3582005A902B /* DataExtension.swift */; }; 1A47996F2543343A00D29E6E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1A47996E2543343A00D29E6E /* GoogleService-Info.plist */; }; 1A564F74237E2E4958CA593A /* SPPillAttachmentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A56463D14569A0B56EE8BAC /* SPPillAttachmentCell.m */; }; 1A5A83532545DA8B00EDC196 /* SPObjectAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D878A15140FEB00F24774 /* SPObjectAdditions.m */; }; @@ -738,6 +740,7 @@ 1A2711CD2539D9B10066ED58 /* SPReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPReachability.h; sourceTree = ""; }; 1A2711CE2539D9B10066ED58 /* SPReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPReachability.m; sourceTree = ""; }; 1A2711E72539E2BB0066ED58 /* local-connection.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "local-connection.html"; sourceTree = ""; }; + 1A416A79257E3582005A902B /* DataExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExtension.swift; sourceTree = ""; }; 1A47996E2543343A00D29E6E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Plists/GoogleService-Info.plist"; sourceTree = ""; }; 1A479B15254375BD00D29E6E /* License.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = License.md; sourceTree = ""; }; 1A479B19254375D200D29E6E /* Pods-Sequel Ace-acknowledgements.markdown */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Pods-Sequel Ace-acknowledgements.markdown"; sourceTree = ""; }; @@ -2295,6 +2298,7 @@ 1A9D83A325514E740024B563 /* DateComponentsFormatterExtension.swift */, 1A94988D25516057000BC793 /* DateExtension.swift */, 5193502E2567D2FB001272B5 /* CollectionExtension.swift */, + 1A416A79257E3582005A902B /* DataExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -3004,6 +3008,7 @@ 50EA92641AB23EAD008D3C4F /* SPDatabaseCopy.m in Sources */, 1AB068B424A3577C00E2AAC2 /* SPValidateKeyAndCertFiles.m in Sources */, 50D3C35D1A77217800B5429C /* SPParserUtils.c in Sources */, + 1A416A7B257E3582005A902B /* DataExtension.swift in Sources */, 380F4EF50FC0B68F00B0BFD7 /* SPStringAdditionsTests.m in Sources */, 1A5A83532545DA8B00EDC196 /* SPObjectAdditions.m in Sources */, 1798F1C4155018E2004B0AB8 /* SPMutableArrayAdditionsTests.m in Sources */, @@ -3159,6 +3164,7 @@ 11B55BFE1189E3B2009EF465 /* SPDatabaseAction.m in Sources */, 58A8A79111A036C000B95749 /* SPWindowController.m in Sources */, 1AF5A261250AC401009885DF /* SPBracketHighlighter.m in Sources */, + 1A416A7A257E3582005A902B /* DataExtension.swift in Sources */, 5806B76411A991EC00813A88 /* SPDocumentController.m in Sources */, 173C837211AAD26E00B8B084 /* SPExportUtilities.m in Sources */, 51F4AFBF24B26665006144D5 /* NSAlertExtension.swift in Sources */, From 87d09cc613ae5b04d0b616e5aabe04d62591c057 Mon Sep 17 00:00:00 2001 From: James Stout Date: Mon, 7 Dec 2020 23:53:55 +0800 Subject: [PATCH 05/37] WIP --- Source/Controllers/Other/SecureBookmarkManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 8ea2a8bbb..f6209e62f 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -143,7 +143,7 @@ handle bookmarkDataIsStale // re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) - if regenerateBookmarkFor(url: urlForBookmark, options: bookmarkOptionsForKey) == true { + if regenerateBookmarkFor(url: URL(fileURLWithPath: key), options: bookmarkOptionsForKey) == true { os_log("Stale bookmark regenerated: key = %@", log: log, type: .error, key) bookmarks.remove(at: index) // } From 96105c356ed08bd6bf97fe99274dba78b672186c Mon Sep 17 00:00:00 2001 From: James Stout Date: Wed, 16 Dec 2020 03:36:41 +0800 Subject: [PATCH 06/37] WIP --- .../DataExport/SPExportController.m | 3 +- .../ConnectionView/SPConnectionController.m | 125 +++++++++--------- Source/Controllers/Other/SecureBookmark.swift | 57 ++++---- .../Other/SecureBookmarkManager.swift | 98 +++++++++++--- .../Preferences/Panes/SPFilePreferencePane.m | 94 +++++++------ .../Panes/SPNetworkPreferencePane.m | 9 +- sequel-ace.xcodeproj/project.pbxproj | 8 ++ 7 files changed, 238 insertions(+), 156 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index 57a0b2c32..68409848a 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -635,7 +635,8 @@ - (IBAction)changeExportOutputPath:(id)sender // user has selected the folder and we have com.apple.security.files.user-selected.read-write if([self->changeExportOutputPathPanel.URL startAccessingSecurityScopedResource] == YES){ - NSLog(@"got access to: %@", self->changeExportOutputPathPanel.URL.absoluteString); + SPLog(@"got access to: %@", self->changeExportOutputPathPanel.URL.absoluteString); + CLS_LOG(@"got access to: %@", self->changeExportOutputPathPanel.URL.absoluteString); BOOL __block beenHereBefore = NO; diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index 1c57d1149..798d4529e 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -172,6 +172,8 @@ @implementation SPConnectionController @synthesize isEditingConnection; + (void)initialize { + + } - (NSString *)keychainPassword @@ -463,67 +465,58 @@ - (IBAction)chooseKeyLocation:(NSButton *)sender [keySelectionPanel setDelegate:self]; [keySelectionPanel beginSheetModalForWindow:[dbDocument parentWindow] completionHandler:^(NSInteger returnCode) { - NSString *selectedFilePath=[[self->keySelectionPanel URL] path]; - NSError *err=nil; - - SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - - if([sharedSecureBookmarkManager addBookMarkForUrl:self->keySelectionPanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - // save to prefs - if(tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - else{ - SPLog(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); - CLS_LOG(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); - } - } - } - else{ - SPLog(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); - CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); - } + NSString *selectedFilePath=[[self->keySelectionPanel URL] path]; + NSError *err=nil; + + SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; + + + if([self->keySelectionPanel.URL startAccessingSecurityScopedResource] == YES){ + + SPLog(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); + CLS_LOG(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); + + // a bit of duplicated code here, + // same code is in the export controler + //TODO: put this in a utility/helper class + BOOL __block beenHereBefore = NO; + + // have we been here before? + [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { + + if(dict[self->keySelectionPanel.URL.absoluteString] != nil){ + NSLog(@"beenHereBefore: %@", dict[self->keySelectionPanel.URL.absoluteString]); + beenHereBefore = YES; + *stop = YES; + } + }]; + + if(beenHereBefore == NO){ + // create a bookmark + NSError *error = nil; + // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess + NSData *tmpAppScopedBookmark = [self->keySelectionPanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope + | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) + includingResourceValuesForKeys:nil + relativeToURL:nil + error:&error]; + // save to prefs + if(tmpAppScopedBookmark && !error) { + [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; + [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; + } + else{ + SPLog(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); + CLS_LOG(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); + } + } + } + else{ + SPLog(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); + CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); + } -// if([self->keySelectionPanel.URL startAccessingSecurityScopedResource] == YES){ -// -// NSLog(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); -// -// // a bit of duplicated code here, -// // same code is in the export controler -// //TODO: put this in a utility/helper class -// BOOL __block beenHereBefore = NO; -// -// // have we been here before? -// [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { -// -// if(dict[self->keySelectionPanel.URL.absoluteString] != nil){ -// NSLog(@"beenHereBefore: %@", dict[self->keySelectionPanel.URL.absoluteString]); -// beenHereBefore = YES; -// *stop = YES; -// } -// }]; -// -// if(beenHereBefore == NO){ -// // create a bookmark -// NSError *error = nil; -// // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess -// NSData *tmpAppScopedBookmark = [self->keySelectionPanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope -// | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) -// includingResourceValuesForKeys:nil -// relativeToURL:nil -// error:&error]; -// // save to prefs -// if(tmpAppScopedBookmark && !error) { -// [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; -// [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; -// } -// } -// -// } + // SSH key file selection if (sender == self->sshSSHKeyButton) { if (returnCode == NSModalResponseCancel) { @@ -1345,7 +1338,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N [bookmarks setArray:o]; } - [self reRequestSecureAccess]; +// [self reRequestSecureAccess]; } } @@ -3235,6 +3228,9 @@ - (void)_setNodeIsExpanded:(BOOL)expanded fromNotification:(NSNotification *)not */ - (instancetype)initWithDocument:(SPDatabaseDocument *)document { + + SPLog(@"initWithDocument"); + if ((self = [super init])) { // Weak reference @@ -3243,6 +3239,7 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document databaseConnectionSuperview = [dbDocument databaseView]; databaseConnectionView = dbDocument->contentViewSplitter; + // Keychain references connectionKeychainItemName = nil; connectionKeychainItemAccount = nil; @@ -3314,9 +3311,11 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document SPLog(@"Could not load SPSecureBookmarks from prefs"); CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); } - + + SecureBookmarkManager __unused *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; + // we need to re-request access to places we've been before.. - [self reRequestSecureAccess]; +// [self reRequestSecureAccess]; // add an observer to get re-read the bookmarks when they change [prefs addObserver:self diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index a943a241b..a9da20d93 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -8,38 +8,35 @@ import Foundation -//public struct SecureBookmark: Identifiable, Codable { -// enum CodingKeys: String, CodingKey { -// case id = "theId" -// case bookmarkData = "theBookmarkData" -// case options = "theOptions" -// } -// -// public let id: String -// let bookmarkData: Data -// let options: UInt -// -// public init(id: String, bookmarkData: Data, options: UInt) { -// self.id = id -// self.bookmarkData = bookmarkData -// self.options = options -// } -//} -struct SecureBookmark: Encodable, Decodable { - - let id: String - let bookmarkData: String - let options: UInt +struct SecureBookmark: Codable { + let bookmarkData: Data + let options: Double + let theUrl: URL } -// -//public struct SecureBookmark: Codable { -// -// let id: String -// let bookmarkData: Data -// let options: UInt -// -// +//extension SecureBookmark { +// func encode() -> Data { +// let data = NSMutableData() +// let archiver = NSKeyedArchiver(forWritingWith: data) +// archiver.encode(bookmarkData, forKey: "bookmarkData") +// archiver.encode(options, forKey: "options") +// archiver.encode(theUrl, forKey: "theUrl") +// archiver.finishEncoding() +// return data as Data +// } +// +// init?(data: Data) { +// let unarchiver = NSKeyedUnarchiver(forReadingWith: data) +// defer { +// unarchiver.finishDecoding() +// } +// guard let bookmarkData = unarchiver.decodeObject(forKey: "bookmarkData") as? Data else { return nil } +// guard let theUrl = unarchiver.decodeObject(forKey: "theUrl") as? URL else { return nil } +// options = unarchiver.decodeDouble(forKey: "options") +// self.bookmarkData = bookmarkData +// self.theUrl = theUrl +// } //} + diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index f6209e62f..4ff423f70 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -48,7 +48,7 @@ handle bookmarkDataIsStale print(bookmarks.count) - if(bookmarks.count == 1){ + if(bookmarks.count < 2){ os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") bookmarks.removeAll() @@ -136,20 +136,43 @@ handle bookmarkDataIsStale var bookmarkDataIsStale = false - let urlForBookmark = try URL(resolvingBookmarkData: urlData , options: [URL.BookmarkResolutionOptions(rawValue: bookmarkOptionsForKey)], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) +// let sp = SecureBookmark(bookmarkData: urlData, options: Double(URL.BookmarkResolutionOptions(rawValue: bookmarkOptionsForKey).rawValue)) +// +// UserDefaults.standard.set(sp.encode(), forKey: "sp") + + let decoder = JSONDecoder() + let sp = try decoder.decode(SecureBookmark.self, from: urlData) + + print(sp.theUrl) + +// let sp = SecureBookmark(data: urlData) + + let urlForBookmark = try URL(resolvingBookmarkData: sp.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(sp.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // a bookmark might be "stale" because the app hasn't been used // in many months, macOS has been upgraded, the app was // re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) - if regenerateBookmarkFor(url: URL(fileURLWithPath: key), options: bookmarkOptionsForKey) == true { + + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(sp.options)) + + let res = sp.theUrl.startAccessingSecurityScopedResource() + + print(res) + + let bookmarkData = try sp.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + + let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: sp.theUrl) + + if regenerateBookmarkFor(secureBookMark: sp ) == true { os_log("Stale bookmark regenerated: key = %@", log: log, type: .error, key) bookmarks.remove(at: index) // } else{ os_log("Regen failed! Stale bookmark added: key = %@", log: log, type: .error, key) staleBookmarks.append(URL(fileURLWithPath: key)) + bookmarks.remove(at: index) } } else { @@ -167,6 +190,21 @@ handle bookmarkDataIsStale } prefs.set(bookmarks, forKey: SPSecureBookmarks) + +// if let data = UserDefaults.standard.object(forKey: "sp") as? Data { +// let room = SecureBookmark(data: data) +//// os_log("JIMMY room = %@", log: log, type: .info, room as! CVarArg) +// +// var bookmarkDataIsStale = false +// +// do { +// let urlForBookmark = try URL(resolvingBookmarkData: room!.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(room!.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) +// +// os_log("JIMMY room = %@", log: log, type: .info, urlForBookmark as CVarArg) +// +// } +// catch{} +// } } // MAY NOT WORK - See https://stackoverflow.com/a/25247535 @@ -174,31 +212,40 @@ handle bookmarkDataIsStale /// - Parameters: /// - url: file URL to generate secure bookmark for /// - Returns: Bool on success or fail - private func regenerateBookmarkFor(url: URL, options: UInt) -> Bool { + private func regenerateBookmarkFor(secureBookMark: SecureBookmark) -> Bool { os_log("regenerateBookmarkFor", log: log, type: .debug) - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) - + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(secureBookMark.options)) +// let urlForBookmark = try URL(resolvingBookmarkData: sp!.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(sp!.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) do { - let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) -// let bookmarkData = Data() + let bookmarkData = try secureBookMark.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - let tmpDict = [url.absoluteString : bookmarkData] + let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: secureBookMark.theUrl) - // i dont think this will work ... - let tmpURL = URL(dataRepresentation: bookmarkData, relativeTo: nil) - _ = tmpURL?.startAccessingSecurityScopedResource() + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(sp) + print(String(data: data, encoding: .utf8)!) - bookmarks.append(tmpDict) - prefs.set(bookmarks, forKey: SPSecureBookmarks) + +// let spData = sp.encode() + bookmarks.append([secureBookMark.theUrl.absoluteString : data]) +// bookmarks.append([url.absoluteString : bookmarkData]) + + bookmarkOptions.append([secureBookMark.theUrl.absoluteString : bookmarkCreationOptions.rawValue]) + prefs.set(bookmarks, forKey: SPSecureBookmarks) + prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) + + // i dont think this will work ... + _ = secureBookMark.theUrl.startAccessingSecurityScopedResource() return true } catch { - os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) - Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(url.absoluteString). Error: \(error.localizedDescription)") +// os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) +// Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(url.absoluteString). Error: \(error.localizedDescription)") return false } } @@ -212,13 +259,15 @@ handle bookmarkDataIsStale let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) + + if url.startAccessingSecurityScopedResource() { for (index, bookmarkDict) in bookmarks.enumerated(){ print("Found \(bookmarkDict) at position \(index)") - if bookmarkDict[url.absoluteString] != nil { + if bookmarkDict[url.absoluteString] != nil { os_log("beenHereBefore", log: log, type: .debug) return true } @@ -228,7 +277,20 @@ handle bookmarkDataIsStale let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - bookmarks.append([url.absoluteString : bookmarkData]) + let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: url) + +// UserDefaults.standard.set(sp.encode(), forKey: "sp") + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(sp) + print(String(data: data, encoding: .utf8)!) + + +// let spData = sp.encode() + bookmarks.append([url.absoluteString : data]) +// bookmarks.append([url.absoluteString : bookmarkData]) + bookmarkOptions.append([url.absoluteString : options]) prefs.set(bookmarks, forKey: SPSecureBookmarks) prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index db244ee30..05212ec74 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -29,6 +29,8 @@ // More info at #import "SPFilePreferencePane.h" +#import "sequel-ace-Swift.h" + @import Firebase; @interface SPFilePreferencePane () @@ -56,6 +58,8 @@ - (instancetype)init - (void)dealloc { + SPLog(@"dealloc"); + for(NSURL *url in resolvedBookmarks){ [url stopAccessingSecurityScopedResource]; } @@ -250,52 +254,60 @@ - (void) chooseFile return; } + SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; + // since ssh configs are able to consist of multiple files, bookmarks // for every selected file should be created in order to access them // read-only. [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ // check if the file is out of the sandbox - if ([self->_currentFilePanel.URL startAccessingSecurityScopedResource] == YES) { - NSLog(@"got access to: %@", url.absoluteString); - - BOOL __block beenHereBefore = NO; - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - // check, if a bookmark already exists - if (dict[url.absoluteString] != nil) { - beenHereBefore = YES; - *stop = YES; - } - }]; - - // if no bookmark exist, create on - if (beenHereBefore == NO) { - NSError *error = nil; - - NSData *tmpAppScopedBookmark = [url - bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope - | - NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - - // save the bookmark to the preferences in order to access - // them later in the SPConnectionController - if (tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{url.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - else{ - SPLog(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); - CLS_LOG(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); - } - } - } - else{ - SPLog(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); - CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); - } +// if ([self->_currentFilePanel.URL startAccessingSecurityScopedResource] == YES) { +// SPLog(@"got access to: %@", self->_currentFilePanel.URL.absoluteString); +// CLS_LOG(@"got access to: %@", self->_currentFilePanel.URL.absoluteString); +// +// BOOL __block beenHereBefore = NO; +// +// [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { +// // check, if a bookmark already exists +// if (dict[url.absoluteString] != nil) { +// beenHereBefore = YES; +// *stop = YES; +// } +// }]; +// +// // if no bookmark exist, create on +// if (beenHereBefore == NO) { +// NSError *error = nil; +// +// NSData *tmpAppScopedBookmark = [self->_currentFilePanel.URL +// bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope +// | +// NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) +// includingResourceValuesForKeys:nil +// relativeToURL:nil +// error:&error]; +// +// // save the bookmark to the preferences in order to access +// // them later in the SPConnectionController +// if (tmpAppScopedBookmark && !error) { +// [self->bookmarks addObject:@{url.absoluteString : tmpAppScopedBookmark}]; +// [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; +// } +// else{ +// SPLog(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); +// CLS_LOG(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); +// } +// } +// } +// else{ +// SPLog(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); +// CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); +// } + + if([sharedSecureBookmarkManager addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookMarkForUrl success"); + } + }]; [self loadBookmarks]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index be2bcc9f7..69a0fdcd5 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -73,6 +73,8 @@ - (instancetype)init - (void)dealloc { + SPLog(@"dealloc"); + for(NSURL *url in resolvedBookmarks){ [url stopAccessingSecurityScopedResource]; } @@ -337,8 +339,9 @@ - (void) chooseSSHConfig [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ // check if the file is out of the sandbox if ([self->_currentFilePanel.URL startAccessingSecurityScopedResource] == YES) { - NSLog(@"got access to: %@", url.absoluteString); - + SPLog(@"got access to: %@", self->_currentFilePanel.URL); + CLS_LOG(@"got access to: %@", self->_currentFilePanel.URL); + BOOL __block beenHereBefore = NO; [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { @@ -353,7 +356,7 @@ - (void) chooseSSHConfig if (beenHereBefore == NO) { NSError *error = nil; - NSData *tmpAppScopedBookmark = [url + NSData *tmpAppScopedBookmark = [self->_currentFilePanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) diff --git a/sequel-ace.xcodeproj/project.pbxproj b/sequel-ace.xcodeproj/project.pbxproj index 8fc58471e..aca735c58 100644 --- a/sequel-ace.xcodeproj/project.pbxproj +++ b/sequel-ace.xcodeproj/project.pbxproj @@ -133,6 +133,8 @@ 1A9498C125517191000BC793 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A94988D25516057000BC793 /* DateExtension.swift */; }; 1A9498DE2551776D000BC793 /* DateFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1EE9492551185D0056FECD /* DateFormatterExtension.swift */; }; 1A94997A25518549000BC793 /* DateComponentsFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9D83A325514E740024B563 /* DateComponentsFormatterExtension.swift */; }; + 1A96E4B62588F34C0055F5F5 /* SecureBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A96E4B42588F34C0055F5F5 /* SecureBookmark.swift */; }; + 1A96E4B72588F34C0055F5F5 /* SecureBookmarkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A96E4B52588F34C0055F5F5 /* SecureBookmarkManager.swift */; }; 1A9D83A425514E740024B563 /* DateComponentsFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9D83A325514E740024B563 /* DateComponentsFormatterExtension.swift */; }; 1A9EB9AE25651F5000FE60FF /* SQLiteHistoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9EB9AD25651F5000FE60FF /* SQLiteHistoryManager.swift */; }; 1A9F343F257B0DBE0062EC87 /* SPBundleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A9F343E257B0DBE0062EC87 /* SPBundleManager.m */; }; @@ -755,6 +757,8 @@ 1A8B53562584520800526DED /* SPURLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPURLAdditions.m; sourceTree = ""; }; 1A8B53672584552F00526DED /* SPURLAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPURLAdditionsTests.m; sourceTree = ""; }; 1A94988D25516057000BC793 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; + 1A96E4B42588F34C0055F5F5 /* SecureBookmark.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureBookmark.swift; sourceTree = ""; }; + 1A96E4B52588F34C0055F5F5 /* SecureBookmarkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureBookmarkManager.swift; sourceTree = ""; }; 1A9D83A325514E740024B563 /* DateComponentsFormatterExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateComponentsFormatterExtension.swift; sourceTree = ""; }; 1A9EB9AD25651F5000FE60FF /* SQLiteHistoryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLiteHistoryManager.swift; sourceTree = ""; }; 1A9F343E257B0DBE0062EC87 /* SPBundleManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SPBundleManager.m; sourceTree = ""; }; @@ -1533,6 +1537,8 @@ 173E70D5107FF7AF008733C9 /* Other */ = { isa = PBXGroup; children = ( + 1A96E4B42588F34C0055F5F5 /* SecureBookmark.swift */, + 1A96E4B52588F34C0055F5F5 /* SecureBookmarkManager.swift */, 589235311020C1230011DE00 /* SPHistoryController.h */, 589235301020C1230011DE00 /* SPHistoryController.m */, 5806B76211A991EC00813A88 /* SPDocumentController.h */, @@ -3136,6 +3142,7 @@ BC8C8532100E0A8000D7A129 /* SPTableView.m in Sources */, BC9F0881100FCF2C00A80D32 /* SPFieldEditorController.m in Sources */, 58D2E229101222670063EF1D /* SPTextAndLinkCell.m in Sources */, + 1A96E4B72588F34C0055F5F5 /* SecureBookmarkManager.swift in Sources */, BC05F1C5101241DF008A97F8 /* YRKSpinningProgressIndicator.m in Sources */, 967D875B24B67B4300BAE934 /* SPAutosizingTextView.swift in Sources */, 503B02CA1AE82C5E0060CAB1 /* SPTableFilterParser.m in Sources */, @@ -3212,6 +3219,7 @@ 1785EB6A127DD79300F468C8 /* SPEditorPreferencePane.m in Sources */, 17FDB04C1280778B00DBBBC2 /* SPFontPreviewTextField.m in Sources */, 17D3C22212859E070047709F /* SPFavoriteNode.m in Sources */, + 1A96E4B62588F34C0055F5F5 /* SecureBookmark.swift in Sources */, 506CE9311A311C6C0039F736 /* SPRuleFilterController.m in Sources */, 17D3C66E128AD4710047709F /* SPFavoritesController.m in Sources */, 17D3C671128AD8160047709F /* SPSingleton.m in Sources */, From 4c0776a25b185ca1d72eef87ebe2747a8d0915b5 Mon Sep 17 00:00:00 2001 From: James Stout Date: Wed, 16 Dec 2020 19:30:02 +0800 Subject: [PATCH 07/37] =?UTF-8?q?WIP=20-=20doesn=E2=80=99t=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/Controllers/Other/SecureBookmark.swift | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index a9da20d93..f06354db3 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -16,27 +16,47 @@ struct SecureBookmark: Codable { } -//extension SecureBookmark { -// func encode() -> Data { -// let data = NSMutableData() -// let archiver = NSKeyedArchiver(forWritingWith: data) -// archiver.encode(bookmarkData, forKey: "bookmarkData") -// archiver.encode(options, forKey: "options") -// archiver.encode(theUrl, forKey: "theUrl") -// archiver.finishEncoding() -// return data as Data -// } -// -// init?(data: Data) { -// let unarchiver = NSKeyedUnarchiver(forReadingWith: data) -// defer { -// unarchiver.finishDecoding() -// } -// guard let bookmarkData = unarchiver.decodeObject(forKey: "bookmarkData") as? Data else { return nil } -// guard let theUrl = unarchiver.decodeObject(forKey: "theUrl") as? URL else { return nil } -// options = unarchiver.decodeDouble(forKey: "options") -// self.bookmarkData = bookmarkData -// self.theUrl = theUrl -// } -//} +extension SecureBookmark { + func encode() -> Data { + let data = NSMutableData() + let archiver = NSKeyedArchiver(forWritingWith: data) + archiver.encode(NSKeyedArchiver.archivedData(withRootObject: bookmarkData), forKey: "bookmarkData") + archiver.encode(NSKeyedArchiver.archivedData(withRootObject: options), forKey: "options") + archiver.encode(NSKeyedArchiver.archivedData(withRootObject: theUrl), forKey: "theUrl") + archiver.finishEncoding() + return data as Data + } + + init?(data: Data) { + let unarchiver = NSKeyedUnarchiver(forReadingWith: data) + defer { + unarchiver.finishDecoding() + } + guard + let bookmarkData = unarchiver.decodeObject(forKey: "bookmarkData") as? Data, + let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data + + else { + return nil + } + guard + let theUrl = unarchiver.decodeObject(forKey: "theUrl") as? Data, + let unarchivedURL = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(theUrl) as? URL + + else { + return nil + } + guard + let options = unarchiver.decodeObject(forKey: "options") as? Data, + let unarchivedOptions = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(options) as? Double + + else { + return nil + } + + self.bookmarkData = unarchivedBookmarkData + self.theUrl = unarchivedURL + self.options = unarchivedOptions + } +} From 5873a58912d6be5fb94ee3ba09c15c474914ee8e Mon Sep 17 00:00:00 2001 From: James Stout Date: Wed, 16 Dec 2020 19:30:36 +0800 Subject: [PATCH 08/37] guard around substringFromIndex --- .../Preferences/Panes/SPFilePreferencePane.m | 9 +++++---- .../Preferences/Panes/SPNetworkPreferencePane.m | 12 +++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 05212ec74..a631515e9 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -124,10 +124,11 @@ - (void)loadBookmarks } // remove the file protocol - NSString *fileName = [key substringFromIndex:[@"file://" length]]; - - // save the filename without the file protocol - [fileNames addObject:fileName]; + if([key hasPrefixWithPrefix:@"file://" caseSensitive:YES] == YES){ + NSString *fileName = [key substringFromIndex:[@"file://" length]]; + // save the filename without the file protocol + [fileNames addObject:fileName]; + } } }]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 69a0fdcd5..1189b955b 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -266,11 +266,13 @@ - (void)updateSSHConfigPopUp // every bookmark is saved in relation to it's abslute path while (key = [keyEnumerator nextObject]) { - NSString *itemTitle = [key substringFromIndex:[@"file://" length]]; - - [sshConfigChooser addItemWithTitle:itemTitle]; - - count++; + + if([key hasPrefixWithPrefix:@"file://" caseSensitive:YES] == YES){ + // save the filename without the file protocol + NSString *itemTitle = [key substringFromIndex:[@"file://" length]]; + [sshConfigChooser addItemWithTitle:itemTitle]; + count++; + } } }]; From 04b4acfb4af016f1a409f2095311b978dc0756e1 Mon Sep 17 00:00:00 2001 From: James Stout Date: Wed, 16 Dec 2020 22:26:23 +0800 Subject: [PATCH 09/37] WIP --- Source/Controllers/Other/SecureBookmark.swift | 62 ++++--------- .../Other/SecureBookmarkData.swift | 58 ++++++++++++ .../Other/SecureBookmarkManager.swift | 89 +++++++++++-------- .../Preferences/Panes/SPFilePreferencePane.m | 2 +- .../Extensions/UserDefaultsExtension.swift | 67 ++++++++++++++ sequel-ace.xcodeproj/project.pbxproj | 4 + 6 files changed, 195 insertions(+), 87 deletions(-) create mode 100644 Source/Controllers/Other/SecureBookmarkData.swift diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index f06354db3..f070b1b86 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -9,54 +9,22 @@ import Foundation -struct SecureBookmark: Codable { - let bookmarkData: Data - let options: Double - let theUrl: URL -} - - -extension SecureBookmark { - func encode() -> Data { - let data = NSMutableData() - let archiver = NSKeyedArchiver(forWritingWith: data) - archiver.encode(NSKeyedArchiver.archivedData(withRootObject: bookmarkData), forKey: "bookmarkData") - archiver.encode(NSKeyedArchiver.archivedData(withRootObject: options), forKey: "options") - archiver.encode(NSKeyedArchiver.archivedData(withRootObject: theUrl), forKey: "theUrl") - archiver.finishEncoding() - return data as Data +class SecureBookmark: NSObject { + var _data: SecureBookmarkData + + init(data: Data, options: Double, url: URL) { + _data = SecureBookmarkData(data: data, options: options, url: url) + super.init() } - - init?(data: Data) { - let unarchiver = NSKeyedUnarchiver(forReadingWith: data) - defer { - unarchiver.finishDecoding() - } - guard - let bookmarkData = unarchiver.decodeObject(forKey: "bookmarkData") as? Data, - let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data - - else { - return nil - } - guard - let theUrl = unarchiver.decodeObject(forKey: "theUrl") as? Data, - let unarchivedURL = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(theUrl) as? URL - - else { - return nil - } - guard - let options = unarchiver.decodeObject(forKey: "options") as? Data, - let unarchivedOptions = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(options) as? Double - - else { - return nil - } - - self.bookmarkData = unarchivedBookmarkData - self.theUrl = unarchivedURL - self.options = unarchivedOptions + + public func getEncodedData() -> Data{ + let codedData = NSKeyedArchiver.archivedData(withRootObject: _data) + return codedData + } + + public class func getDecodedData(encodedData: Data) -> SecureBookmarkData { + return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as! SecureBookmarkData } } + diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift new file mode 100644 index 000000000..8309373d7 --- /dev/null +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -0,0 +1,58 @@ +// +// SecureBookmark.swift +// Sequel Ace +// +// Created by James on 7/12/2020. +// Copyright © 2020 Sequel-Ace. All rights reserved. +// + +import Foundation + +class SecureBookmarkData: NSObject, NSCoding, NSSecureCoding { + + var bookmarkData: Data + var options: Double + var theUrl: URL + + static var supportsSecureCoding: Bool { + return true + } + + init(data: Data, options: Double, url: URL ) { + self.bookmarkData = data + self.options = options + self.theUrl = url + super.init() + } + + // MARK: NSCoding Implementation + enum Keys: String { + case bookmarkData = "BookmarkData" + case options = "Options" + case theUrl = "TheUrl" + } + + func encode(with coder: NSCoder) { + coder.encode(bookmarkData, forKey: Keys.bookmarkData.rawValue) + coder.encode(options, forKey: Keys.options.rawValue) + coder.encode(theUrl, forKey: Keys.theUrl.rawValue) + + // For NSSecureCoding + // coder.encode(bookmarkData as NSData, forKey: Keys.bookmarkData.rawValue) + // coder.encode(NSNumber(value: options), forKey: Keys.options.rawValue) + // coder.encode(theUrl as NSURL, forKey: Keys.theUrl.rawValue) + + } + + required convenience init?(coder: NSCoder) { + let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data + let options = coder.decodeDouble(forKey: Keys.options.rawValue) + let theUrl = coder.decodeObject(forKey: Keys.theUrl.rawValue) as! URL + self.init(data: bookmarkData, options: options, url: theUrl) + } + + +} + + + diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 4ff423f70..bf135b3cf 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -28,7 +28,7 @@ handle bookmarkDataIsStale @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] private var createdBookmarkOptions: Bool - + private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard @@ -48,12 +48,12 @@ handle bookmarkDataIsStale print(bookmarks.count) - if(bookmarks.count < 2){ - os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) - Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") - bookmarks.removeAll() - return - } +// if(bookmarks.count < 2){ +// os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) +// Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") +// bookmarks.removeAll() +// return +// } os_log("bookmarks = %@", log: log, type: .info, bookmarks) @@ -103,6 +103,18 @@ handle bookmarkDataIsStale /// reRequestSecureAccessToBookmarks @objc public func reRequestSecureAccessToBookmarks() { +// let bmData = UserDefaults.getBookmarkData(key: "file:///Users/james/.ssh/known_hosts") +// +// var bookmarkDataIsStale = false +// +// do { +// let urlForBookmark = try URL(resolvingBookmarkData: bmData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(6114))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) +// } +// catch{ +// os_log("resolvingBookmarkData failed. Error: %2", log: log, type: .error, error.localizedDescription) +// +// } + //re-read ? bookmarkOptions = prefs.array(forKey: SPSecureBookmarkOptions) as? [[String: UInt]] ?? [["": 0]] @@ -140,14 +152,17 @@ handle bookmarkDataIsStale // // UserDefaults.standard.set(sp.encode(), forKey: "sp") - let decoder = JSONDecoder() - let sp = try decoder.decode(SecureBookmark.self, from: urlData) - - print(sp.theUrl) +// let decoder = JSONDecoder() +// let sp = try decoder.decode(SecureBookmark.self, from: urlData) +// +// print(sp.theUrl) + + let spData = SecureBookmark.getDecodedData(encodedData: urlData) + +// let bookmarkResolutionOptions : NSURL.BookmarkResolutionOptions = NSURL.BookmarkResolutionOptions.init(rawValue: UInt(1024)) -// let sp = SecureBookmark(data: urlData) - let urlForBookmark = try URL(resolvingBookmarkData: sp.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(sp.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // a bookmark might be "stale" because the app hasn't been used // in many months, macOS has been upgraded, the app was @@ -155,17 +170,17 @@ handle bookmarkDataIsStale if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(sp.options)) + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(spData.options)) - let res = sp.theUrl.startAccessingSecurityScopedResource() + let res = spData.theUrl.startAccessingSecurityScopedResource() print(res) - let bookmarkData = try sp.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + let bookmarkData = try spData.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: sp.theUrl) + let sp = SecureBookmarkData(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: spData.theUrl) - if regenerateBookmarkFor(secureBookMark: sp ) == true { + if regenerateBookmarkFor(secureBookMarkData: sp ) == true { os_log("Stale bookmark regenerated: key = %@", log: log, type: .error, key) bookmarks.remove(at: index) // } @@ -212,34 +227,30 @@ handle bookmarkDataIsStale /// - Parameters: /// - url: file URL to generate secure bookmark for /// - Returns: Bool on success or fail - private func regenerateBookmarkFor(secureBookMark: SecureBookmark) -> Bool { + private func regenerateBookmarkFor(secureBookMarkData: SecureBookmarkData) -> Bool { os_log("regenerateBookmarkFor", log: log, type: .debug) - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(secureBookMark.options)) + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(secureBookMarkData.options)) // let urlForBookmark = try URL(resolvingBookmarkData: sp!.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(sp!.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) do { - let bookmarkData = try secureBookMark.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - - let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: secureBookMark.theUrl) + let bookmarkData = try secureBookMarkData.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - let data = try encoder.encode(sp) - print(String(data: data, encoding: .utf8)!) + let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: secureBookMarkData.theUrl) + let spd = sp.getEncodedData() // let spData = sp.encode() - bookmarks.append([secureBookMark.theUrl.absoluteString : data]) + bookmarks.append([secureBookMarkData.theUrl.absoluteString : spd]) // bookmarks.append([url.absoluteString : bookmarkData]) - bookmarkOptions.append([secureBookMark.theUrl.absoluteString : bookmarkCreationOptions.rawValue]) + bookmarkOptions.append([secureBookMarkData.theUrl.absoluteString : bookmarkCreationOptions.rawValue]) prefs.set(bookmarks, forKey: SPSecureBookmarks) prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) // i dont think this will work ... - _ = secureBookMark.theUrl.startAccessingSecurityScopedResource() + _ = secureBookMarkData.theUrl.startAccessingSecurityScopedResource() return true @@ -259,8 +270,6 @@ handle bookmarkDataIsStale let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) - - if url.startAccessingSecurityScopedResource() { for (index, bookmarkDict) in bookmarks.enumerated(){ @@ -277,23 +286,25 @@ handle bookmarkDataIsStale let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - let sp = SecureBookmark(bookmarkData: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), theUrl: url) + let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: url) // UserDefaults.standard.set(sp.encode(), forKey: "sp") - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - let data = try encoder.encode(sp) - print(String(data: data, encoding: .utf8)!) +// let encoder = JSONEncoder() +// encoder.outputFormatting = .prettyPrinted +// let data = try encoder.encode(sp) +// print(String(data: data, encoding: .utf8)!) -// let spData = sp.encode() - bookmarks.append([url.absoluteString : data]) + let spData = sp.getEncodedData() + bookmarks.append([url.absoluteString : spData]) // bookmarks.append([url.absoluteString : bookmarkData]) bookmarkOptions.append([url.absoluteString : options]) prefs.set(bookmarks, forKey: SPSecureBookmarks) prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) + + UserDefaults .saveBookmarkData(bookmarkData, key: url.absoluteString) return true diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index a631515e9..886826609 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -108,7 +108,7 @@ - (void)loadBookmarks } // we need to re-request access to places we've been before.. - [self reRequestSecureAccess]; +// [self reRequestSecureAccess]; // remove all saved filenames for the list view [fileNames removeAllObjects]; diff --git a/Source/Other/Extensions/UserDefaultsExtension.swift b/Source/Other/Extensions/UserDefaultsExtension.swift index 67fe8bc7e..c59939824 100644 --- a/Source/Other/Extensions/UserDefaultsExtension.swift +++ b/Source/Other/Extensions/UserDefaultsExtension.swift @@ -28,4 +28,71 @@ extension UserDefaults { } return savedFont } + + @objc static func saveBookmarkData(_ bookmarkData: Data, key: String) { + let defaults = UserDefaults.standard + defaults.set(NSKeyedArchiver.archivedData(withRootObject: bookmarkData), forKey: key) + } + + @objc static func getBookmarkData(key: String) -> Data { + let defaults = UserDefaults.standard + guard + let bookmarkData = defaults.data(forKey: key), + let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data + + else { + return Data() + } + return unarchivedBookmarkData + } + + @objc static func saveBookmarks(_ bookmarks: [Dictionary]) { + let defaults = UserDefaults.standard + + var bmCopy = bookmarks + bmCopy.removeAll() + for (_, bookmarkDict) in bookmarks.enumerated(){ + for (key, bookmarkData) in bookmarkDict { + let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) + let newDict = [key : encData] + bmCopy.append(newDict) + } + } + defaults.set(bmCopy, forKey: SPSecureBookmarks) + + } + + @objc static func getBookmarks() -> NSArray { + let defaults = UserDefaults.standard + + let bookmarks = defaults.object(forKey: SPSecureBookmarks) as! Array> + + var bmCopy : [Dictionary] = [] + + for (_, bookmarkDict) in bookmarks.enumerated(){ + for (key, bookmarkData) in bookmarkDict { + guard + let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data, + let newDict = [key : unarchivedBookmarkData], + bmCopy.append(newDict) + else { + return Data() + } + } + } + + +// var bmCopy = bookmarks +// bmCopy.removeAll() +// for (_, bookmarkDict) in bookmarks.enumerated(){ +// +// for (key, bookmarkData) in bookmarkDict { +// let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) +// let newDict = [key : encData] +// bmCopy.append(newDict) +// } +// } +// defaults.set(bmCopy, forKey: SPSecureBookmarks) + + } } diff --git a/sequel-ace.xcodeproj/project.pbxproj b/sequel-ace.xcodeproj/project.pbxproj index 870f021cb..4c6882e70 100644 --- a/sequel-ace.xcodeproj/project.pbxproj +++ b/sequel-ace.xcodeproj/project.pbxproj @@ -117,6 +117,7 @@ 1A19962A257A624200F5B0F1 /* BundleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A199629257A624200F5B0F1 /* BundleExtension.swift */; }; 1A1EE94A2551185D0056FECD /* DateFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1EE9492551185D0056FECD /* DateFormatterExtension.swift */; }; 1A1EE9582551249C0056FECD /* NumberFormatterExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1EE9572551249C0056FECD /* NumberFormatterExtension.swift */; }; + 1A24B627258A2E9A00541E88 /* SecureBookmarkData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A24B626258A2E9A00541E88 /* SecureBookmarkData.swift */; }; 1A2711CF2539D9B10066ED58 /* SPReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A2711CE2539D9B10066ED58 /* SPReachability.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 1A2711E82539E2BB0066ED58 /* local-connection.html in Resources */ = {isa = PBXBuildFile; fileRef = 1A2711E72539E2BB0066ED58 /* local-connection.html */; }; 1A3FA7962495FC2D00B7291A /* SPMainThreadTrampoline.m in Sources */ = {isa = PBXBuildFile; fileRef = 589582141154F8F400EDCC28 /* SPMainThreadTrampoline.m */; }; @@ -744,6 +745,7 @@ 1A1EE9492551185D0056FECD /* DateFormatterExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatterExtension.swift; sourceTree = ""; }; 1A1EE9572551249C0056FECD /* NumberFormatterExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatterExtension.swift; sourceTree = ""; }; 1A1EE986255131560056FECD /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; + 1A24B626258A2E9A00541E88 /* SecureBookmarkData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureBookmarkData.swift; sourceTree = ""; }; 1A2711CD2539D9B10066ED58 /* SPReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPReachability.h; sourceTree = ""; }; 1A2711CE2539D9B10066ED58 /* SPReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPReachability.m; sourceTree = ""; }; 1A2711E72539E2BB0066ED58 /* local-connection.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "local-connection.html"; sourceTree = ""; }; @@ -1532,6 +1534,7 @@ isa = PBXGroup; children = ( 1A96E4B42588F34C0055F5F5 /* SecureBookmark.swift */, + 1A24B626258A2E9A00541E88 /* SecureBookmarkData.swift */, 1A96E4B52588F34C0055F5F5 /* SecureBookmarkManager.swift */, 589235311020C1230011DE00 /* SPHistoryController.h */, 589235301020C1230011DE00 /* SPHistoryController.m */, @@ -3093,6 +3096,7 @@ 58C56EF50F438E120035701E /* SPDataCellFormatter.m in Sources */, 1A2711CF2539D9B10066ED58 /* SPReachability.m in Sources */, 172A65110F7BED7A001E861A /* SPConsoleMessage.m in Sources */, + 1A24B627258A2E9A00541E88 /* SecureBookmarkData.swift in Sources */, 179F15060F7C433C00579954 /* SPEditorTokens.l in Sources */, B5E92F1C0F75B2E800012500 /* SPExportController.m in Sources */, B57747D40F7A8974003B34F9 /* SPPreferenceController.m in Sources */, From 2cd3a202b17a592101f834b194bde4a5dfcb09ac Mon Sep 17 00:00:00 2001 From: James Stout Date: Thu, 17 Dec 2020 22:33:03 +0800 Subject: [PATCH 10/37] WIP --- Source/Controllers/Other/SecureBookmark.swift | 18 +- .../Other/SecureBookmarkData.swift | 44 ++-- .../Other/SecureBookmarkManager.swift | 190 ++---------------- .../Preferences/Panes/SPFilePreferencePane.h | 1 - .../Preferences/Panes/SPFilePreferencePane.m | 89 +------- .../Panes/SPNetworkPreferencePane.h | 1 - .../Panes/SPNetworkPreferencePane.m | 91 +-------- Source/Other/Data/SPConstants.h | 1 - Source/Other/Data/SPConstants.m | 2 - .../Extensions/UserDefaultsExtension.swift | 58 +++--- 10 files changed, 105 insertions(+), 390 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index f070b1b86..f5df85803 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -18,12 +18,24 @@ class SecureBookmark: NSObject { } public func getEncodedData() -> Data{ - let codedData = NSKeyedArchiver.archivedData(withRootObject: _data) - return codedData + if #available(OSX 10.13, *) { + let codedData = try! NSKeyedArchiver.archivedData(withRootObject: _data, requiringSecureCoding: true) + return codedData + } else { + // Fallback on earlier versions + let codedData = NSKeyedArchiver.archivedData(withRootObject: _data) + return codedData + } } public class func getDecodedData(encodedData: Data) -> SecureBookmarkData { - return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as! SecureBookmarkData + + if #available(OSX 10.13, *) { + return try! NSKeyedUnarchiver.unarchivedObject(ofClass: SecureBookmarkData.self, from: encodedData)! + } else { + // Fallback on earlier versions + return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as! SecureBookmarkData + } } } diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index 8309373d7..0060e4e41 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -33,26 +33,36 @@ class SecureBookmarkData: NSObject, NSCoding, NSSecureCoding { } func encode(with coder: NSCoder) { - coder.encode(bookmarkData, forKey: Keys.bookmarkData.rawValue) - coder.encode(options, forKey: Keys.options.rawValue) - coder.encode(theUrl, forKey: Keys.theUrl.rawValue) - - // For NSSecureCoding - // coder.encode(bookmarkData as NSData, forKey: Keys.bookmarkData.rawValue) - // coder.encode(NSNumber(value: options), forKey: Keys.options.rawValue) - // coder.encode(theUrl as NSURL, forKey: Keys.theUrl.rawValue) + if #available(OSX 10.13, *) { + //For NSSecureCoding + coder.encode(bookmarkData as NSData, forKey: Keys.bookmarkData.rawValue) + coder.encode(NSNumber(value: options), forKey: Keys.options.rawValue) + coder.encode(theUrl as NSURL, forKey: Keys.theUrl.rawValue) + } + else { + // For NSCoding + coder.encode(bookmarkData, forKey: Keys.bookmarkData.rawValue) + coder.encode(options, forKey: Keys.options.rawValue) + coder.encode(theUrl, forKey: Keys.theUrl.rawValue) + } } required convenience init?(coder: NSCoder) { - let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data - let options = coder.decodeDouble(forKey: Keys.options.rawValue) - let theUrl = coder.decodeObject(forKey: Keys.theUrl.rawValue) as! URL - self.init(data: bookmarkData, options: options, url: theUrl) - } - - -} - + if #available(OSX 10.13, *) { + let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue)! as Data + let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue)! as! Double + let theUrl = coder.decodeObject(of: NSURL.self, forKey: Keys.theUrl.rawValue)! as URL + self.init(data: bookmarkData, options: options, url: theUrl) + } + else{ + // For NSCoding + let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data + let options = coder.decodeDouble(forKey: Keys.options.rawValue) + let theUrl = coder.decodeObject(forKey: Keys.theUrl.rawValue) as! URL + self.init(data: bookmarkData, options: options, url: theUrl) + } + } +} diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index bf135b3cf..f7bf249c4 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -13,7 +13,6 @@ import Firebase /* reRequestSecureAccess -revokeBookmark addBookmark handle bookmarkDataIsStale @@ -22,12 +21,9 @@ handle bookmarkDataIsStale @objc final class SecureBookmarkManager: NSObject { @objc static let sharedInstance = SecureBookmarkManager() - @objc public var bookmarks: [Dictionary] = [] - private var bookmarkOptions: [Dictionary] = [] @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] - private var createdBookmarkOptions: Bool private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard @@ -35,16 +31,10 @@ handle bookmarkDataIsStale override private init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") - createdBookmarkOptions = prefs.bool(forKey: SPCreatedBookmarksOptions) super.init() - if createdBookmarkOptions == false { - createBookmarkOptions() - } - // error handle? bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] - bookmarkOptions = prefs.array(forKey: SPSecureBookmarkOptions) as? [[String: UInt]] ?? [["": 0]] print(bookmarks.count) @@ -61,65 +51,20 @@ handle bookmarkDataIsStale // i think part of init of this manager should be to re-request access reRequestSecureAccessToBookmarks() -// os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) -// os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) - + os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) + os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) } - private func createBookmarkOptions() { - - // error handle? - bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] - - print(bookmarks.count) - - if(bookmarks.count == 1){ - os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) - Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") - bookmarks.removeAll() - return - } - - os_log("bookmarks = %@", log: log, type: .info, bookmarks) - - for (index, bookmarkDict) in bookmarks.enumerated(){ - - os_log("Found %@ at position %i", log: log, type: .info, bookmarkDict, index) - - for (key, _) in bookmarkDict { - let opts = URL.BookmarkResolutionOptions.withSecurityScope.rawValue // default to this as we don't know what they were created with - bookmarkOptions.append([key : opts]) - } - - } - - os_log("createdBookmarkOptions for existing bookmarks", log: log, type: .info) - createdBookmarkOptions = true - prefs.set(true, forKey: SPCreatedBookmarksOptions) - prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) - } /// reRequestSecureAccessToBookmarks @objc public func reRequestSecureAccessToBookmarks() { -// let bmData = UserDefaults.getBookmarkData(key: "file:///Users/james/.ssh/known_hosts") -// -// var bookmarkDataIsStale = false -// -// do { -// let urlForBookmark = try URL(resolvingBookmarkData: bmData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(6114))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) -// } -// catch{ -// os_log("resolvingBookmarkData failed. Error: %2", log: log, type: .error, error.localizedDescription) -// -// } - - //re-read ? - bookmarkOptions = prefs.array(forKey: SPSecureBookmarkOptions) as? [[String: UInt]] ?? [["": 0]] - let bmCopy = bookmarks + // start afresh + bookmarks.removeAll() + for (index, bookmarkDict) in bmCopy.enumerated(){ os_log("Found %@ at position %i", log: log, type: .info, bookmarkDict, index) @@ -129,73 +74,35 @@ handle bookmarkDataIsStale os_log("JIMMY key = %@", log: log, type: .info, key) do { - - var bookmarkOptionsForKey : UInt = 1024 - - for (_, bookmarkOptionsDict) in bookmarkOptions.enumerated(){ - for (optKey, bmOptions) in bookmarkOptionsDict { - - os_log("JIMMY key = %@", log: log, type: .info, key) - os_log("JIMMY optKey = %@", log: log, type: .info, optKey) - - if key == optKey { - bookmarkOptionsForKey = bmOptions - os_log("JIMMY bookmarkOptionsForKey = %i", log: log, type: .info, bookmarkOptionsForKey) - break - } - } - } - var bookmarkDataIsStale = false -// let sp = SecureBookmark(bookmarkData: urlData, options: Double(URL.BookmarkResolutionOptions(rawValue: bookmarkOptionsForKey).rawValue)) -// -// UserDefaults.standard.set(sp.encode(), forKey: "sp") - -// let decoder = JSONDecoder() -// let sp = try decoder.decode(SecureBookmark.self, from: urlData) -// -// print(sp.theUrl) - let spData = SecureBookmark.getDecodedData(encodedData: urlData) - -// let bookmarkResolutionOptions : NSURL.BookmarkResolutionOptions = NSURL.BookmarkResolutionOptions.init(rawValue: UInt(1024)) - + // always resolve with just _NSURLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) +// bookmarkDataIsStale = true // a bookmark might be "stale" because the app hasn't been used // in many months, macOS has been upgraded, the app was // re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) - - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(spData.options)) - - let res = spData.theUrl.startAccessingSecurityScopedResource() - - print(res) - - let bookmarkData = try spData.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - - let sp = SecureBookmarkData(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: spData.theUrl) - - if regenerateBookmarkFor(secureBookMarkData: sp ) == true { - os_log("Stale bookmark regenerated: key = %@", log: log, type: .error, key) - bookmarks.remove(at: index) // - } - else{ - os_log("Regen failed! Stale bookmark added: key = %@", log: log, type: .error, key) - staleBookmarks.append(URL(fileURLWithPath: key)) - bookmarks.remove(at: index) - } - } + staleBookmarks.append(URL(fileURLWithPath: key)) + } else { os_log("Resolved bookmark: key = %@", log: log, type: .info, key) - _ = urlForBookmark.startAccessingSecurityScopedResource() - resolvedBookmarks.append(urlForBookmark) - } + let res = urlForBookmark.startAccessingSecurityScopedResource() + if res == true { + os_log("success: startAccessingSecurityScopedResource: for = %@", log: log, type: .info, key) + resolvedBookmarks.append(urlForBookmark) + bookmarks.append([urlForBookmark.absoluteString : urlData]) + } + else{ + os_log("ERROR: startAccessingSecurityScopedResource: for = %@", log: log, type: .info, key) + staleBookmarks.append(URL(fileURLWithPath: key)) + } + } } catch { staleBookmarks.append(URL(fileURLWithPath: key)) os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) @@ -204,62 +111,11 @@ handle bookmarkDataIsStale } } + // reset bookmarks prefs.set(bookmarks, forKey: SPSecureBookmarks) -// if let data = UserDefaults.standard.object(forKey: "sp") as? Data { -// let room = SecureBookmark(data: data) -//// os_log("JIMMY room = %@", log: log, type: .info, room as! CVarArg) -// -// var bookmarkDataIsStale = false -// -// do { -// let urlForBookmark = try URL(resolvingBookmarkData: room!.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(room!.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) -// -// os_log("JIMMY room = %@", log: log, type: .info, urlForBookmark as CVarArg) -// -// } -// catch{} -// } } - // MAY NOT WORK - See https://stackoverflow.com/a/25247535 - /// regenerateBookmarkFor a URL. - /// - Parameters: - /// - url: file URL to generate secure bookmark for - /// - Returns: Bool on success or fail - private func regenerateBookmarkFor(secureBookMarkData: SecureBookmarkData) -> Bool { - os_log("regenerateBookmarkFor", log: log, type: .debug) - - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: UInt(secureBookMarkData.options)) - -// let urlForBookmark = try URL(resolvingBookmarkData: sp!.bookmarkData , options: [URL.BookmarkResolutionOptions(rawValue: UInt(sp!.options))], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) - do { - - let bookmarkData = try secureBookMarkData.theUrl.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - - let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: secureBookMarkData.theUrl) - - let spd = sp.getEncodedData() - -// let spData = sp.encode() - bookmarks.append([secureBookMarkData.theUrl.absoluteString : spd]) -// bookmarks.append([url.absoluteString : bookmarkData]) - - bookmarkOptions.append([secureBookMarkData.theUrl.absoluteString : bookmarkCreationOptions.rawValue]) - prefs.set(bookmarks, forKey: SPSecureBookmarks) - prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) - - // i dont think this will work ... - _ = secureBookMarkData.theUrl.startAccessingSecurityScopedResource() - - return true - - } catch { -// os_log("Error regenerateBookmarkFor: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) -// Crashlytics.crashlytics().log("Error regenerateBookmarkFor: key = \(url.absoluteString). Error: \(error.localizedDescription)") - return false - } - } /// addBookMark /// - Parameters: @@ -300,11 +156,7 @@ handle bookmarkDataIsStale bookmarks.append([url.absoluteString : spData]) // bookmarks.append([url.absoluteString : bookmarkData]) - bookmarkOptions.append([url.absoluteString : options]) prefs.set(bookmarks, forKey: SPSecureBookmarks) - prefs.set(bookmarkOptions, forKey: SPSecureBookmarkOptions) - - UserDefaults .saveBookmarkData(bookmarkData, key: url.absoluteString) return true diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.h b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.h index edcabe3eb..190075f78 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.h +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.h @@ -47,7 +47,6 @@ } @property (readwrite, strong) NSMutableArray *> *bookmarks; -@property (readwrite, strong) NSMutableArray *resolvedBookmarks; - (IBAction) revokeBookmark:(id)sender; - (IBAction) addBookmark:(id)sender; diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 886826609..44deeec6a 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -39,7 +39,6 @@ @interface SPFilePreferencePane () @implementation SPFilePreferencePane @synthesize bookmarks; -@synthesize resolvedBookmarks; - (instancetype)init { @@ -48,7 +47,6 @@ - (instancetype)init if (self) { fileNames = [[NSMutableArray alloc] init]; bookmarks = [[NSMutableArray alloc] init]; - resolvedBookmarks = [[NSMutableArray alloc] init]; [self loadBookmarks]; } @@ -60,7 +58,7 @@ - (void)dealloc { SPLog(@"dealloc"); - for(NSURL *url in resolvedBookmarks){ + for(NSURL *url in SecureBookmarkManager.sharedInstance.resolvedBookmarks){ [url stopAccessingSecurityScopedResource]; } @@ -108,8 +106,8 @@ - (void)loadBookmarks } // we need to re-request access to places we've been before.. -// [self reRequestSecureAccess]; - + // not anymore, done at startup + // remove all saved filenames for the list view [fileNames removeAllObjects]; @@ -137,39 +135,6 @@ - (void)loadBookmarks [fileView reloadData]; } --(void)reRequestSecureAccess{ - - SPLog(@"reRequestSecureAccess to saved bookmarks"); - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { - - NSError *error = nil; - BOOL bookmarkDataIsStale; - - NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&bookmarkDataIsStale - error:&error]; - - if(!error){ - [tmpURL startAccessingSecurityScopedResource]; - [resolvedBookmarks addObject:tmpURL]; - } - else{ - SPLog(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - CLS_LOG(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - } - }]; - }]; - - SPLog(@"resolvedBookmarks - %@",resolvedBookmarks); - CLS_LOG(@"resolvedBookmarks - %@",resolvedBookmarks); - -} - #pragma mark - #pragma mark File operations @@ -255,60 +220,14 @@ - (void) chooseFile return; } - SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - // since ssh configs are able to consist of multiple files, bookmarks // for every selected file should be created in order to access them // read-only. [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ - // check if the file is out of the sandbox -// if ([self->_currentFilePanel.URL startAccessingSecurityScopedResource] == YES) { -// SPLog(@"got access to: %@", self->_currentFilePanel.URL.absoluteString); -// CLS_LOG(@"got access to: %@", self->_currentFilePanel.URL.absoluteString); -// -// BOOL __block beenHereBefore = NO; -// -// [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { -// // check, if a bookmark already exists -// if (dict[url.absoluteString] != nil) { -// beenHereBefore = YES; -// *stop = YES; -// } -// }]; -// -// // if no bookmark exist, create on -// if (beenHereBefore == NO) { -// NSError *error = nil; -// -// NSData *tmpAppScopedBookmark = [self->_currentFilePanel.URL -// bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope -// | -// NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) -// includingResourceValuesForKeys:nil -// relativeToURL:nil -// error:&error]; -// -// // save the bookmark to the preferences in order to access -// // them later in the SPConnectionController -// if (tmpAppScopedBookmark && !error) { -// [self->bookmarks addObject:@{url.absoluteString : tmpAppScopedBookmark}]; -// [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; -// } -// else{ -// SPLog(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); -// CLS_LOG(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); -// } -// } -// } -// else{ -// SPLog(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); -// CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); -// } - if([sharedSecureBookmarkManager addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ SPLog(@"addBookMarkForUrl success"); } - }]; [self loadBookmarks]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.h b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.h index 57f78d512..a3d1dbbc4 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.h +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.h @@ -52,7 +52,6 @@ } @property (readwrite, strong) NSMutableArray *> *bookmarks; -@property (readwrite, strong) NSMutableArray *resolvedBookmarks; - (IBAction)pickSSHClientViaFileBrowser:(id)sender; - (IBAction)pickSSHClient:(id)sender; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 1189b955b..87766f840 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -46,7 +46,6 @@ + (NSArray *)defaultSSLCipherList; @implementation SPNetworkPreferencePane @synthesize bookmarks; -@synthesize resolvedBookmarks; - (instancetype)init { @@ -54,7 +53,6 @@ - (instancetype)init if (self) { sslCiphers = [[NSMutableArray alloc] init]; bookmarks = [[NSMutableArray alloc] init]; - resolvedBookmarks = [[NSMutableArray alloc] init]; id o; if((o = [prefs objectForKey:SPSecureBookmarks])){ @@ -65,7 +63,6 @@ - (instancetype)init CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); } - [self reRequestSecureAccess]; } return self; @@ -75,7 +72,7 @@ - (void)dealloc { SPLog(@"dealloc"); - for(NSURL *url in resolvedBookmarks){ + for(NSURL *url in SecureBookmarkManager.sharedInstance.resolvedBookmarks){ [url stopAccessingSecurityScopedResource]; } } @@ -121,40 +118,6 @@ - (void)preferencePaneWillBeShown [sslCipherView registerForDraggedTypes:@[SPSSLCipherPboardTypeName]]; } -#pragma mark - -#pragma mark Bookmarks - --(void)reRequestSecureAccess{ - - SPLog(@"reRequestSecureAccess to saved bookmarks"); - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { - - NSError *error = nil; - BOOL bookmarkDataIsStale; - - NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&bookmarkDataIsStale - error:&error]; - - if(!error){ - [tmpURL startAccessingSecurityScopedResource]; - [resolvedBookmarks addObject:tmpURL]; - } - else{ - SPLog(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - CLS_LOG(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - } - }]; - }]; - - SPLog(@"resolvedBookmarks - %@",resolvedBookmarks); - CLS_LOG(@"resolvedBookmarks - %@",resolvedBookmarks); -} #pragma mark - #pragma mark Custom SSH client methods @@ -312,7 +275,9 @@ - (void) chooseSSHConfig } else { homeDirectory = [NSURL fileURLWithPath:NSHomeDirectory()]; } - + + SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; + _currentFilePanel = [NSOpenPanel openPanel]; [_currentFilePanel setTitle:@"Choose ssh config"]; [_currentFilePanel setCanChooseFiles:YES]; @@ -340,49 +305,11 @@ - (void) chooseSSHConfig // read-only. [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ // check if the file is out of the sandbox - if ([self->_currentFilePanel.URL startAccessingSecurityScopedResource] == YES) { - SPLog(@"got access to: %@", self->_currentFilePanel.URL); - CLS_LOG(@"got access to: %@", self->_currentFilePanel.URL); - - BOOL __block beenHereBefore = NO; - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - // check, if a bookmark already exists - if (dict[url.absoluteString] != nil) { - beenHereBefore = YES; - *stop = YES; - } - }]; - - // if no bookmark exist, create on - if (beenHereBefore == NO) { - NSError *error = nil; - - NSData *tmpAppScopedBookmark = [self->_currentFilePanel.URL - bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope - | - NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - - // save the bookmark to the preferences in order to access - // them later in the SPConnectionController - if (tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{url.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - else{ - SPLog(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); - CLS_LOG(@"Problem creating bookmark - %@ : %@",url.absoluteString, [error localizedDescription]); - } - } - } - else{ - SPLog(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); - CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",url.absoluteString); - } - + + if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookMarkForUrl success"); + } + // set the config path to the first selected file if (idxURL == 0) { // save the preferences diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 8af557c90..500a72fd1 100755 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -665,7 +665,6 @@ typedef NS_ENUM(NSInteger, SPBundleRedirectAction) { }; extern NSString *SPMigratedQueriesFromPrefs; -extern NSString *SPCreatedBookmarksOptions; extern NSString *SPTraceSQLiteExecutions; // URL scheme diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 8a3204925..9ae9eba3d 100755 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -411,8 +411,6 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPBundleVersionKey = @"bundleVersion"; const long SPBundleCurrentVersion = 2; -NSString *SPCreatedBookmarksOptions = @"CreatedBookmarksOptions"; - NSString *SPBundleFileName = @"command.plist"; NSString *SPBundleTaskInputFilePath = @"~/tmp/SP_BUNDLE_INPUT"; NSString *SPBundleTaskOutputFilePath = @"~/tmp/SP_BUNDLE_OUTPUT"; diff --git a/Source/Other/Extensions/UserDefaultsExtension.swift b/Source/Other/Extensions/UserDefaultsExtension.swift index c59939824..71bb8ea02 100644 --- a/Source/Other/Extensions/UserDefaultsExtension.swift +++ b/Source/Other/Extensions/UserDefaultsExtension.swift @@ -62,37 +62,37 @@ extension UserDefaults { } - @objc static func getBookmarks() -> NSArray { - let defaults = UserDefaults.standard - - let bookmarks = defaults.object(forKey: SPSecureBookmarks) as! Array> - - var bmCopy : [Dictionary] = [] - - for (_, bookmarkDict) in bookmarks.enumerated(){ - for (key, bookmarkData) in bookmarkDict { - guard - let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data, - let newDict = [key : unarchivedBookmarkData], - bmCopy.append(newDict) - else { - return Data() - } - } - } - - -// var bmCopy = bookmarks -// bmCopy.removeAll() -// for (_, bookmarkDict) in bookmarks.enumerated(){ +// @objc static func getBookmarks() -> NSArray { +// let defaults = UserDefaults.standard +// +// let bookmarks = defaults.object(forKey: SPSecureBookmarks) as! Array> +// +// var bmCopy : [Dictionary] = [] // +// for (_, bookmarkDict) in bookmarks.enumerated(){ // for (key, bookmarkData) in bookmarkDict { -// let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) -// let newDict = [key : encData] -// bmCopy.append(newDict) +// guard +// let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data, +// let newDict = [key : unarchivedBookmarkData], +// bmCopy.append(newDict) +// else { +// return Data() +// } // } // } -// defaults.set(bmCopy, forKey: SPSecureBookmarks) - - } +// +// +//// var bmCopy = bookmarks +//// bmCopy.removeAll() +//// for (_, bookmarkDict) in bookmarks.enumerated(){ +//// +//// for (key, bookmarkData) in bookmarkDict { +//// let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) +//// let newDict = [key : encData] +//// bmCopy.append(newDict) +//// } +//// } +//// defaults.set(bmCopy, forKey: SPSecureBookmarks) +// +// } } From 0b28ba1d83d73fc7c436b3fd2efc5a147402af49 Mon Sep 17 00:00:00 2001 From: James Stout Date: Fri, 18 Dec 2020 17:39:00 +0800 Subject: [PATCH 11/37] WIP --- .../Other/SecureBookmarkData.swift | 2 +- .../Other/SecureBookmarkManager.swift | 89 +++++++++++-------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index 0060e4e41..adf37ef96 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -1,5 +1,5 @@ // -// SecureBookmark.swift +// SecureBookmarkData.swift // Sequel Ace // // Created by James on 7/12/2020. diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index f7bf249c4..7f59d0e6d 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -18,46 +18,53 @@ handle bookmarkDataIsStale */ - @objc final class SecureBookmarkManager: NSObject { @objc static let sharedInstance = SecureBookmarkManager() @objc public var bookmarks: [Dictionary] = [] @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) + private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard override private init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") + os_log("SecureBookmarkManager init = %@", log: log, type: .info) + Crashlytics.crashlytics().log("SecureBookmarkManager init.") + super.init() - // error handle? bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] - print(bookmarks.count) - -// if(bookmarks.count < 2){ -// os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) -// Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") -// bookmarks.removeAll() -// return -// } + // the default above means there is always one entry, even if there are none in prefs + // so check for less than 2 + if(bookmarks.count < 2){ + os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) + Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") + bookmarks.removeAll() + return + } os_log("bookmarks = %@", log: log, type: .info, bookmarks) - - // i think part of init of this manager should be to re-request access + // I think part of init of this manager should be to re-request access reRequestSecureAccessToBookmarks() - os_log("resolvedBookmarks = %@", log: log, type: .info, resolvedBookmarks) - os_log("staleBookmarks = %@", log: log, type: .info, staleBookmarks) + os_log("resolvedBookmarks count = %i", log: log, type: .info, resolvedBookmarks.count) + os_log("staleBookmarks count = %i", log: log, type: .info, staleBookmarks.count) + Crashlytics.crashlytics().log("resolvedBookmarks count = \(resolvedBookmarks.count)") + Crashlytics.crashlytics().log("staleBookmarks count = \(staleBookmarks.count)") } /// reRequestSecureAccessToBookmarks + // loops through current bookmarks from prefs and re-requests secure access + // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use + // _NSURLBookmarkResolutionWithSecurityScope, not the options it was originally created with + // otherwise it will be markes as stale. @objc public func reRequestSecureAccessToBookmarks() { let bmCopy = bookmarks @@ -65,19 +72,20 @@ handle bookmarkDataIsStale // start afresh bookmarks.removeAll() - for (index, bookmarkDict) in bmCopy.enumerated(){ - - os_log("Found %@ at position %i", log: log, type: .info, bookmarkDict, index) - + for (_, bookmarkDict) in bmCopy.enumerated(){ for (key, urlData) in bookmarkDict { - os_log("JIMMY key = %@", log: log, type: .info, key) + os_log("Bookmark URL = %@", log: log, type: .info, key) do { var bookmarkDataIsStale = false + os_log("Attempting to getDecodedData for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to getDecodedData for: \(key)") let spData = SecureBookmark.getDecodedData(encodedData: urlData) + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") // always resolve with just _NSURLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) @@ -87,21 +95,24 @@ handle bookmarkDataIsStale // re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) + Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") staleBookmarks.append(URL(fileURLWithPath: key)) } else { - os_log("Resolved bookmark: key = %@", log: log, type: .info, key) + os_log("Resolved bookmark: %@", log: log, type: .info, key) + Crashlytics.crashlytics().log("Resolved bookmark: \(key)") let res = urlForBookmark.startAccessingSecurityScopedResource() if res == true { - os_log("success: startAccessingSecurityScopedResource: for = %@", log: log, type: .info, key) + os_log("success: startAccessingSecurityScopedResource for: %@", log: log, type: .info, key) + Crashlytics.crashlytics().log("success: startAccessingSecurityScopedResource for: \(key)") resolvedBookmarks.append(urlForBookmark) bookmarks.append([urlForBookmark.absoluteString : urlData]) } else{ - os_log("ERROR: startAccessingSecurityScopedResource: for = %@", log: log, type: .info, key) + os_log("ERROR: startAccessingSecurityScopedResource for: %@", log: log, type: .info, key) + Crashlytics.crashlytics().log("ERROR: startAccessingSecurityScopedResource for: \(key)") staleBookmarks.append(URL(fileURLWithPath: key)) } - } } catch { staleBookmarks.append(URL(fileURLWithPath: key)) @@ -127,35 +138,37 @@ handle bookmarkDataIsStale let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) if url.startAccessingSecurityScopedResource() { + os_log("success: startAccessingSecurityScopedResource for: %@", log: log, type: .info, url.absoluteString) + Crashlytics.crashlytics().log("success: startAccessingSecurityScopedResource for: \(url.absoluteString)") - for (index, bookmarkDict) in bookmarks.enumerated(){ - - print("Found \(bookmarkDict) at position \(index)") - + for (_, bookmarkDict) in bookmarks.enumerated(){ if bookmarkDict[url.absoluteString] != nil { - os_log("beenHereBefore", log: log, type: .debug) + os_log("Existing bookmark for:", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("Existing bookmark for: \(url.absoluteString)") return true } } do { - + // any errors are caught below in the catch{} + os_log("Attempting to create secure bookmark for %@ - with bookmarkCreationOptions: %i", log: log, type: .debug, url.absoluteString, bookmarkCreationOptions.rawValue) + Crashlytics.crashlytics().log("Attempting to create secure bookmark for: \(url.absoluteString) - with bookmarkCreationOptions:\(bookmarkCreationOptions.rawValue)") let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + os_log("Attempting to create SecureBookmark object for %@", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("Attempting to create SecureBookmark object for: \(url.absoluteString)") let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: url) -// UserDefaults.standard.set(sp.encode(), forKey: "sp") - -// let encoder = JSONEncoder() -// encoder.outputFormatting = .prettyPrinted -// let data = try encoder.encode(sp) -// print(String(data: data, encoding: .utf8)!) - - + os_log("Attempting to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) + Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") let spData = sp.getEncodedData() + + os_log("Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("Adding \(url.absoluteString) to bookmarks") bookmarks.append([url.absoluteString : spData]) -// bookmarks.append([url.absoluteString : bookmarkData]) + os_log("Updating UserDefaults", log: log, type: .debug) + Crashlytics.crashlytics().log("Updating UserDefaults") prefs.set(bookmarks, forKey: SPSecureBookmarks) return true From 1df6fab1e138b99e934f5ef2bf7fe95b6b06de09 Mon Sep 17 00:00:00 2001 From: James Stout Date: Fri, 18 Dec 2020 21:05:37 +0800 Subject: [PATCH 12/37] WIP --- .../DataExport/SPExportController.m | 56 +------- .../ConnectionView/SPConnectionController.h | 2 - .../ConnectionView/SPConnectionController.m | 129 +++--------------- .../Other/SecureBookmarkManager.swift | 115 +++++++++++++--- .../Preferences/Panes/SPFilePreferencePane.m | 28 ++-- .../Panes/SPNetworkPreferencePane.m | 16 +-- Source/Controllers/SPAppController.m | 6 +- Source/Other/Data/SPConstants.h | 3 +- Source/Other/Data/SPConstants.m | 3 +- .../Extensions/UserDefaultsExtension.swift | 70 +--------- 10 files changed, 141 insertions(+), 287 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index c7497f173..5ec3921e0 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -289,14 +289,7 @@ - (void)exportTables:(NSArray *)exportTables asFormat:(SPExportType)format using // initially popuplate the tables list [self refreshTableList:nil]; - id o; - if((o = [prefs objectForKey:SPSecureBookmarks])){ - [bookmarks setArray:o]; - } - else{ - SPLog(@"Could not load SPSecureBookmarks from prefs"); - CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); - } + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; // overwrite defaults with user settings from last export [self applySettingsFromDictionary:[prefs objectForKey:SPLastExportSettings] error:NULL]; @@ -630,48 +623,11 @@ - (IBAction)changeExportOutputPath:(id)sender } [self->exportPathField setStringValue:path]; - - // the code always seems to go into this block as the - // user has selected the folder and we have com.apple.security.files.user-selected.read-write - if([self->changeExportOutputPathPanel.URL startAccessingSecurityScopedResource] == YES){ - - SPLog(@"got access to: %@", self->changeExportOutputPathPanel.URL.absoluteString); - CLS_LOG(@"got access to: %@", self->changeExportOutputPathPanel.URL.absoluteString); - - BOOL __block beenHereBefore = NO; - - // have we been here before? - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - if(dict[self->changeExportOutputPathPanel.URL.absoluteString] != nil){ - NSLog(@"beenHereBefore: %@", dict[self->changeExportOutputPathPanel.URL.absoluteString]); - beenHereBefore = YES; - *stop = YES; - } - }]; - - if(beenHereBefore == NO){ - // create a bookmark - NSError *error = nil; - NSData *tmpAppScopedBookmark = [self->changeExportOutputPathPanel.URL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope // this needs to be read-write - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - // save to prefs - if(tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{self->changeExportOutputPathPanel.URL.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - else{ - SPLog(@"Problem creating bookmark - %@ : %@",self->changeExportOutputPathPanel.URL.absoluteString, [error localizedDescription]); - CLS_LOG(@"Problem creating bookmark - %@ : %@",self->changeExportOutputPathPanel.URL.absoluteString, [error localizedDescription]); - } - } - } - else{ - SPLog(@"Problem startAccessingSecurityScopedResource for - %@",self->changeExportOutputPathPanel.URL.absoluteString); - CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",self->changeExportOutputPathPanel.URL.absoluteString); - } + + // this needs to be read-write + if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->changeExportOutputPathPanel.URL options:(NSURLBookmarkCreationWithSecurityScope)] == YES){ + SPLog(@"addBookMarkForUrl success"); + } } }]; } diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.h b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.h index 5a19c34df..ccdf05508 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.h +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.h @@ -232,7 +232,6 @@ typedef NS_ENUM(NSInteger, SPConnectionTimeZoneMode) { @property (readwrite, copy) NSString *connectionSSHKeychainItemAccount; @property (readwrite) BOOL useCompression; @property (readwrite, strong) NSMutableArray *> *bookmarks; -@property (readwrite, strong) NSMutableArray *resolvedBookmarks; @property (readonly) BOOL isConnecting; @property (readonly) BOOL isEditingConnection; @@ -260,7 +259,6 @@ typedef NS_ENUM(NSInteger, SPConnectionTimeZoneMode) { -(BOOL)validateCertFile:(NSURL *)url error:(NSError **)outError; -(BOOL)validateKeyFile:(NSURL *)url error:(NSError **)outError; -(void)showValidationAlertForError:(NSError*)err; --(void)reRequestSecureAccess; -(BOOL)connected; // Favorites interaction diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index ca93d6de6..bbc0f5711 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -159,7 +159,6 @@ @implementation SPConnectionController @synthesize sshPort; @synthesize useCompression; @synthesize bookmarks; -@synthesize resolvedBookmarks; @synthesize allowSplitViewResizing; @synthesize connectionKeychainID = connectionKeychainID; @@ -463,59 +462,15 @@ - (IBAction)chooseKeyLocation:(NSButton *)sender [keySelectionPanel setAccessoryViewDisclosed:YES]; } [keySelectionPanel setDelegate:self]; - [keySelectionPanel beginSheetModalForWindow:[dbDocument parentWindow] completionHandler:^(NSInteger returnCode) - { + [keySelectionPanel beginSheetModalForWindow:[dbDocument parentWindow] completionHandler:^(NSInteger returnCode){ + NSString *selectedFilePath=[[self->keySelectionPanel URL] path]; NSError *err=nil; - SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - - - if([self->keySelectionPanel.URL startAccessingSecurityScopedResource] == YES){ - - SPLog(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); - CLS_LOG(@"got access to: %@", self->keySelectionPanel.URL.absoluteString); - - // a bit of duplicated code here, - // same code is in the export controler - //TODO: put this in a utility/helper class - BOOL __block beenHereBefore = NO; - - // have we been here before? - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - if(dict[self->keySelectionPanel.URL.absoluteString] != nil){ - NSLog(@"beenHereBefore: %@", dict[self->keySelectionPanel.URL.absoluteString]); - beenHereBefore = YES; - *stop = YES; - } - }]; - - if(beenHereBefore == NO){ - // create a bookmark - NSError *error = nil; - // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess - NSData *tmpAppScopedBookmark = [self->keySelectionPanel.URL bookmarkDataWithOptions:(NSURLBookmarkCreationWithSecurityScope - | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess) - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; - // save to prefs - if(tmpAppScopedBookmark && !error) { - [self->bookmarks addObject:@{self->keySelectionPanel.URL.absoluteString : tmpAppScopedBookmark}]; - [self->prefs setObject:self->bookmarks forKey:SPSecureBookmarks]; - } - else{ - SPLog(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); - CLS_LOG(@"Problem creating bookmark - %@ : %@",self->keySelectionPanel.URL.absoluteString, [error localizedDescription]); - } - } + // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess + if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->keySelectionPanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookMarkForUrl success"); } - else{ - SPLog(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); - CLS_LOG(@"Problem startAccessingSecurityScopedResource for - %@",self->keySelectionPanel.URL.absoluteString); - } - // SSH key file selection if (sender == self->sshSSHKeyButton) { @@ -1331,14 +1286,15 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N // reload the bookmarks, when the observer detected a change in them // // thanks a lot to @jamesstout for pointing this out! - if ([keyPath isEqualToString:SPSecureBookmarks]) { + if ([keyPath isEqualToString:SASecureBookmarks]) { id o; - if((o = [prefs objectForKey:SPSecureBookmarks])){ + if((o = [prefs objectForKey:SASecureBookmarks])){ [bookmarks setArray:o]; } -// [self reRequestSecureAccess]; + // do we? + [SecureBookmarkManager.sharedInstance reRequestSecureAccessToBookmarks]; } } @@ -2031,10 +1987,8 @@ - (void)_documentWillClose:(NSNotification *)notification [sshTunnel setConnectionStateChangeSelector:nil delegate:nil]; } - - for(NSURL *url in resolvedBookmarks){ - [url stopAccessingSecurityScopedResource]; - } + [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; + } #pragma mark - SPConnectionHandler @@ -3299,29 +3253,18 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document prefs = [NSUserDefaults standardUserDefaults]; bookmarks = [[NSMutableArray alloc] init]; - resolvedBookmarks = [[NSMutableArray alloc] init]; - SPLog(@"prefs: %@", prefs.dictionaryRepresentation); CLS_LOG(@"prefs: %@", prefs.dictionaryRepresentation); - id o; - if((o = [prefs objectForKey:SPSecureBookmarks])){ - [bookmarks setArray:o]; - } - else{ - SPLog(@"Could not load SPSecureBookmarks from prefs"); - CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); - } + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; - SecureBookmarkManager __unused *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - - // we need to re-request access to places we've been before.. -// [self reRequestSecureAccess]; + // we need to re-request access to places we've been before.. JCS: do we? + [SecureBookmarkManager.sharedInstance reRequestSecureAccessToBookmarks]; // add an observer to get re-read the bookmarks when they change [prefs addObserver:self - forKeyPath:SPSecureBookmarks + forKeyPath:SASecureBookmarks options:NSKeyValueObservingOptionNew context:NULL]; @@ -3397,44 +3340,6 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document return timeZoneMenuItems; } --(void)reRequestSecureAccess{ - - SPLog(@"reRequestSecureAccess to saved bookmarks"); - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { - - [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSData *obj, BOOL *stop2) { - - NSError *error = nil; - - BOOL bookmarkDataIsStale; - - NSURL *tmpURL = [NSURL URLByResolvingBookmarkData:obj - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&bookmarkDataIsStale - error:&error]; - - if(!error){ - [tmpURL startAccessingSecurityScopedResource]; - [resolvedBookmarks addObject:tmpURL]; - } - else if(bookmarkDataIsStale == YES){ - SPLog("The bookmark is outdated and needs to be regenerated - %@", key); -// _ = saveBookmarkForSelectedURL() - } - else{ - SPLog(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - CLS_LOG(@"Problem resolving bookmark - %@ : %@",key, [error localizedDescription]); - } - }]; - }]; - - SPLog(@"resolvedBookmarks - %@",resolvedBookmarks); - CLS_LOG(@"resolvedBookmarks - %@",resolvedBookmarks); - -} - /** * Loads the connection controllers UI nib. */ @@ -3787,9 +3692,7 @@ - (void)dealloc [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationEnabledKey]; [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationKey]; - for(NSURL *url in resolvedBookmarks){ - [url stopAccessingSecurityScopedResource]; - } + [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; [self setConnectionKeychainID:nil]; diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 7f59d0e6d..66273e7c0 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -15,7 +15,8 @@ import Firebase reRequestSecureAccess addBookmark handle bookmarkDataIsStale - +revokeBookmark + stopAllSecurityScopedAccess */ @objc final class SecureBookmarkManager: NSObject { @@ -24,28 +25,41 @@ handle bookmarkDataIsStale @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) - - private let log: OSLog - private let prefs: UserDefaults = UserDefaults.standard + private let log: OSLog + private let prefs: UserDefaults = UserDefaults.standard + var observer: NSKeyValueObservation? override private init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") - os_log("SecureBookmarkManager init = %@", log: log, type: .info) + os_log("SecureBookmarkManager init.", log: log, type: .info) Crashlytics.crashlytics().log("SecureBookmarkManager init.") super.init() - bookmarks = prefs.array(forKey: SPSecureBookmarks) as? [[String: Data]] ?? [["": Data()]] + // this manager *should* be the only thing changing the bookmarks pref, but in case... + observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new,.old], changeHandler: { (defaults, change) in - // the default above means there is always one entry, even if there are none in prefs - // so check for less than 2 - if(bookmarks.count < 2){ - os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) - Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") - bookmarks.removeAll() - return - } + print("SPSecureBookmarks changed from: \(String(describing: change.oldValue)), updated to: \(String(describing: change.newValue))") + + print("change.newValue count: \(String(describing: change.newValue?.count))") + print("change.oldValue count: \(String(describing: change.oldValue?.count))") + + self.bookmarks.removeAll() + self.bookmarks = change.newValue! + + }) + + // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... + bookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]] ?? [["noBookmarks": Data()]] + + // check for default empty value + if bookmarks[0]["noBookmarks"] != nil { + os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) + Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") + bookmarks.removeAll() + return + } os_log("bookmarks = %@", log: log, type: .info, bookmarks) @@ -59,7 +73,6 @@ handle bookmarkDataIsStale Crashlytics.crashlytics().log("staleBookmarks count = \(staleBookmarks.count)") } - /// reRequestSecureAccessToBookmarks // loops through current bookmarks from prefs and re-requests secure access // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use @@ -123,11 +136,10 @@ handle bookmarkDataIsStale } // reset bookmarks - prefs.set(bookmarks, forKey: SPSecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) } - /// addBookMark /// - Parameters: /// - url: file URL to generate secure bookmark for @@ -169,7 +181,7 @@ handle bookmarkDataIsStale os_log("Updating UserDefaults", log: log, type: .debug) Crashlytics.crashlytics().log("Updating UserDefaults") - prefs.set(bookmarks, forKey: SPSecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) return true @@ -185,4 +197,71 @@ handle bookmarkDataIsStale return false } } + + /// revokeBookmark + /// - Parameters: + /// - filename: filename to revoke secure bookmark for + /// - Returns: Bool on success or fail + @objc public func revokeBookmark(filename: String) -> Bool { + + // FIXME: don't seem to need to take a mutable copy + // is that right @Kaspik? + for (index, bookmarkDict) in bookmarks.enumerated(){ + for (key, urlData) in bookmarkDict { + + if key == filename { + do { + os_log("Revoking bookmark for: %@", log: log, type: .debug, filename) + Crashlytics.crashlytics().log("Revoking bookmark for: \(filename)") + + os_log("bookmarks[%i]: %@", log: log, type: .debug, index, bookmarks[index]) + + var bookmarkDataIsStale = false + + // need to get the proper URL + let spData = SecureBookmark.getDecodedData(encodedData: urlData) + + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") + // always resolve with just _NSURLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + + urlForBookmark.stopAccessingSecurityScopedResource() + + resolvedBookmarks.removeAll(where: { $0 == urlForBookmark }) + bookmarks.remove(at: index) + prefs.set(bookmarks, forKey: SASecureBookmarks) + os_log("Successfully revoked bookmark for: %@", log: log, type: .debug, filename) + Crashlytics.crashlytics().log("Successfully revoked bookmark for: \(filename)") + return true + } + catch{ + os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) + Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") + os_log("Failed to revoke bookmark for: %@", log: log, type: .debug, filename) + Crashlytics.crashlytics().log("Failed to revoke bookmark for: \(filename)") + return false + } + } + } + } + + os_log("Failed to revoke bookmark for: %@", log: log, type: .debug, filename) + Crashlytics.crashlytics().log("Failed to revoke bookmark for: \(filename)") + return false + } + + // revoke secure access to all bookmarks + @objc public func stopAllSecurityScopedAccess() { + + for url in resolvedBookmarks{ + resolvedBookmarks.removeAll(where: { $0 == url }) + url.stopAccessingSecurityScopedResource() + } + } + + deinit { + observer?.invalidate() + } + } diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 44deeec6a..2c2f73996 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -57,10 +57,7 @@ - (instancetype)init - (void)dealloc { SPLog(@"dealloc"); - - for(NSURL *url in SecureBookmarkManager.sharedInstance.resolvedBookmarks){ - [url stopAccessingSecurityScopedResource]; - } + [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; } @@ -95,16 +92,9 @@ - (void)preferencePaneWillBeShown - (void)loadBookmarks { - id o; - - if((o = [prefs objectForKey:SPSecureBookmarks])){ - [bookmarks setArray:o]; - } - else{ - SPLog(@"Could not load SPSecureBookmarks from prefs"); - CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); - } - + + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; + // we need to re-request access to places we've been before.. // not anymore, done at startup @@ -145,7 +135,7 @@ - (IBAction)revokeBookmark:(id)sender // iterate through all selected indice [indiceToRevoke enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { // retrieve the filename - NSString __block *fileName = [NSString stringWithFormat:@"file://%@", fileNames[idx]]; + NSString __block *fileName = [NSString stringWithFormat:@"file://%@", fileNames[idx]]; [bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idxBookmarks, BOOL *stopBookmarks) { NSEnumerator *keyEnumerator = [dict keyEnumerator]; @@ -155,9 +145,11 @@ - (IBAction)revokeBookmark:(id)sender if (![key isEqualToString:fileName]) { continue; } - - [bookmarks removeObjectAtIndex:idxBookmarks]; - [prefs setObject:bookmarks forKey:SPSecureBookmarks]; + + [SecureBookmarkManager.sharedInstance revokeBookmarkWithFilename:fileName]; + +// [bookmarks removeObjectAtIndex:idxBookmarks]; +// [prefs setObject:bookmarks forKey:SASecureBookmarks]; } }]; }]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 87766f840..136f28f50 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -54,14 +54,7 @@ - (instancetype)init sslCiphers = [[NSMutableArray alloc] init]; bookmarks = [[NSMutableArray alloc] init]; - id o; - if((o = [prefs objectForKey:SPSecureBookmarks])){ - [bookmarks setArray:o]; - } - else{ - SPLog(@"Could not load SPSecureBookmarks from prefs"); - CLS_LOG(@"Could not load SPSecureBookmarks from prefs"); - } + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; } @@ -71,10 +64,7 @@ - (instancetype)init - (void)dealloc { SPLog(@"dealloc"); - - for(NSURL *url in SecureBookmarkManager.sharedInstance.resolvedBookmarks){ - [url stopAccessingSecurityScopedResource]; - } + [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; } #pragma mark - @@ -276,8 +266,6 @@ - (void) chooseSSHConfig homeDirectory = [NSURL fileURLWithPath:NSHomeDirectory()]; } - SecureBookmarkManager *sharedSecureBookmarkManager = SecureBookmarkManager.sharedInstance; - _currentFilePanel = [NSOpenPanel openPanel]; [_currentFilePanel setTitle:@"Choose ssh config"]; [_currentFilePanel setCanChooseFiles:YES]; diff --git a/Source/Controllers/SPAppController.m b/Source/Controllers/SPAppController.m index 140437fa1..1b8dbbc86 100755 --- a/Source/Controllers/SPAppController.m +++ b/Source/Controllers/SPAppController.m @@ -108,7 +108,7 @@ - (instancetype)init [NSApp setDelegate:self]; - SecureBookmarkManager __unused *secureBookmarkManager = SecureBookmarkManager.sharedInstance; + } @@ -202,7 +202,9 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification // cannot be set higher than FIRLoggerLevelNotice [[FIRConfiguration sharedInstance] setLoggerLevel:FIRLoggerLevelDebug]; #endif - + + // has to be after FIRApp configure + SecureBookmarkManager __unused *secureBookmarkManager = SecureBookmarkManager.sharedInstance; // init SQLite query history SQLiteHistoryManager __unused *sqliteHistoryManager = SQLiteHistoryManager.sharedInstance; diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 500a72fd1..db5cafde0 100755 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -425,8 +425,7 @@ extern NSString *SPFileNameTimeTokenName; extern NSString *SPFileName24HourTimeTokenName; extern NSString *SPFileNameFavoriteTokenName; extern NSString *SPFileNameTableTokenName; -extern NSString *SPSecureBookmarks; -extern NSString *SPSecureBookmarkOptions; +extern NSString *SASecureBookmarks; // Misc extern NSString *SPContentFilters; diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 9ae9eba3d..071f78d13 100755 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -219,8 +219,7 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPCSVFieldImportMappingAlignment = @"CSVFieldImportMappingAlignment"; NSString *SPImportClipboardTempFileNamePrefix = @"~/tmp/_SP_ClipBoard_Import_File_"; NSString *SPLastExportSettings = @"LastExportSettings"; -NSString *SPSecureBookmarks = @"SPSecureBookmarks"; -NSString *SPSecureBookmarkOptions = @"SPSecureBookmarkOptions"; +NSString *SASecureBookmarks = @"SPSecureBookmarks"; // Export filename tokens NSString *SPFileNameDatabaseTokenName = @"database"; NSString *SPFileNameHostTokenName = @"host"; diff --git a/Source/Other/Extensions/UserDefaultsExtension.swift b/Source/Other/Extensions/UserDefaultsExtension.swift index 71bb8ea02..45e870720 100644 --- a/Source/Other/Extensions/UserDefaultsExtension.swift +++ b/Source/Other/Extensions/UserDefaultsExtension.swift @@ -28,71 +28,9 @@ extension UserDefaults { } return savedFont } - - @objc static func saveBookmarkData(_ bookmarkData: Data, key: String) { - let defaults = UserDefaults.standard - defaults.set(NSKeyedArchiver.archivedData(withRootObject: bookmarkData), forKey: key) - } - - @objc static func getBookmarkData(key: String) -> Data { - let defaults = UserDefaults.standard - guard - let bookmarkData = defaults.data(forKey: key), - let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data - - else { - return Data() - } - return unarchivedBookmarkData - } - - @objc static func saveBookmarks(_ bookmarks: [Dictionary]) { - let defaults = UserDefaults.standard - - var bmCopy = bookmarks - bmCopy.removeAll() - for (_, bookmarkDict) in bookmarks.enumerated(){ - for (key, bookmarkData) in bookmarkDict { - let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) - let newDict = [key : encData] - bmCopy.append(newDict) - } - } - defaults.set(bmCopy, forKey: SPSecureBookmarks) - + + @objc dynamic var SPSecureBookmarks: [Dictionary] { + return array(forKey: SASecureBookmarks) as? [Dictionary] ?? [["noBookmarks": Data()]] } - -// @objc static func getBookmarks() -> NSArray { -// let defaults = UserDefaults.standard -// -// let bookmarks = defaults.object(forKey: SPSecureBookmarks) as! Array> -// -// var bmCopy : [Dictionary] = [] -// -// for (_, bookmarkDict) in bookmarks.enumerated(){ -// for (key, bookmarkData) in bookmarkDict { -// guard -// let unarchivedBookmarkData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(bookmarkData) as? Data, -// let newDict = [key : unarchivedBookmarkData], -// bmCopy.append(newDict) -// else { -// return Data() -// } -// } -// } -// -// -//// var bmCopy = bookmarks -//// bmCopy.removeAll() -//// for (_, bookmarkDict) in bookmarks.enumerated(){ -//// -//// for (key, bookmarkData) in bookmarkDict { -//// let encData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) -//// let newDict = [key : encData] -//// bmCopy.append(newDict) -//// } -//// } -//// defaults.set(bmCopy, forKey: SPSecureBookmarks) -// -// } + } From 3cd6ca96b72c66fea12b227fcbeea342564be098 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 01:33:32 +0800 Subject: [PATCH 13/37] added bookMarkFor a file --- .../DataExport/SPExportController.m | 26 ++------ .../Other/SecureBookmarkManager.swift | 60 ++++++++++++++++++- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index 5ec3921e0..ab922f699 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -3119,30 +3119,12 @@ - (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err // look up that bookmark and request access if(bookmarks.count > 0){ if((o = [dict objectForKey:@"exportPath"])) [exportPathField setStringValue:o]; - - NSError __block *error = nil; - - [self.bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict2, NSUInteger idx, BOOL *stop) { - - NSString *tmpStr = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; - - if(dict2[tmpStr] != nil){ - BOOL bookmarkDataIsStale; + NSString *tmpStr = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; + + // ret value can be nil + userChosenDirectory = [SecureBookmarkManager.sharedInstance bookMarkForFilename:tmpStr]; - self.userChosenDirectory = [NSURL URLByResolvingBookmarkData:dict2[tmpStr] - options:NSURLBookmarkResolutionWithSecurityScope - relativeToURL:nil - bookmarkDataIsStale:&bookmarkDataIsStale - error:&error]; - *stop = YES; - } - }]; - - // if no bookmark was found this just calls against nil - if(!error){ - [userChosenDirectory startAccessingSecurityScopedResource]; - } } SPExportType et; diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 66273e7c0..f74355670 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -16,7 +16,8 @@ reRequestSecureAccess addBookmark handle bookmarkDataIsStale revokeBookmark - stopAllSecurityScopedAccess +stopAllSecurityScopedAccess + */ @objc final class SecureBookmarkManager: NSObject { @@ -198,6 +199,63 @@ revokeBookmark } } + /// bookMarkFor a file + /// - Parameters: + /// - filename: file URL to generate secure bookmark for + /// - Returns: the resolved URL or nil + @objc public func bookMarkFor(filename: String) -> URL? { + + os_log("filename %@", log: log, type: .debug, filename) + + for (_, bookmarkDict) in bookmarks.enumerated(){ + for (key, urlData) in bookmarkDict { + + os_log("Bookmark URL = %@", log: log, type: .info, key) + + if key == filename { + do { + var bookmarkDataIsStale = false + + os_log("Attempting to getDecodedData for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to getDecodedData for: \(key)") + let spData = SecureBookmark.getDecodedData(encodedData: urlData) + + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") + // always resolve with just _NSURLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + + if bookmarkDataIsStale { + os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) + Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") + staleBookmarks.append(URL(fileURLWithPath: key)) + } + else { + if urlForBookmark.startAccessingSecurityScopedResource() { + return urlForBookmark + } + else{ + os_log("Error startAccessingSecurityScopedResource For: key = %@.", log: log, type: .error, urlForBookmark.absoluteString) + Crashlytics.crashlytics().log("Error startAccessingSecurityScopedResource For: key = \(urlForBookmark.absoluteString).") + return nil + } + } + } + catch{ + os_log("Error resolving bookmark: filename = %@. Error: %@", log: log, type: .error, filename, error.localizedDescription) + Crashlytics.crashlytics().log("Error resolving bookmark: filename = \(filename). Error: \(error.localizedDescription)") + return nil + } + } + } + } + + os_log("No bookmark found for %@", log: log, type: .info, filename) + Crashlytics.crashlytics().log("No bookmark found for: \(filename)") + return nil + } + + /// revokeBookmark /// - Parameters: /// - filename: filename to revoke secure bookmark for From d8ec37a13a0d9d8886e74f23e0ab1753960c5cf2 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 03:14:40 +0800 Subject: [PATCH 14/37] SPBookmarksChanged notification --- .../ConnectionView/SPConnectionController.m | 64 +++++++++---------- .../Other/SecureBookmarkManager.swift | 34 +++++++--- Source/Other/Data/SPConstants.h | 1 + Source/Other/Data/SPConstants.m | 1 + 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index bbc0f5711..ddbd1b6ba 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -95,6 +95,8 @@ - (void)_selectNode:(SPTreeNode *)node; - (void)_scrollToSelectedNode; - (void)_removeNode:(SPTreeNode *)node; - (void)_removeAllPasswordsForNode:(SPTreeNode *)node; +- (void)_refreshBookmarks; + - (NSNumber *)_createNewFavoriteID; - (SPTreeNode *)_favoriteNodeForFavoriteID:(NSInteger)favoriteID; @@ -1286,16 +1288,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N // reload the bookmarks, when the observer detected a change in them // // thanks a lot to @jamesstout for pointing this out! - if ([keyPath isEqualToString:SASecureBookmarks]) { - id o; - - if((o = [prefs objectForKey:SASecureBookmarks])){ - [bookmarks setArray:o]; - } - - // do we? - [SecureBookmarkManager.sharedInstance reRequestSecureAccessToBookmarks]; - } + // no longer needed + // but for some reson there are other KVO registered, so need to keep the method... } #pragma mark - @@ -3252,21 +3246,15 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document keychain = [[SPKeychain alloc] init]; prefs = [NSUserDefaults standardUserDefaults]; - bookmarks = [[NSMutableArray alloc] init]; + bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; SPLog(@"prefs: %@", prefs.dictionaryRepresentation); CLS_LOG(@"prefs: %@", prefs.dictionaryRepresentation); - [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; - // we need to re-request access to places we've been before.. JCS: do we? [SecureBookmarkManager.sharedInstance reRequestSecureAccessToBookmarks]; - - // add an observer to get re-read the bookmarks when they change - [prefs addObserver:self - forKeyPath:SASecureBookmarks - options:NSKeyValueObservingOptionNew - context:NULL]; + + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_refreshBookmarks) name:SPBookmarksChangedNotification object:SecureBookmarkManager.sharedInstance]; // Create a reference to the favorites controller, forcing the data to be loaded from disk // and the tree to be constructed. @@ -3306,6 +3294,15 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document return self; } +- (void)_refreshBookmarks{ + SPLog(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + CLS_LOG(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; +} + + + // TODO: this is called once per connection screen - but the timezones don't change right? Should be static/class method? - (NSArray *)generateTimeZoneMenuItems { @@ -3356,19 +3353,21 @@ - (void)loadNib */ - (void)registerForNotifications { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_documentWillClose:) - name:SPDocumentWillCloseNotification - object:dbDocument]; + NSNotificationCenter *nc = NSNotificationCenter.defaultCenter; + + [nc addObserver:self + selector:@selector(_documentWillClose:) + name:SPDocumentWillCloseNotification + object:dbDocument]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(scrollViewFrameChanged:) - name:NSViewFrameDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_processFavoritesDataChange:) - name:SPConnectionFavoritesChangedNotification - object:nil]; + [nc addObserver:self + selector:@selector(scrollViewFrameChanged:) + name:NSViewFrameDidChangeNotification + object:nil]; + [nc addObserver:self + selector:@selector(_processFavoritesDataChange:) + name:SPConnectionFavoritesChangedNotification + object:nil]; // Registered to be notified of changes to connection information [self addObserver:self @@ -3690,7 +3689,8 @@ - (void)dealloc [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationEnabledKey]; [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationKey]; [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationEnabledKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationKey]; + [self removeObserver:self forKeyPath:SPBookmarksChangedNotification]; [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index f74355670..8d79c691c 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -21,14 +21,20 @@ stopAllSecurityScopedAccess */ @objc final class SecureBookmarkManager: NSObject { + @objc static let sharedInstance = SecureBookmarkManager() - @objc public var bookmarks: [Dictionary] = [] + + @objc public var bookmarks: [Dictionary] = [] @objc public var resolvedBookmarks: [URL] = [] @objc public var staleBookmarks: [URL] = [] + private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard - var observer: NSKeyValueObservation? + private var observer: NSKeyValueObservation? + + private var iChangedTheBookmarks: Bool = false + override private init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") @@ -41,15 +47,20 @@ stopAllSecurityScopedAccess // this manager *should* be the only thing changing the bookmarks pref, but in case... observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new,.old], changeHandler: { (defaults, change) in - print("SPSecureBookmarks changed from: \(String(describing: change.oldValue)), updated to: \(String(describing: change.newValue))") + os_log("SPSecureBookmarks changed.", log: self.log, type: .debug) + Crashlytics.crashlytics().log("SPSecureBookmarks changed.") - print("change.newValue count: \(String(describing: change.newValue?.count))") - print("change.oldValue count: \(String(describing: change.oldValue?.count))") + if self.iChangedTheBookmarks == false{ + self.bookmarks.removeAll() + self.bookmarks = change.newValue! + } + // reset + self.iChangedTheBookmarks = false - self.bookmarks.removeAll() - self.bookmarks = change.newValue! + // post notificay for ConnectionController + NotificationCenter.default.post(name: Notification.Name.init(NSNotification.Name.SPBookmarksChanged.rawValue), object: self) - }) + }) // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... bookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]] ?? [["noBookmarks": Data()]] @@ -137,6 +148,7 @@ stopAllSecurityScopedAccess } // reset bookmarks + iChangedTheBookmarks = true prefs.set(bookmarks, forKey: SASecureBookmarks) } @@ -176,12 +188,13 @@ stopAllSecurityScopedAccess Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") let spData = sp.getEncodedData() - os_log("Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) - Crashlytics.crashlytics().log("Adding \(url.absoluteString) to bookmarks") + os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") bookmarks.append([url.absoluteString : spData]) os_log("Updating UserDefaults", log: log, type: .debug) Crashlytics.crashlytics().log("Updating UserDefaults") + iChangedTheBookmarks = true prefs.set(bookmarks, forKey: SASecureBookmarks) return true @@ -288,6 +301,7 @@ stopAllSecurityScopedAccess resolvedBookmarks.removeAll(where: { $0 == urlForBookmark }) bookmarks.remove(at: index) + iChangedTheBookmarks = true prefs.set(bookmarks, forKey: SASecureBookmarks) os_log("Successfully revoked bookmark for: %@", log: log, type: .debug, filename) Crashlytics.crashlytics().log("Successfully revoked bookmark for: \(filename)") diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index db5cafde0..ebb0dd525 100755 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -429,6 +429,7 @@ extern NSString *SASecureBookmarks; // Misc extern NSString *SPContentFilters; +extern NSString *SPBookmarksChangedNotification; extern NSString *SPDocumentTaskEndNotification; extern NSString *SPDocumentTaskStartNotification; extern NSString *SPDocumentWillCloseNotification; diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 071f78d13..435f3240a 100755 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -235,6 +235,7 @@ - (SPTableViewType)tableViewTypeEnumFromString{ // Misc NSString *SPContentFilters = @"ContentFilters"; NSString *SPDocumentTaskEndNotification = @"DocumentTaskEnded"; +NSString *SPBookmarksChangedNotification = @"BookmarksChangedNotification"; NSString *SPDocumentTaskStartNotification = @"DocumentTaskStarted"; NSString *SPDocumentWillCloseNotification = @"DocumentWillClose"; NSString *SPActivitiesUpdateNotification = @"ActivitiesUpdateNotification"; From ab7bcd7a99681f86f4921a7950747c7def3e6f02 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 03:15:05 +0800 Subject: [PATCH 15/37] changed bookmarks init --- Source/Controllers/DataExport/SPExportController.m | 8 +++----- .../Preferences/Panes/SPNetworkPreferencePane.m | 5 +---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index ab922f699..e09d1febb 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -202,8 +202,8 @@ - (instancetype)init exporters = [[NSMutableArray alloc] init]; exportFiles = [[NSMutableArray alloc] init]; operationQueue = [[NSOperationQueue alloc] init]; - bookmarks = [[NSMutableArray alloc] init]; - + bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; + showAdvancedView = NO; showCustomFilenameView = NO; serverLowerCaseTableNameValue = NSNotFound; @@ -288,9 +288,7 @@ - (void)exportTables:(NSArray *)exportTables asFormat:(SPExportType)format using // initially popuplate the tables list [self refreshTableList:nil]; - - [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; - + // overwrite defaults with user settings from last export [self applySettingsFromDictionary:[prefs objectForKey:SPLastExportSettings] error:NULL]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 136f28f50..fd78c26ff 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -52,10 +52,7 @@ - (instancetype)init self = [super init]; if (self) { sslCiphers = [[NSMutableArray alloc] init]; - bookmarks = [[NSMutableArray alloc] init]; - - [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; - + bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; } return self; From 27516012f10c84db2660fe3d286e33249b6863c4 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 03:16:20 +0800 Subject: [PATCH 16/37] use revokeBookmarkWithFilename --- .../Preferences/Panes/SPFilePreferencePane.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 2c2f73996..507717a5d 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -92,7 +92,7 @@ - (void)preferencePaneWillBeShown - (void)loadBookmarks { - + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; // we need to re-request access to places we've been before.. @@ -146,10 +146,10 @@ - (IBAction)revokeBookmark:(id)sender continue; } - [SecureBookmarkManager.sharedInstance revokeBookmarkWithFilename:fileName]; - -// [bookmarks removeObjectAtIndex:idxBookmarks]; -// [prefs setObject:bookmarks forKey:SASecureBookmarks]; + if([SecureBookmarkManager.sharedInstance revokeBookmarkWithFilename:fileName] == YES){ + SPLog(@"refreshing bookmarks: %@", bookmarks); + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; + } } }]; }]; From 20f51389ddc5110d6ee07b7e3ba6253ccba110e1 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 04:17:30 +0800 Subject: [PATCH 17/37] logging --- .../Controllers/Other/SecureBookmarkManager.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 8d79c691c..7c0c45669 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -47,10 +47,9 @@ stopAllSecurityScopedAccess // this manager *should* be the only thing changing the bookmarks pref, but in case... observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new,.old], changeHandler: { (defaults, change) in - os_log("SPSecureBookmarks changed.", log: self.log, type: .debug) - Crashlytics.crashlytics().log("SPSecureBookmarks changed.") - if self.iChangedTheBookmarks == false{ + os_log("SPSecureBookmarks changed NOT by SecureBookmarkManager.", log: self.log, type: .debug) + Crashlytics.crashlytics().log("SPSecureBookmarks changed NOT by SecureBookmarkManager.") self.bookmarks.removeAll() self.bookmarks = change.newValue! } @@ -65,8 +64,8 @@ stopAllSecurityScopedAccess // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... bookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]] ?? [["noBookmarks": Data()]] - // check for default empty value - if bookmarks[0]["noBookmarks"] != nil { + // FIXME: check for default empty value - I don't like this. + if bookmarks.isEmpty == true || bookmarks[0]["noBookmarks"] != nil { os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") bookmarks.removeAll() @@ -114,7 +113,7 @@ stopAllSecurityScopedAccess // always resolve with just _NSURLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) -// bookmarkDataIsStale = true +// bookmarkDataIsStale = true // a bookmark might be "stale" because the app hasn't been used // in many months, macOS has been upgraded, the app was // re-installed, the app's preferences .plist file was deleted, etc. @@ -150,9 +149,9 @@ stopAllSecurityScopedAccess // reset bookmarks iChangedTheBookmarks = true prefs.set(bookmarks, forKey: SASecureBookmarks) - } + /// addBookMark /// - Parameters: /// - url: file URL to generate secure bookmark for From 5a351fdf13f9c06e36f4ca53958dbd1ee18ec337 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 04:18:52 +0800 Subject: [PATCH 18/37] tidied revokeBookmarkWithFilename --- .../Preferences/Panes/SPFilePreferencePane.m | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 507717a5d..d288d427c 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -137,21 +137,10 @@ - (IBAction)revokeBookmark:(id)sender // retrieve the filename NSString __block *fileName = [NSString stringWithFormat:@"file://%@", fileNames[idx]]; - [bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idxBookmarks, BOOL *stopBookmarks) { - NSEnumerator *keyEnumerator = [dict keyEnumerator]; - id key; - - while (key = [keyEnumerator nextObject]) { - if (![key isEqualToString:fileName]) { - continue; - } - - if([SecureBookmarkManager.sharedInstance revokeBookmarkWithFilename:fileName] == YES){ - SPLog(@"refreshing bookmarks: %@", bookmarks); - [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; - } - } - }]; + if([SecureBookmarkManager.sharedInstance revokeBookmarkWithFilename:fileName] == YES){ + SPLog(@"refreshing bookmarks: %@", bookmarks); + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; + } }]; // reload the bookmarks and reset the view From 2e2340bd5855baa2831a33b404d0ba61b9849d7c Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 05:42:52 +0800 Subject: [PATCH 19/37] prompt user to recreate stale secure bookmarks --- .../Localization/en.lproj/Localizable.strings | 7 +++++ .../es-ES.lproj/Localizable.strings | 7 +++++ .../Other/SecureBookmarkManager.swift | 14 ++++----- .../Preferences/SPPreferenceController.h | 4 ++- Source/Controllers/SPAppController.m | 29 +++++++++++++++++-- 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/Resources/Localization/en.lproj/Localizable.strings b/Resources/Localization/en.lproj/Localizable.strings index ce1847650..17a0bcb3a 100644 --- a/Resources/Localization/en.lproj/Localizable.strings +++ b/Resources/Localization/en.lproj/Localizable.strings @@ -3173,3 +3173,10 @@ /* connection view : ssl : key file picker : wrong format error title */ "“%@” is not a valid private key file." = "“%@” is not a valid private key file."; + +/* text shown when there are stale bookmarks */ +"App Sandbox Issue" = "App Sandbox Issue"; +"Stale Bookmarks" = "Stale Bookmarks"; +"You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?" = "You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?"; +"Yes" = "Yes"; +"A reminder of your stale secure bookmarks:\n\n%@\n" = "A reminder of your stale secure bookmarks:\n\n%@\n"; diff --git a/Resources/Localization/es-ES.lproj/Localizable.strings b/Resources/Localization/es-ES.lproj/Localizable.strings index 8268f4298..e4bfea311 100644 --- a/Resources/Localization/es-ES.lproj/Localizable.strings +++ b/Resources/Localization/es-ES.lproj/Localizable.strings @@ -3173,3 +3173,10 @@ /* connection view : ssl : key file picker : wrong format error title */ "“%@” is not a valid private key file." = "“%@” is not a valid private key file."; + +/* text shown when there are stale bookmarks */ +"App Sandbox Issue" = "App Sandbox Issue"; +"Stale Bookmarks" = "Stale Bookmarks"; +"You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?" = "You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?"; +"Yes" = "Yes"; +"A reminder of your stale secure bookmarks:\n\n%@\n" = "A reminder of your stale secure bookmarks:\n\n%@\n"; diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 7c0c45669..460a2ec61 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -26,7 +26,7 @@ stopAllSecurityScopedAccess @objc public var bookmarks: [Dictionary] = [] @objc public var resolvedBookmarks: [URL] = [] - @objc public var staleBookmarks: [URL] = [] + @objc public var staleBookmarks: [String] = [] private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog @@ -120,8 +120,7 @@ stopAllSecurityScopedAccess if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") - staleBookmarks.append(URL(fileURLWithPath: key)) - } + staleBookmarks.append(key) } else { os_log("Resolved bookmark: %@", log: log, type: .info, key) Crashlytics.crashlytics().log("Resolved bookmark: \(key)") @@ -135,12 +134,11 @@ stopAllSecurityScopedAccess else{ os_log("ERROR: startAccessingSecurityScopedResource for: %@", log: log, type: .info, key) Crashlytics.crashlytics().log("ERROR: startAccessingSecurityScopedResource for: \(key)") - staleBookmarks.append(URL(fileURLWithPath: key)) - } + staleBookmarks.append(key) } } } catch { - staleBookmarks.append(URL(fileURLWithPath: key)) - os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) + staleBookmarks.append(key) + os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") } } @@ -240,7 +238,7 @@ stopAllSecurityScopedAccess if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") - staleBookmarks.append(URL(fileURLWithPath: key)) + staleBookmarks.append(key) } else { if urlForBookmark.startAccessingSecurityScopedResource() { diff --git a/Source/Controllers/Preferences/SPPreferenceController.h b/Source/Controllers/Preferences/SPPreferenceController.h index be52d8709..7853ecc59 100644 --- a/Source/Controllers/Preferences/SPPreferenceController.h +++ b/Source/Controllers/Preferences/SPPreferenceController.h @@ -66,9 +66,11 @@ NSToolbarItem *networkItem; NSToolbarItem *editorItem; NSToolbarItem *shortcutItem; + SPPreferenceFontChangeTarget fontChangeTarget; + + @package NSToolbarItem *fileItem; - SPPreferenceFontChangeTarget fontChangeTarget; } @property (readonly) SPGeneralPreferencePane *generalPreferencePane; diff --git a/Source/Controllers/SPAppController.m b/Source/Controllers/SPAppController.m index 1b8dbbc86..cf5339f45 100755 --- a/Source/Controllers/SPAppController.m +++ b/Source/Controllers/SPAppController.m @@ -204,8 +204,31 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification #endif // has to be after FIRApp configure - SecureBookmarkManager __unused *secureBookmarkManager = SecureBookmarkManager.sharedInstance; - + // this reRequests access to all bookmarks + SecureBookmarkManager *secureBookmarkManager = SecureBookmarkManager.sharedInstance; + + // prompt user to recreate secure bookmarks + if(secureBookmarkManager.staleBookmarks.count > 0){ + + SPLog(@"We have stale bookmarks"); + + NSString *staleBookmarksString = [secureBookmarkManager.staleBookmarks componentsJoinedByString:@"\n"]; + + [NSAlert createDefaultAlertWithTitle:[NSString stringWithFormat:NSLocalizedString(@"App Sandbox Issue", @"App Sandbox Issue")] + message:[NSString stringWithFormat:NSLocalizedString(@"You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?", @"Would you like to re-request access now?"), staleBookmarksString] + primaryButtonTitle:NSLocalizedString(@"Yes", @"Yes") + primaryButtonHandler:^{ + SPLog(@"re-request access now"); + [self->prefsController showWindow:self]; + [self->prefsController displayPreferencePane:self->prefsController->fileItem]; + + [NSAlert createWarningAlertWithTitle:NSLocalizedString(@"Stale Bookmarks", @"Stale Bookmarks") message:[NSString stringWithFormat:NSLocalizedString(@"A reminder of your stale secure bookmarks:\n\n%@\n", @"A reminder of your stale secure bookmarks:\n\n%@\n"), staleBookmarksString] callback:nil]; + + } cancelButtonHandler:^{ + SPLog(@"No not now"); + }]; + } + // init SQLite query history SQLiteHistoryManager __unused *sqliteHistoryManager = SQLiteHistoryManager.sharedInstance; @@ -239,7 +262,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification } // Set autoconnection if appropriate - if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault]) { + if ([[NSUserDefaults standardUserDefaults] boolForKey:SPAutoConnectToDefault] && secureBookmarkManager.staleBookmarks.count == 0) { [newConnection connect]; } } From b551f6c6b22ca8f58690b634f353418f4077f00c Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 05:43:09 +0800 Subject: [PATCH 20/37] rm logging --- .../MainViewControllers/ConnectionView/SPConnectionController.m | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index ddbd1b6ba..2c0382a65 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -3248,7 +3248,6 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; - SPLog(@"prefs: %@", prefs.dictionaryRepresentation); CLS_LOG(@"prefs: %@", prefs.dictionaryRepresentation); // we need to re-request access to places we've been before.. JCS: do we? From dcfea6cae5be716fae1cc37dbb3b9a9bb2427a46 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 06:04:12 +0800 Subject: [PATCH 21/37] logging --- Source/Controllers/Other/SecureBookmarkManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 460a2ec61..abd44ad03 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -165,7 +165,7 @@ stopAllSecurityScopedAccess for (_, bookmarkDict) in bookmarks.enumerated(){ if bookmarkDict[url.absoluteString] != nil { - os_log("Existing bookmark for:", log: log, type: .debug, url.absoluteString) + os_log("Existing bookmark for: %@", log: log, type: .debug, url.absoluteString) Crashlytics.crashlytics().log("Existing bookmark for: \(url.absoluteString)") return true } From 9b57ea9594c2d49130703fb2af1f7353ac7f76a6 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 06:04:53 +0800 Subject: [PATCH 22/37] addObserver for SPBookmarksChangedNotification which calls _refreshBookmarks --- .../Preferences/Panes/SPFilePreferencePane.m | 13 ++++++++++++- .../Preferences/Panes/SPNetworkPreferencePane.m | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index d288d427c..166bc5d5d 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -34,6 +34,8 @@ @import Firebase; @interface SPFilePreferencePane () +- (void)_refreshBookmarks; + @end @implementation SPFilePreferencePane @@ -47,7 +49,9 @@ - (instancetype)init if (self) { fileNames = [[NSMutableArray alloc] init]; bookmarks = [[NSMutableArray alloc] init]; - + + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_refreshBookmarks) name:SPBookmarksChangedNotification object:SecureBookmarkManager.sharedInstance]; + [self loadBookmarks]; } @@ -61,6 +65,13 @@ - (void)dealloc } +- (void)_refreshBookmarks{ + SPLog(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + CLS_LOG(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; +} + - (NSImage *)preferencePaneIcon { if (@available(macOS 11.0, *)) { return [NSImage imageWithSystemSymbolName:@"folder" accessibilityDescription:nil]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index fd78c26ff..62eb8f2d6 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -41,6 +41,8 @@ - (void)updateHiddenFiles; - (void)loadSSLCiphers; - (void)storeSSLCiphers; + (NSArray *)defaultSSLCipherList; +- (void)_refreshBookmarks; + @end @implementation SPNetworkPreferencePane @@ -52,7 +54,8 @@ - (instancetype)init self = [super init]; if (self) { sslCiphers = [[NSMutableArray alloc] init]; - bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; + bookmarks = [NSMutableArray arrayWithArray:SecureBookmarkManager.sharedInstance.bookmarks]; + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_refreshBookmarks) name:SPBookmarksChangedNotification object:SecureBookmarkManager.sharedInstance]; } return self; @@ -61,9 +64,17 @@ - (instancetype)init - (void)dealloc { SPLog(@"dealloc"); + [self removeObserver:self forKeyPath:SPBookmarksChangedNotification]; [SecureBookmarkManager.sharedInstance stopAllSecurityScopedAccess]; } +- (void)_refreshBookmarks{ + SPLog(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + CLS_LOG(@"Got SPBookmarksChangedNotification, refreshing bookmarks"); + + [bookmarks setArray:SecureBookmarkManager.sharedInstance.bookmarks]; +} + #pragma mark - #pragma mark Preference pane protocol methods From 26a5313e9a933a3d9b90038fe06011af63dd2b06 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 22:34:28 +0800 Subject: [PATCH 23/37] feedback from Kaspik --- .../Localization/en.lproj/Localizable.strings | 10 +- .../es-ES.lproj/Localizable.strings | 6 - .../DataExport/SPExportController.m | 5 +- Source/Controllers/Other/SecureBookmark.swift | 6 +- .../Other/SecureBookmarkData.swift | 31 +++-- .../Other/SecureBookmarkManager.swift | 123 ++++++++---------- Source/Other/Extensions/DataExtension.swift | 9 -- .../Extensions/UserDefaultsExtension.swift | 3 +- 8 files changed, 90 insertions(+), 103 deletions(-) delete mode 100644 Source/Other/Extensions/DataExtension.swift diff --git a/Resources/Localization/en.lproj/Localizable.strings b/Resources/Localization/en.lproj/Localizable.strings index 17a0bcb3a..f719c1a59 100644 --- a/Resources/Localization/en.lproj/Localizable.strings +++ b/Resources/Localization/en.lproj/Localizable.strings @@ -3174,9 +3174,17 @@ /* connection view : ssl : key file picker : wrong format error title */ "“%@” is not a valid private key file." = "“%@” is not a valid private key file."; -/* text shown when there are stale bookmarks */ +/* text shown when there are App Sandbox Issues */ "App Sandbox Issue" = "App Sandbox Issue"; + +/* Stale Bookmarks error title */ "Stale Bookmarks" = "Stale Bookmarks"; + +/* Question to user to see if they would like to re-request access */ "You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?" = "You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?"; + +/* The answer, yes */ "Yes" = "Yes"; + +/* Shown to remind users of their stale bookmarks */ "A reminder of your stale secure bookmarks:\n\n%@\n" = "A reminder of your stale secure bookmarks:\n\n%@\n"; diff --git a/Resources/Localization/es-ES.lproj/Localizable.strings b/Resources/Localization/es-ES.lproj/Localizable.strings index e4bfea311..3973e08e3 100644 --- a/Resources/Localization/es-ES.lproj/Localizable.strings +++ b/Resources/Localization/es-ES.lproj/Localizable.strings @@ -3174,9 +3174,3 @@ /* connection view : ssl : key file picker : wrong format error title */ "“%@” is not a valid private key file." = "“%@” is not a valid private key file."; -/* text shown when there are stale bookmarks */ -"App Sandbox Issue" = "App Sandbox Issue"; -"Stale Bookmarks" = "Stale Bookmarks"; -"You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?" = "You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?"; -"Yes" = "Yes"; -"A reminder of your stale secure bookmarks:\n\n%@\n" = "A reminder of your stale secure bookmarks:\n\n%@\n"; diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index e09d1febb..fa4941943 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -3118,11 +3118,10 @@ - (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err if(bookmarks.count > 0){ if((o = [dict objectForKey:@"exportPath"])) [exportPathField setStringValue:o]; - NSString *tmpStr = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; + NSString *fileURLString = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; // ret value can be nil - userChosenDirectory = [SecureBookmarkManager.sharedInstance bookMarkForFilename:tmpStr]; - + userChosenDirectory = [SecureBookmarkManager.sharedInstance bookMarkForFilename:fileURLString]; } SPExportType et; diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index f5df85803..7c588ede4 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -9,7 +9,7 @@ import Foundation -class SecureBookmark: NSObject { +final class SecureBookmark: NSObject { var _data: SecureBookmarkData init(data: Data, options: Double, url: URL) { @@ -17,7 +17,7 @@ class SecureBookmark: NSObject { super.init() } - public func getEncodedData() -> Data{ + func getEncodedData() -> Data{ if #available(OSX 10.13, *) { let codedData = try! NSKeyedArchiver.archivedData(withRootObject: _data, requiringSecureCoding: true) return codedData @@ -28,7 +28,7 @@ class SecureBookmark: NSObject { } } - public class func getDecodedData(encodedData: Data) -> SecureBookmarkData { + class func getDecodedData(encodedData: Data) -> SecureBookmarkData { if #available(OSX 10.13, *) { return try! NSKeyedUnarchiver.unarchivedObject(ofClass: SecureBookmarkData.self, from: encodedData)! diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index adf37ef96..3e0052c3f 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -8,30 +8,33 @@ import Foundation -class SecureBookmarkData: NSObject, NSCoding, NSSecureCoding { +final class SecureBookmarkData: NSObject { - var bookmarkData: Data - var options: Double - var theUrl: URL - - static var supportsSecureCoding: Bool { - return true - } - + internal let bookmarkData: Data + internal let options: Double + internal let theUrl: URL + init(data: Data, options: Double, url: URL ) { self.bookmarkData = data self.options = options self.theUrl = url super.init() } - +} + +extension SecureBookmarkData: NSCoding, NSSecureCoding { + + static var supportsSecureCoding: Bool { + return true + } + // MARK: NSCoding Implementation enum Keys: String { case bookmarkData = "BookmarkData" case options = "Options" case theUrl = "TheUrl" } - + func encode(with coder: NSCoder) { if #available(OSX 10.13, *) { @@ -47,8 +50,8 @@ class SecureBookmarkData: NSObject, NSCoding, NSSecureCoding { coder.encode(theUrl, forKey: Keys.theUrl.rawValue) } } - - required convenience init?(coder: NSCoder) { + + convenience init?(coder: NSCoder) { if #available(OSX 10.13, *) { let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue)! as Data @@ -63,6 +66,6 @@ class SecureBookmarkData: NSObject, NSCoding, NSSecureCoding { let theUrl = coder.decodeObject(forKey: Keys.theUrl.rawValue) as! URL self.init(data: bookmarkData, options: options, url: theUrl) } - } } + diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index abd44ad03..7fb07614b 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -24,11 +24,11 @@ stopAllSecurityScopedAccess @objc static let sharedInstance = SecureBookmarkManager() - @objc public var bookmarks: [Dictionary] = [] - @objc public var resolvedBookmarks: [URL] = [] - @objc public var staleBookmarks: [String] = [] + @objc var bookmarks: [Dictionary] = [] + @objc var staleBookmarks: [String] = [] + @objc private var resolvedBookmarks: [URL] = [] - private let _NSURLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) + private let URLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard private var observer: NSKeyValueObservation? @@ -36,7 +36,7 @@ stopAllSecurityScopedAccess private var iChangedTheBookmarks: Bool = false - override private init() { + override init() { log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") os_log("SecureBookmarkManager init.", log: log, type: .info) @@ -62,16 +62,15 @@ stopAllSecurityScopedAccess }) // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... - bookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]] ?? [["noBookmarks": Data()]] - // FIXME: check for default empty value - I don't like this. - if bookmarks.isEmpty == true || bookmarks[0]["noBookmarks"] != nil { + guard let secureBookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]], secureBookmarks.isNotEmpty else { os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") - bookmarks.removeAll() return } + bookmarks = secureBookmarks + os_log("bookmarks = %@", log: log, type: .info, bookmarks) // I think part of init of this manager should be to re-request access @@ -87,16 +86,16 @@ stopAllSecurityScopedAccess /// reRequestSecureAccessToBookmarks // loops through current bookmarks from prefs and re-requests secure access // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use - // _NSURLBookmarkResolutionWithSecurityScope, not the options it was originally created with + // URLBookmarkResolutionWithSecurityScope, not the options it was originally created with // otherwise it will be markes as stale. - @objc public func reRequestSecureAccessToBookmarks() { + @objc func reRequestSecureAccessToBookmarks() { let bmCopy = bookmarks // start afresh bookmarks.removeAll() - for (_, bookmarkDict) in bmCopy.enumerated(){ + for bookmarkDict in bmCopy { for (key, urlData) in bookmarkDict { os_log("Bookmark URL = %@", log: log, type: .info, key) @@ -110,8 +109,8 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") - // always resolve with just _NSURLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + // always resolve with just URLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // bookmarkDataIsStale = true // a bookmark might be "stale" because the app hasn't been used @@ -157,57 +156,50 @@ stopAllSecurityScopedAccess /// - Returns: Bool on success or fail @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) + let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) - if url.startAccessingSecurityScopedResource() { - os_log("success: startAccessingSecurityScopedResource for: %@", log: log, type: .info, url.absoluteString) - Crashlytics.crashlytics().log("success: startAccessingSecurityScopedResource for: \(url.absoluteString)") + // A file chosen from an NSOpen/SavePanel already has access + // no need to start access again here again here - for (_, bookmarkDict) in bookmarks.enumerated(){ - if bookmarkDict[url.absoluteString] != nil { - os_log("Existing bookmark for: %@", log: log, type: .debug, url.absoluteString) - Crashlytics.crashlytics().log("Existing bookmark for: \(url.absoluteString)") - return true - } - } + for bookmarkDict in bookmarks { + if bookmarkDict[url.absoluteString] != nil { + os_log("Existing bookmark for: %@", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("Existing bookmark for: \(url.absoluteString)") + return true + } + } - do { - // any errors are caught below in the catch{} - os_log("Attempting to create secure bookmark for %@ - with bookmarkCreationOptions: %i", log: log, type: .debug, url.absoluteString, bookmarkCreationOptions.rawValue) - Crashlytics.crashlytics().log("Attempting to create secure bookmark for: \(url.absoluteString) - with bookmarkCreationOptions:\(bookmarkCreationOptions.rawValue)") - let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) + do { + // any errors are caught below in the catch{} + os_log("Attempting to create secure bookmark for %@ - with bookmarkCreationOptions: %i", log: log, type: .debug, url.absoluteString, bookmarkCreationOptions.rawValue) + Crashlytics.crashlytics().log("Attempting to create secure bookmark for: \(url.absoluteString) - with bookmarkCreationOptions:\(bookmarkCreationOptions.rawValue)") + let bookmarkData = try url.bookmarkData(options: [bookmarkCreationOptions], includingResourceValuesForKeys: nil, relativeTo: nil) - os_log("Attempting to create SecureBookmark object for %@", log: log, type: .debug, url.absoluteString) - Crashlytics.crashlytics().log("Attempting to create SecureBookmark object for: \(url.absoluteString)") - let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: url) + os_log("Attempting to create SecureBookmark object for %@", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("Attempting to create SecureBookmark object for: \(url.absoluteString)") + let sp = SecureBookmark(data: bookmarkData, options: Double(bookmarkCreationOptions.rawValue), url: url) - os_log("Attempting to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) - Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") - let spData = sp.getEncodedData() + os_log("Attempting to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) + Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") + let spData = sp.getEncodedData() - os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) - Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") - bookmarks.append([url.absoluteString : spData]) + os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) + Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") + bookmarks.append([url.absoluteString : spData]) - os_log("Updating UserDefaults", log: log, type: .debug) - Crashlytics.crashlytics().log("Updating UserDefaults") - iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + os_log("Updating UserDefaults", log: log, type: .debug) + Crashlytics.crashlytics().log("Updating UserDefaults") + iChangedTheBookmarks = true + prefs.set(bookmarks, forKey: SASecureBookmarks) - return true + return true - } catch { - os_log("Error creating secure Bookmark For: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) - Crashlytics.crashlytics().log("Error creating secure Bookmark For: key = \(url.absoluteString). Error: \(error.localizedDescription)") - return false - } - } - else{ - os_log("Error startAccessingSecurityScopedResource For: key = %@.", log: log, type: .error, url.absoluteString) - Crashlytics.crashlytics().log("Error startAccessingSecurityScopedResource For: key = \(url.absoluteString).") - return false - } - } + } catch { + os_log("Error creating secure Bookmark For: key = %@. Error: %@", log: log, type: .error, url.absoluteString, error.localizedDescription) + Crashlytics.crashlytics().log("Error creating secure Bookmark For: key = \(url.absoluteString). Error: \(error.localizedDescription)") + return false + } + } /// bookMarkFor a file /// - Parameters: @@ -217,7 +209,7 @@ stopAllSecurityScopedAccess os_log("filename %@", log: log, type: .debug, filename) - for (_, bookmarkDict) in bookmarks.enumerated(){ + for bookmarkDict in bookmarks { for (key, urlData) in bookmarkDict { os_log("Bookmark URL = %@", log: log, type: .info, key) @@ -232,8 +224,8 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") - // always resolve with just _NSURLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + // always resolve with just URLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) @@ -270,10 +262,8 @@ stopAllSecurityScopedAccess /// - Parameters: /// - filename: filename to revoke secure bookmark for /// - Returns: Bool on success or fail - @objc public func revokeBookmark(filename: String) -> Bool { + @objc func revokeBookmark(filename: String) -> Bool { - // FIXME: don't seem to need to take a mutable copy - // is that right @Kaspik? for (index, bookmarkDict) in bookmarks.enumerated(){ for (key, urlData) in bookmarkDict { @@ -291,10 +281,11 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") - // always resolve with just _NSURLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [_NSURLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + // always resolve with just URLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) - urlForBookmark.stopAccessingSecurityScopedResource() + // you do not need to call .stopAccessingSecurityScopedResource() + // simply remove from bookmarks resolvedBookmarks.removeAll(where: { $0 == urlForBookmark }) bookmarks.remove(at: index) @@ -321,7 +312,7 @@ stopAllSecurityScopedAccess } // revoke secure access to all bookmarks - @objc public func stopAllSecurityScopedAccess() { + @objc func stopAllSecurityScopedAccess() { for url in resolvedBookmarks{ resolvedBookmarks.removeAll(where: { $0 == url }) diff --git a/Source/Other/Extensions/DataExtension.swift b/Source/Other/Extensions/DataExtension.swift deleted file mode 100644 index f1b77aaaf..000000000 --- a/Source/Other/Extensions/DataExtension.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// DataExtension.swift -// sequel-ace -// -// Created by James on 7/12/2020. -// Copyright © 2020 Sequel-Ace. All rights reserved. -// - -import Foundation diff --git a/Source/Other/Extensions/UserDefaultsExtension.swift b/Source/Other/Extensions/UserDefaultsExtension.swift index 45e870720..ca676410b 100644 --- a/Source/Other/Extensions/UserDefaultsExtension.swift +++ b/Source/Other/Extensions/UserDefaultsExtension.swift @@ -29,7 +29,8 @@ extension UserDefaults { return savedFont } - @objc dynamic var SPSecureBookmarks: [Dictionary] { + // needs to be objc for KVO + @objc var SPSecureBookmarks: [Dictionary] { return array(forKey: SASecureBookmarks) as? [Dictionary] ?? [["noBookmarks": Data()]] } From 8b4f1fcb10222fa379d78e3c675d90a6ec22afdc Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 22:41:04 +0800 Subject: [PATCH 24/37] changed indentation --- Source/Controllers/Other/SecureBookmark.swift | 12 +- .../Other/SecureBookmarkData.swift | 20 +-- .../Other/SecureBookmarkManager.swift | 144 ++++++++---------- 3 files changed, 74 insertions(+), 102 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 7c588ede4..182220923 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -8,16 +8,15 @@ import Foundation - final class SecureBookmark: NSObject { var _data: SecureBookmarkData - + init(data: Data, options: Double, url: URL) { _data = SecureBookmarkData(data: data, options: options, url: url) super.init() } - - func getEncodedData() -> Data{ + + func getEncodedData() -> Data { if #available(OSX 10.13, *) { let codedData = try! NSKeyedArchiver.archivedData(withRootObject: _data, requiringSecureCoding: true) return codedData @@ -27,9 +26,8 @@ final class SecureBookmark: NSObject { return codedData } } - + class func getDecodedData(encodedData: Data) -> SecureBookmarkData { - if #available(OSX 10.13, *) { return try! NSKeyedUnarchiver.unarchivedObject(ofClass: SecureBookmarkData.self, from: encodedData)! } else { @@ -38,5 +36,3 @@ final class SecureBookmark: NSObject { } } } - - diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index 3e0052c3f..ef78c227f 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -9,26 +9,25 @@ import Foundation final class SecureBookmarkData: NSObject { - internal let bookmarkData: Data internal let options: Double internal let theUrl: URL - init(data: Data, options: Double, url: URL ) { - self.bookmarkData = data + init(data: Data, options: Double, url: URL) { + bookmarkData = data self.options = options - self.theUrl = url + theUrl = url super.init() } } extension SecureBookmarkData: NSCoding, NSSecureCoding { - static var supportsSecureCoding: Bool { return true } // MARK: NSCoding Implementation + enum Keys: String { case bookmarkData = "BookmarkData" case options = "Options" @@ -36,14 +35,12 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { } func encode(with coder: NSCoder) { - if #available(OSX 10.13, *) { - //For NSSecureCoding + // For NSSecureCoding coder.encode(bookmarkData as NSData, forKey: Keys.bookmarkData.rawValue) coder.encode(NSNumber(value: options), forKey: Keys.options.rawValue) coder.encode(theUrl as NSURL, forKey: Keys.theUrl.rawValue) - } - else { + } else { // For NSCoding coder.encode(bookmarkData, forKey: Keys.bookmarkData.rawValue) coder.encode(options, forKey: Keys.options.rawValue) @@ -52,14 +49,12 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { } convenience init?(coder: NSCoder) { - if #available(OSX 10.13, *) { let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue)! as Data let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue)! as! Double let theUrl = coder.decodeObject(of: NSURL.self, forKey: Keys.theUrl.rawValue)! as URL self.init(data: bookmarkData, options: options, url: theUrl) - } - else{ + } else { // For NSCoding let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data let options = coder.decodeDouble(forKey: Keys.options.rawValue) @@ -68,4 +63,3 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { } } } - diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 7fb07614b..5cd4b632e 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -6,26 +6,24 @@ // Copyright © 2020 Sequel-Ace. All rights reserved. // +import Firebase import Foundation import os.log -import Firebase - /* -reRequestSecureAccess -addBookmark -handle bookmarkDataIsStale -revokeBookmark -stopAllSecurityScopedAccess + reRequestSecureAccess + addBookmark + handle bookmarkDataIsStale + revokeBookmark + stopAllSecurityScopedAccess -*/ + */ @objc final class SecureBookmarkManager: NSObject { - - @objc static let sharedInstance = SecureBookmarkManager() + @objc static let sharedInstance = SecureBookmarkManager() @objc var bookmarks: [Dictionary] = [] - @objc var staleBookmarks: [String] = [] + @objc var staleBookmarks: [String] = [] @objc private var resolvedBookmarks: [URL] = [] private let URLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) @@ -35,19 +33,18 @@ stopAllSecurityScopedAccess private var iChangedTheBookmarks: Bool = false - override init() { - log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") + log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") os_log("SecureBookmarkManager init.", log: log, type: .info) Crashlytics.crashlytics().log("SecureBookmarkManager init.") - super.init() + super.init() // this manager *should* be the only thing changing the bookmarks pref, but in case... - observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new,.old], changeHandler: { (defaults, change) in + observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new, .old], changeHandler: { _, change in - if self.iChangedTheBookmarks == false{ + if self.iChangedTheBookmarks == false { os_log("SPSecureBookmarks changed NOT by SecureBookmarkManager.", log: self.log, type: .debug) Crashlytics.crashlytics().log("SPSecureBookmarks changed NOT by SecureBookmarkManager.") self.bookmarks.removeAll() @@ -57,51 +54,49 @@ stopAllSecurityScopedAccess self.iChangedTheBookmarks = false // post notificay for ConnectionController - NotificationCenter.default.post(name: Notification.Name.init(NSNotification.Name.SPBookmarksChanged.rawValue), object: self) + NotificationCenter.default.post(name: Notification.Name(NSNotification.Name.SPBookmarksChanged.rawValue), object: self) }) // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... guard let secureBookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]], secureBookmarks.isNotEmpty else { - os_log("Could not get secureBookmarks from prefs.", log: self.log, type: .error) + os_log("Could not get secureBookmarks from prefs.", log: log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") return } bookmarks = secureBookmarks - os_log("bookmarks = %@", log: log, type: .info, bookmarks) + os_log("bookmarks = %@", log: log, type: .info, bookmarks) - // I think part of init of this manager should be to re-request access - reRequestSecureAccessToBookmarks() + // I think part of init of this manager should be to re-request access + reRequestSecureAccessToBookmarks() os_log("resolvedBookmarks count = %i", log: log, type: .info, resolvedBookmarks.count) os_log("staleBookmarks count = %i", log: log, type: .info, staleBookmarks.count) Crashlytics.crashlytics().log("resolvedBookmarks count = \(resolvedBookmarks.count)") Crashlytics.crashlytics().log("staleBookmarks count = \(staleBookmarks.count)") - } + } - /// reRequestSecureAccessToBookmarks + /// reRequestSecureAccessToBookmarks // loops through current bookmarks from prefs and re-requests secure access // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use // URLBookmarkResolutionWithSecurityScope, not the options it was originally created with // otherwise it will be markes as stale. - @objc func reRequestSecureAccessToBookmarks() { - - let bmCopy = bookmarks + @objc func reRequestSecureAccessToBookmarks() { + let bmCopy = bookmarks // start afresh bookmarks.removeAll() for bookmarkDict in bmCopy { - for (key, urlData) in bookmarkDict { - - os_log("Bookmark URL = %@", log: log, type: .info, key) + for (key, urlData) in bookmarkDict { + os_log("Bookmark URL = %@", log: log, type: .info, key) - do { - var bookmarkDataIsStale = false + do { + var bookmarkDataIsStale = false os_log("Attempting to getDecodedData for %@", log: log, type: .debug, key) Crashlytics.crashlytics().log("Attempting to getDecodedData for: \(key)") @@ -110,53 +105,51 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") // always resolve with just URLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // bookmarkDataIsStale = true -// a bookmark might be "stale" because the app hasn't been used -// in many months, macOS has been upgraded, the app was -// re-installed, the app's preferences .plist file was deleted, etc. - if bookmarkDataIsStale { - os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) + // a bookmark might be "stale" because the app hasn't been used + // in many months, macOS has been upgraded, the app was + // re-installed, the app's preferences .plist file was deleted, etc. + if bookmarkDataIsStale { + os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") - staleBookmarks.append(key) } - else { - os_log("Resolved bookmark: %@", log: log, type: .info, key) + staleBookmarks.append(key) + } else { + os_log("Resolved bookmark: %@", log: log, type: .info, key) Crashlytics.crashlytics().log("Resolved bookmark: \(key)") let res = urlForBookmark.startAccessingSecurityScopedResource() if res == true { os_log("success: startAccessingSecurityScopedResource for: %@", log: log, type: .info, key) Crashlytics.crashlytics().log("success: startAccessingSecurityScopedResource for: \(key)") resolvedBookmarks.append(urlForBookmark) - bookmarks.append([urlForBookmark.absoluteString : urlData]) - } - else{ + bookmarks.append([urlForBookmark.absoluteString: urlData]) + } else { os_log("ERROR: startAccessingSecurityScopedResource for: %@", log: log, type: .info, key) Crashlytics.crashlytics().log("ERROR: startAccessingSecurityScopedResource for: \(key)") - staleBookmarks.append(key) } - } - } catch { + staleBookmarks.append(key) + } + } + } catch { staleBookmarks.append(key) os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) - Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") - } - } - } + Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") + } + } + } // reset bookmarks iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) - } - - - /// addBookMark - /// - Parameters: - /// - url: file URL to generate secure bookmark for - /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions - /// - Returns: Bool on success or fail - @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { + prefs.set(bookmarks, forKey: SASecureBookmarks) + } - let bookmarkCreationOptions : URL.BookmarkCreationOptions = URL.BookmarkCreationOptions.init(rawValue: options) + /// addBookMark + /// - Parameters: + /// - url: file URL to generate secure bookmark for + /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions + /// - Returns: Bool on success or fail + @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { + let bookmarkCreationOptions: URL.BookmarkCreationOptions = URL.BookmarkCreationOptions(rawValue: options) // A file chosen from an NSOpen/SavePanel already has access // no need to start access again here again here @@ -185,7 +178,7 @@ stopAllSecurityScopedAccess os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") - bookmarks.append([url.absoluteString : spData]) + bookmarks.append([url.absoluteString: spData]) os_log("Updating UserDefaults", log: log, type: .debug) Crashlytics.crashlytics().log("Updating UserDefaults") @@ -206,12 +199,10 @@ stopAllSecurityScopedAccess /// - filename: file URL to generate secure bookmark for /// - Returns: the resolved URL or nil @objc public func bookMarkFor(filename: String) -> URL? { - os_log("filename %@", log: log, type: .debug, filename) for bookmarkDict in bookmarks { for (key, urlData) in bookmarkDict { - os_log("Bookmark URL = %@", log: log, type: .info, key) if key == filename { @@ -225,25 +216,22 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") // always resolve with just URLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") staleBookmarks.append(key) - } - else { + } else { if urlForBookmark.startAccessingSecurityScopedResource() { return urlForBookmark - } - else{ + } else { os_log("Error startAccessingSecurityScopedResource For: key = %@.", log: log, type: .error, urlForBookmark.absoluteString) Crashlytics.crashlytics().log("Error startAccessingSecurityScopedResource For: key = \(urlForBookmark.absoluteString).") return nil } } - } - catch{ + } catch { os_log("Error resolving bookmark: filename = %@. Error: %@", log: log, type: .error, filename, error.localizedDescription) Crashlytics.crashlytics().log("Error resolving bookmark: filename = \(filename). Error: \(error.localizedDescription)") return nil @@ -257,16 +245,13 @@ stopAllSecurityScopedAccess return nil } - /// revokeBookmark /// - Parameters: /// - filename: filename to revoke secure bookmark for /// - Returns: Bool on success or fail @objc func revokeBookmark(filename: String) -> Bool { - - for (index, bookmarkDict) in bookmarks.enumerated(){ + for (index, bookmarkDict) in bookmarks.enumerated() { for (key, urlData) in bookmarkDict { - if key == filename { do { os_log("Revoking bookmark for: %@", log: log, type: .debug, filename) @@ -282,7 +267,7 @@ stopAllSecurityScopedAccess os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") // always resolve with just URLBookmarkResolutionWithSecurityScope - let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData , options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // you do not need to call .stopAccessingSecurityScopedResource() // simply remove from bookmarks @@ -294,8 +279,7 @@ stopAllSecurityScopedAccess os_log("Successfully revoked bookmark for: %@", log: log, type: .debug, filename) Crashlytics.crashlytics().log("Successfully revoked bookmark for: \(filename)") return true - } - catch{ + } catch { os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") os_log("Failed to revoke bookmark for: %@", log: log, type: .debug, filename) @@ -313,8 +297,7 @@ stopAllSecurityScopedAccess // revoke secure access to all bookmarks @objc func stopAllSecurityScopedAccess() { - - for url in resolvedBookmarks{ + for url in resolvedBookmarks { resolvedBookmarks.removeAll(where: { $0 == url }) url.stopAccessingSecurityScopedResource() } @@ -323,5 +306,4 @@ stopAllSecurityScopedAccess deinit { observer?.invalidate() } - } From 8762dfbc435627150665a0d73a66920a38ff26b7 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 22:59:03 +0800 Subject: [PATCH 25/37] more feedback --- Source/Controllers/Other/SecureBookmark.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 182220923..2c68ebb57 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -9,20 +9,20 @@ import Foundation final class SecureBookmark: NSObject { - var _data: SecureBookmarkData + private let theData: SecureBookmarkData init(data: Data, options: Double, url: URL) { - _data = SecureBookmarkData(data: data, options: options, url: url) + theData = SecureBookmarkData(data: data, options: options, url: url) super.init() } func getEncodedData() -> Data { if #available(OSX 10.13, *) { - let codedData = try! NSKeyedArchiver.archivedData(withRootObject: _data, requiringSecureCoding: true) + let codedData = try! NSKeyedArchiver.archivedData(withRootObject: theData, requiringSecureCoding: true) return codedData } else { // Fallback on earlier versions - let codedData = NSKeyedArchiver.archivedData(withRootObject: _data) + let codedData = NSKeyedArchiver.archivedData(withRootObject: theData) return codedData } } From b2bc87dc5d4877eace0bb5a6cf2ecc90be078959 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sat, 19 Dec 2020 22:59:18 +0800 Subject: [PATCH 26/37] better logging --- .../Other/SecureBookmarkManager.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 5cd4b632e..12e31d383 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -102,15 +102,15 @@ import os.log Crashlytics.crashlytics().log("Attempting to getDecodedData for: \(key)") let spData = SecureBookmark.getDecodedData(encodedData: urlData) - os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) - Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(key)") // always resolve with just URLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) // bookmarkDataIsStale = true - // a bookmark might be "stale" because the app hasn't been used - // in many months, macOS has been upgraded, the app was - // re-installed, the app's preferences .plist file was deleted, etc. + //a bookmark might be "stale" because the app hasn't been used + //in many months, macOS has been upgraded, the app was + //re-installed, the app's preferences .plist file was deleted, etc. if bookmarkDataIsStale { os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") @@ -213,8 +213,8 @@ import os.log Crashlytics.crashlytics().log("Attempting to getDecodedData for: \(key)") let spData = SecureBookmark.getDecodedData(encodedData: urlData) - os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) - Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(key)") // always resolve with just URLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) @@ -264,8 +264,8 @@ import os.log // need to get the proper URL let spData = SecureBookmark.getDecodedData(encodedData: urlData) - os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, spData.debugDescription) - Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(spData.debugDescription)") + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(key)") // always resolve with just URLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) From a4d97ede5b7aa245449d0df47e3d535fda6d6f81 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 20 Dec 2020 01:05:10 +0800 Subject: [PATCH 27/37] more kaspik feedback addressed --- .../ConnectionView/SPConnectionController.m | 3 --- Source/Controllers/Other/SecureBookmark.swift | 8 +++---- .../Other/SecureBookmarkData.swift | 22 +++++++++---------- .../Other/SecureBookmarkManager.swift | 7 +++--- .../Extensions/UserDefaultsExtension.swift | 2 +- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index 2c0382a65..3e47db327 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -3250,9 +3250,6 @@ - (instancetype)initWithDocument:(SPDatabaseDocument *)document CLS_LOG(@"prefs: %@", prefs.dictionaryRepresentation); - // we need to re-request access to places we've been before.. JCS: do we? - [SecureBookmarkManager.sharedInstance reRequestSecureAccessToBookmarks]; - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(_refreshBookmarks) name:SPBookmarksChangedNotification object:SecureBookmarkManager.sharedInstance]; // Create a reference to the favorites controller, forcing the data to be loaded from disk diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 2c68ebb57..ab16d9e01 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -9,20 +9,20 @@ import Foundation final class SecureBookmark: NSObject { - private let theData: SecureBookmarkData + private let bookmarkData: SecureBookmarkData init(data: Data, options: Double, url: URL) { - theData = SecureBookmarkData(data: data, options: options, url: url) + bookmarkData = SecureBookmarkData(data: data, options: options, url: url) super.init() } func getEncodedData() -> Data { if #available(OSX 10.13, *) { - let codedData = try! NSKeyedArchiver.archivedData(withRootObject: theData, requiringSecureCoding: true) + let codedData = try! NSKeyedArchiver.archivedData(withRootObject: bookmarkData, requiringSecureCoding: true) return codedData } else { // Fallback on earlier versions - let codedData = NSKeyedArchiver.archivedData(withRootObject: theData) + let codedData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) return codedData } } diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index ef78c227f..879aea465 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -9,14 +9,14 @@ import Foundation final class SecureBookmarkData: NSObject { - internal let bookmarkData: Data - internal let options: Double - internal let theUrl: URL + let bookmarkData: Data + private let options: Double + private let bookmarkURL: URL init(data: Data, options: Double, url: URL) { bookmarkData = data self.options = options - theUrl = url + bookmarkURL = url super.init() } } @@ -31,7 +31,7 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { enum Keys: String { case bookmarkData = "BookmarkData" case options = "Options" - case theUrl = "TheUrl" + case bookmarkURL = "BookmarkURL" } func encode(with coder: NSCoder) { @@ -39,12 +39,12 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { // For NSSecureCoding coder.encode(bookmarkData as NSData, forKey: Keys.bookmarkData.rawValue) coder.encode(NSNumber(value: options), forKey: Keys.options.rawValue) - coder.encode(theUrl as NSURL, forKey: Keys.theUrl.rawValue) + coder.encode(bookmarkURL as NSURL, forKey: Keys.bookmarkURL.rawValue) } else { // For NSCoding coder.encode(bookmarkData, forKey: Keys.bookmarkData.rawValue) coder.encode(options, forKey: Keys.options.rawValue) - coder.encode(theUrl, forKey: Keys.theUrl.rawValue) + coder.encode(bookmarkURL, forKey: Keys.bookmarkURL.rawValue) } } @@ -52,14 +52,14 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { if #available(OSX 10.13, *) { let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue)! as Data let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue)! as! Double - let theUrl = coder.decodeObject(of: NSURL.self, forKey: Keys.theUrl.rawValue)! as URL - self.init(data: bookmarkData, options: options, url: theUrl) + let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue)! as URL + self.init(data: bookmarkData, options: options, url: bookmarkURL) } else { // For NSCoding let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data let options = coder.decodeDouble(forKey: Keys.options.rawValue) - let theUrl = coder.decodeObject(forKey: Keys.theUrl.rawValue) as! URL - self.init(data: bookmarkData, options: options, url: theUrl) + let bookmarkURL = coder.decodeObject(forKey: Keys.bookmarkURL.rawValue) as! URL + self.init(data: bookmarkData, options: options, url: bookmarkURL) } } } diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 12e31d383..224995660 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -24,8 +24,8 @@ import os.log @objc var bookmarks: [Dictionary] = [] @objc var staleBookmarks: [String] = [] - @objc private var resolvedBookmarks: [URL] = [] + private var resolvedBookmarks: [URL] = [] private let URLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard @@ -107,7 +107,6 @@ import os.log // always resolve with just URLBookmarkResolutionWithSecurityScope let urlForBookmark = try URL(resolvingBookmarkData: spData.bookmarkData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) -// bookmarkDataIsStale = true //a bookmark might be "stale" because the app hasn't been used //in many months, macOS has been upgraded, the app was //re-installed, the app's preferences .plist file was deleted, etc. @@ -148,7 +147,7 @@ import os.log /// - url: file URL to generate secure bookmark for /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions /// - Returns: Bool on success or fail - @objc public func addBookMarkFor(url: URL, options: UInt) -> Bool { + @objc func addBookMarkFor(url: URL, options: UInt) -> Bool { let bookmarkCreationOptions: URL.BookmarkCreationOptions = URL.BookmarkCreationOptions(rawValue: options) // A file chosen from an NSOpen/SavePanel already has access @@ -198,7 +197,7 @@ import os.log /// - Parameters: /// - filename: file URL to generate secure bookmark for /// - Returns: the resolved URL or nil - @objc public func bookMarkFor(filename: String) -> URL? { + @objc func bookMarkFor(filename: String) -> URL? { os_log("filename %@", log: log, type: .debug, filename) for bookmarkDict in bookmarks { diff --git a/Source/Other/Extensions/UserDefaultsExtension.swift b/Source/Other/Extensions/UserDefaultsExtension.swift index ca676410b..2cc20e82e 100644 --- a/Source/Other/Extensions/UserDefaultsExtension.swift +++ b/Source/Other/Extensions/UserDefaultsExtension.swift @@ -31,7 +31,7 @@ extension UserDefaults { // needs to be objc for KVO @objc var SPSecureBookmarks: [Dictionary] { - return array(forKey: SASecureBookmarks) as? [Dictionary] ?? [["noBookmarks": Data()]] + return array(forKey: SASecureBookmarks) as? [Dictionary] ?? [] } } From b8bf7efb383e3f620a533b2e0d76d78ee53dffd4 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 20 Dec 2020 01:10:04 +0800 Subject: [PATCH 28/37] bit of indenting --- .../Other/SQLiteHistoryManager.swift | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/Source/Controllers/Other/SQLiteHistoryManager.swift b/Source/Controllers/Other/SQLiteHistoryManager.swift index 18be8a7b5..e2e12b279 100644 --- a/Source/Controllers/Other/SQLiteHistoryManager.swift +++ b/Source/Controllers/Other/SQLiteHistoryManager.swift @@ -6,9 +6,9 @@ // Copyright © 2020 Sequel-Ace. All rights reserved. // +import Firebase import Foundation import os.log -import Firebase typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void @@ -37,7 +37,7 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void do { tmpPath = try FileManager.default.applicationSupportDirectory(forSubDirectory: SPDataSupportFolder) } catch { - os_log("Could not get path to applicationSupportDirectory. Error: %@", log: self.log, type: .error, error as CVarArg) + os_log("Could not get path to applicationSupportDirectory. Error: %@", log: log, type: .error, error as CVarArg) Crashlytics.crashlytics().log("Could not get path to applicationSupportDirectory. Error: \(error.localizedDescription)") migratedPrefsToDB = false prefs.set(false, forKey: SPMigratedQueriesFromPrefs) @@ -153,17 +153,16 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void /// Loads the query history from the SQLite database. private func loadQueryHistory() { - - os_log("loading Query History. SPCustomQueryMaxHistoryItems: %i", log: self.log, type: .debug, prefs.integer(forKey: SPCustomQueryMaxHistoryItems)) + os_log("loading Query History. SPCustomQueryMaxHistoryItems: %i", log: log, type: .debug, prefs.integer(forKey: SPCustomQueryMaxHistoryItems)) queue.inDatabase { db in do { db.traceExecution = traceExecution - // select by _rowid_ desc to get latest first, limit to max pref + // select by _rowid_ desc to get latest first, limit to max pref let rs = try db.executeQuery("SELECT rowid, query FROM QueryHistory order by _rowid_ desc LIMIT (?)", values: [prefs.integer(forKey: SPCustomQueryMaxHistoryItems)]) while rs.next() { - queryHist[rs.longLongInt(forColumn: "rowid")] = rs.string(forColumn: "query") + queryHist[rs.longLongInt(forColumn: "rowid")] = rs.string(forColumn: "query") } rs.close() } catch { @@ -213,28 +212,28 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void os_log("migrateQueriesFromPrefs", log: log, type: .debug) - let queryHistoryArray = prefs.stringArray(forKey: SPQueryHistory) ?? [String]() + let queryHistoryArray = prefs.stringArray(forKey: SPQueryHistory) ?? [String]() - // we want to reverse the array from prefs - // prefs is stored by created date asc - // we want to insert in the opposite order - // so that drop down displays by latest created - for query in queryHistoryArray.reversed() where query.isNotEmpty { - os_log("query: [%@]", log: log, type: .debug, query) + // we want to reverse the array from prefs + // prefs is stored by created date asc + // we want to insert in the opposite order + // so that drop down displays by latest created + for query in queryHistoryArray.reversed() where query.isNotEmpty { + os_log("query: [%@]", log: log, type: .debug, query) - let newDate = Date() + let newDate = Date() - os_log("date: %@", log: log, type: .debug, newDate as CVarArg) + os_log("date: %@", log: log, type: .debug, newDate as CVarArg) queue.inDatabase { db in db.traceExecution = traceExecution do { try db.executeUpdate("INSERT OR IGNORE INTO QueryHistory (query, createdTime) VALUES (?, ?)", - values: [query.trimmedString, newDate]) + values: [query.trimmedString, newDate]) } catch { logDBError(error) } - queryHist[db.lastInsertRowId] = query + queryHist[db.lastInsertRowId] = query } } // JCS note: at the moment I'm not deleting the queryHistory key from prefs @@ -242,37 +241,35 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void os_log("migrated prefs query hist to db", log: log, type: .info) migratedPrefsToDB = true prefs.set(true, forKey: SPMigratedQueriesFromPrefs) - reloadQueryHistory() - + reloadQueryHistory() } /// Updates the history. /// - Parameters: /// - newHist: Array of Strings - the Strings being the new history to update /// - Returns: Nothing - @objc func updateQueryHistory(newHist: [String]) { - os_log("updateQueryHistory", log: log, type: .debug) - - // dont delete any history, keep it all? - for query in newHist where query.isNotEmpty { - - let newDate = Date() - - queue.inDatabase { db in - db.traceExecution = traceExecution - do { - try db.executeUpdate("INSERT OR IGNORE INTO QueryHistory (query, createdTime) VALUES (?, ?)", - values: [query.trimmedString, newDate]) - } catch { - logDBError(error) - } - - queryHist[db.lastInsertRowId] = query - } - } - getDBsize() - queue.close() - } + @objc func updateQueryHistory(newHist: [String]) { + os_log("updateQueryHistory", log: log, type: .debug) + + // dont delete any history, keep it all? + for query in newHist where query.isNotEmpty { + let newDate = Date() + + queue.inDatabase { db in + db.traceExecution = traceExecution + do { + try db.executeUpdate("INSERT OR IGNORE INTO QueryHistory (query, createdTime) VALUES (?, ?)", + values: [query.trimmedString, newDate]) + } catch { + logDBError(error) + } + + queryHist[db.lastInsertRowId] = query + } + } + getDBsize() + queue.close() + } /// Deletes all query history from the db @objc func deleteQueryHistory() { @@ -294,7 +291,7 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void /// Executes the vacuum command on the db /// The VACUUM command rebuilds the database file, repacking it into a minimal amount of disk space - @objc func execSQLiteVacuum() { + @objc func execSQLiteVacuum() { os_log("execSQLiteVacuum", log: log, type: .debug) queue.inDatabase { db in @@ -310,7 +307,7 @@ typealias SASchemaBuilder = (_ db: FMDatabase, _ schemaVersion: Int) -> Void /// Handles db fails /// - Parameters: - /// - error: the thrown Error + /// - error: the thrown Error /// - Returns: nothing, should crash func failed(error: Error) { Crashlytics.crashlytics().log("Migration failed: \(error.localizedDescription)") From 98e35a25e9169a14e201350fb40ba53373599e02 Mon Sep 17 00:00:00 2001 From: James Stout Date: Tue, 22 Dec 2020 16:10:26 +0800 Subject: [PATCH 29/37] PR feedback --- .../DataExport/SPExportController.m | 6 +-- .../ConnectionView/SPConnectionController.m | 46 +++++++++---------- .../Other/SecureBookmarkManager.swift | 33 ++++++++----- .../Preferences/Panes/SPFilePreferencePane.m | 4 +- .../Panes/SPNetworkPreferencePane.m | 23 +++++----- 5 files changed, 62 insertions(+), 50 deletions(-) diff --git a/Source/Controllers/DataExport/SPExportController.m b/Source/Controllers/DataExport/SPExportController.m index fa4941943..d7645d7ee 100644 --- a/Source/Controllers/DataExport/SPExportController.m +++ b/Source/Controllers/DataExport/SPExportController.m @@ -623,8 +623,8 @@ - (IBAction)changeExportOutputPath:(id)sender [self->exportPathField setStringValue:path]; // this needs to be read-write - if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->changeExportOutputPathPanel.URL options:(NSURLBookmarkCreationWithSecurityScope)] == YES){ - SPLog(@"addBookMarkForUrl success"); + if([SecureBookmarkManager.sharedInstance addBookmarkForUrl:self->changeExportOutputPathPanel.URL options:(NSURLBookmarkCreationWithSecurityScope)] == YES){ + SPLog(@"addBookmarkForUrl success"); } } }]; @@ -3121,7 +3121,7 @@ - (BOOL)applySettingsFromDictionary:(NSDictionary *)dict error:(NSError **)err NSString *fileURLString = [NSURL fileURLWithPath:[exportPathField stringValue] isDirectory:YES].absoluteString; // ret value can be nil - userChosenDirectory = [SecureBookmarkManager.sharedInstance bookMarkForFilename:fileURLString]; + userChosenDirectory = [SecureBookmarkManager.sharedInstance bookmarkForFilename:fileURLString]; } SPExportType et; diff --git a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m index 3e47db327..e6e3d2556 100644 --- a/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m +++ b/Source/Controllers/MainViewControllers/ConnectionView/SPConnectionController.m @@ -470,8 +470,8 @@ - (IBAction)chooseKeyLocation:(NSButton *)sender NSError *err=nil; // this needs to be read-only to handle keys with 400 perms so we add the bitwise OR NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess - if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->keySelectionPanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ - SPLog(@"addBookMarkForUrl success"); + if([SecureBookmarkManager.sharedInstance addBookmarkForUrl:self->keySelectionPanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookmarkForUrl success"); } // SSH key file selection @@ -3664,27 +3664,27 @@ - (void)dealloc [NSObject cancelPreviousPerformRequestsWithTarget:self]; // Unregister observers - [self removeObserver:self forKeyPath:SPFavoriteTypeKey]; - [self removeObserver:self forKeyPath:SPFavoriteNameKey]; - [self removeObserver:self forKeyPath:SPFavoriteHostKey]; - [self removeObserver:self forKeyPath:SPFavoriteUserKey]; - [self removeObserver:self forKeyPath:SPFavoriteColorIndexKey]; - [self removeObserver:self forKeyPath:SPFavoriteDatabaseKey]; - [self removeObserver:self forKeyPath:SPFavoriteSocketKey]; - [self removeObserver:self forKeyPath:SPFavoritePortKey]; - [self removeObserver:self forKeyPath:SPFavoriteAllowDataLocalInfileKey]; - [self removeObserver:self forKeyPath:SPFavoriteEnableClearTextPluginKey]; - [self removeObserver:self forKeyPath:SPFavoriteUseSSLKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSHHostKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSHUserKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSHPortKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSHKeyLocationEnabledKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSHKeyLocationKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLKeyFileLocationEnabledKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLKeyFileLocationKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationEnabledKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationKey]; - [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationEnabledKey]; + [self removeObserver:self forKeyPath:SPFavoriteTypeKey]; + [self removeObserver:self forKeyPath:SPFavoriteNameKey]; + [self removeObserver:self forKeyPath:SPFavoriteHostKey]; + [self removeObserver:self forKeyPath:SPFavoriteUserKey]; + [self removeObserver:self forKeyPath:SPFavoriteColorIndexKey]; + [self removeObserver:self forKeyPath:SPFavoriteDatabaseKey]; + [self removeObserver:self forKeyPath:SPFavoriteSocketKey]; + [self removeObserver:self forKeyPath:SPFavoritePortKey]; + [self removeObserver:self forKeyPath:SPFavoriteAllowDataLocalInfileKey]; + [self removeObserver:self forKeyPath:SPFavoriteEnableClearTextPluginKey]; + [self removeObserver:self forKeyPath:SPFavoriteUseSSLKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSHHostKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSHUserKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSHPortKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSHKeyLocationEnabledKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSHKeyLocationKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLKeyFileLocationEnabledKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLKeyFileLocationKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationEnabledKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLCertificateFileLocationKey]; + [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationEnabledKey]; [self removeObserver:self forKeyPath:SPFavoriteSSLCACertFileLocationKey]; [self removeObserver:self forKeyPath:SPBookmarksChangedNotification]; diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 224995660..c7949d292 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -34,7 +34,12 @@ import os.log private var iChangedTheBookmarks: Bool = false override init() { - log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "secureBookmarks") + if let bundleID = Bundle.main.bundleIdentifier { + log = OSLog(subsystem: bundleID, category: "secureBookmarks") + } + else{ + log = OSLog.`default` + } os_log("SecureBookmarkManager init.", log: log, type: .info) Crashlytics.crashlytics().log("SecureBookmarkManager init.") @@ -58,8 +63,6 @@ import os.log }) - // FIXME: @Kaspik need help ... need a guard or if let, don't want the default value... - guard let secureBookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]], secureBookmarks.isNotEmpty else { os_log("Could not get secureBookmarks from prefs.", log: log, type: .error) Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") @@ -85,7 +88,7 @@ import os.log // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use // URLBookmarkResolutionWithSecurityScope, not the options it was originally created with // otherwise it will be markes as stale. - @objc func reRequestSecureAccessToBookmarks() { + private func reRequestSecureAccessToBookmarks() { let bmCopy = bookmarks // start afresh @@ -139,15 +142,15 @@ import os.log // reset bookmarks iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) } - /// addBookMark + /// addBookmark /// - Parameters: /// - url: file URL to generate secure bookmark for /// - options: URL.BookmarkCreationOptions. see https://developer.apple.com/documentation/foundation/nsurl/bookmarkcreationoptions /// - Returns: Bool on success or fail - @objc func addBookMarkFor(url: URL, options: UInt) -> Bool { + @objc func addBookmarkFor(url: URL, options: UInt) -> Bool { let bookmarkCreationOptions: URL.BookmarkCreationOptions = URL.BookmarkCreationOptions(rawValue: options) // A file chosen from an NSOpen/SavePanel already has access @@ -178,11 +181,12 @@ import os.log os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") bookmarks.append([url.absoluteString: spData]) + resolvedBookmarks.append(url) os_log("Updating UserDefaults", log: log, type: .debug) Crashlytics.crashlytics().log("Updating UserDefaults") iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) return true @@ -193,11 +197,11 @@ import os.log } } - /// bookMarkFor a file + /// bookmarkFor a file /// - Parameters: /// - filename: file URL to generate secure bookmark for /// - Returns: the resolved URL or nil - @objc func bookMarkFor(filename: String) -> URL? { + @objc func bookmarkFor(filename: String) -> URL? { os_log("filename %@", log: log, type: .debug, filename) for bookmarkDict in bookmarks { @@ -223,6 +227,7 @@ import os.log staleBookmarks.append(key) } else { if urlForBookmark.startAccessingSecurityScopedResource() { + resolvedBookmarks.append(urlForBookmark) return urlForBookmark } else { os_log("Error startAccessingSecurityScopedResource For: key = %@.", log: log, type: .error, urlForBookmark.absoluteString) @@ -274,7 +279,7 @@ import os.log resolvedBookmarks.removeAll(where: { $0 == urlForBookmark }) bookmarks.remove(at: index) iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) os_log("Successfully revoked bookmark for: %@", log: log, type: .debug, filename) Crashlytics.crashlytics().log("Successfully revoked bookmark for: \(filename)") return true @@ -296,6 +301,12 @@ import os.log // revoke secure access to all bookmarks @objc func stopAllSecurityScopedAccess() { + + os_log("stopAllSecurityScopedAccess called", log: log, type: .debug) + Crashlytics.crashlytics().log("stopAllSecurityScopedAccess called") + Crashlytics.crashlytics().log("resolvedBookmarks count = \(resolvedBookmarks.count)") + os_log("resolvedBookmarks count = %i", log: log, type: .info, resolvedBookmarks.count) + for url in resolvedBookmarks { resolvedBookmarks.removeAll(where: { $0 == url }) url.stopAccessingSecurityScopedResource() diff --git a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m index 166bc5d5d..4f911dcc9 100644 --- a/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPFilePreferencePane.m @@ -217,8 +217,8 @@ - (void) chooseFile // read-only. [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ - if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ - SPLog(@"addBookMarkForUrl success"); + if([SecureBookmarkManager.sharedInstance addBookmarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookmarkForUrl success"); } }]; diff --git a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m index 62eb8f2d6..d40ba5234 100644 --- a/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m +++ b/Source/Controllers/Preferences/Panes/SPNetworkPreferencePane.m @@ -219,22 +219,23 @@ - (void)updateSSHConfigPopUp [[sshConfigChooser menu] addItem:[NSMenuItem separatorItem]]; NSUInteger __block count = 0; - + + NSUInteger len = [@"file://" length]; + // iterate through all bookmarks in order to display them as menu items [bookmarks enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) { NSEnumerator *keyEnumerator = [dict keyEnumerator]; id key; // every bookmark is saved in relation to it's abslute path - while (key = [keyEnumerator nextObject]) { - - if([key hasPrefixWithPrefix:@"file://" caseSensitive:YES] == YES){ - // save the filename without the file protocol - NSString *itemTitle = [key substringFromIndex:[@"file://" length]]; - [sshConfigChooser addItemWithTitle:itemTitle]; - count++; + while (key = [keyEnumerator nextObject]) { + if([key hasPrefixWithPrefix:@"file://" caseSensitive:YES] != YES){ + continue; } - } + NSString *itemTitle = [key substringFromIndex:len]; + [sshConfigChooser addItemWithTitle:itemTitle]; + count++; + } }]; // default value if no bookmarks are available @@ -302,8 +303,8 @@ - (void) chooseSSHConfig [self->_currentFilePanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idxURL, BOOL *stopURL){ // check if the file is out of the sandbox - if([SecureBookmarkManager.sharedInstance addBookMarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ - SPLog(@"addBookMarkForUrl success"); + if([SecureBookmarkManager.sharedInstance addBookmarkForUrl:self->_currentFilePanel.URL options:(NSURLBookmarkCreationWithSecurityScope|NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess)] == YES){ + SPLog(@"addBookmarkForUrl success"); } // set the config path to the first selected file From be65388c7f3bd6b27c6bc2b4d487584d2b043e07 Mon Sep 17 00:00:00 2001 From: James Stout Date: Tue, 22 Dec 2020 16:40:49 +0800 Subject: [PATCH 30/37] doesn't work ... --- Source/Controllers/Other/SecureBookmark.swift | 41 ++++++++++++++++--- .../Other/SecureBookmarkData.swift | 33 +++++++++++---- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index ab16d9e01..99c20d5c9 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -7,10 +7,16 @@ // import Foundation +import os.log +import Firebase final class SecureBookmark: NSObject { private let bookmarkData: SecureBookmarkData + static var emptySucureBookmarkData: SecureBookmarkData{ + return SecureBookmarkData(data: Data(), options: 0, url: URL(string: "nil")!) + } + init(data: Data, options: Double, url: URL) { bookmarkData = SecureBookmarkData(data: data, options: options, url: url) super.init() @@ -18,9 +24,17 @@ final class SecureBookmark: NSObject { func getEncodedData() -> Data { if #available(OSX 10.13, *) { - let codedData = try! NSKeyedArchiver.archivedData(withRootObject: bookmarkData, requiringSecureCoding: true) - return codedData - } else { + do{ + let codedData = try NSKeyedArchiver.archivedData(withRootObject: bookmarkData, requiringSecureCoding: true) + return codedData + } + catch{ + os_log("Failed to encode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + Crashlytics.crashlytics().log("Failed to encode data, Error: \(error.localizedDescription)") + return Data() // hmmmmmmm JCS. + } + } + else { // Fallback on earlier versions let codedData = NSKeyedArchiver.archivedData(withRootObject: bookmarkData) return codedData @@ -29,10 +43,27 @@ final class SecureBookmark: NSObject { class func getDecodedData(encodedData: Data) -> SecureBookmarkData { if #available(OSX 10.13, *) { - return try! NSKeyedUnarchiver.unarchivedObject(ofClass: SecureBookmarkData.self, from: encodedData)! + do{ + let retData = try NSKeyedUnarchiver.unarchivedObject(ofClass: SecureBookmarkData.self, from: encodedData) ?? emptySucureBookmarkData + return retData + } + catch{ + os_log("Failed to decode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + Crashlytics.crashlytics().log("Failed to decode data, Error: \(error.localizedDescription)") + return emptySucureBookmarkData + } } else { // Fallback on earlier versions - return try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as! SecureBookmarkData + do{ + let retData = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as? SecureBookmarkData ?? emptySucureBookmarkData + return retData + } + catch{ + os_log("Failed to decode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + Crashlytics.crashlytics().log("Failed to encode data, Error: \(error.localizedDescription)") + return emptySucureBookmarkData + } } } } + diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index 879aea465..d5b4444e6 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -26,6 +26,10 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { return true } + static var decodingFailurePolicy: NSCoder.DecodingFailurePolicy{ + return NSCoder.DecodingFailurePolicy.setErrorAndReturn + } + // MARK: NSCoding Implementation enum Keys: String { @@ -50,15 +54,30 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { convenience init?(coder: NSCoder) { if #available(OSX 10.13, *) { - let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue)! as Data - let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue)! as! Double - let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue)! as URL - self.init(data: bookmarkData, options: options, url: bookmarkURL) - } else { + guard let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.options.rawValue) else { + coder.failWithError(CocoaError.error(.coderValueNotFound)) + return nil + } + guard let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue) else { + coder.failWithError(CocoaError.error(.coderValueNotFound)) + return nil + } + guard let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue) else { + coder.failWithError(CocoaError.error(.coderValueNotFound)) + return nil + } + self.init(data: bookmarkData as Data, options: Double(truncating: options), url: bookmarkURL as URL) + } + else { // For NSCoding - let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as! Data + guard let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as? Data else { + return nil + } + guard let bookmarkURL = coder.decodeObject(forKey: Keys.bookmarkURL.rawValue) as? URL else { + return nil + } let options = coder.decodeDouble(forKey: Keys.options.rawValue) - let bookmarkURL = coder.decodeObject(forKey: Keys.bookmarkURL.rawValue) as! URL + self.init(data: bookmarkData, options: options, url: bookmarkURL) } } From b38122bbb4c9e16860c703036506e11078c2d681 Mon Sep 17 00:00:00 2001 From: James Stout Date: Tue, 22 Dec 2020 16:42:43 +0800 Subject: [PATCH 31/37] doesn't work as expected either --- Source/Controllers/SPAppController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Controllers/SPAppController.m b/Source/Controllers/SPAppController.m index cf5339f45..e7469203b 100755 --- a/Source/Controllers/SPAppController.m +++ b/Source/Controllers/SPAppController.m @@ -222,7 +222,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification [self->prefsController showWindow:self]; [self->prefsController displayPreferencePane:self->prefsController->fileItem]; - [NSAlert createWarningAlertWithTitle:NSLocalizedString(@"Stale Bookmarks", @"Stale Bookmarks") message:[NSString stringWithFormat:NSLocalizedString(@"A reminder of your stale secure bookmarks:\n\n%@\n", @"A reminder of your stale secure bookmarks:\n\n%@\n"), staleBookmarksString] callback:nil]; + [self->prefsController.window makeKeyAndOrderFront:nil]; } cancelButtonHandler:^{ SPLog(@"No not now"); From 24d9e817d712f49b4cffb35a1e4ecf3c4af5d168 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 27 Dec 2020 18:43:05 +0800 Subject: [PATCH 32/37] combine guards (per @kaspik) --- .../Other/SecureBookmarkData.swift | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index d5b4444e6..ccebd55df 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -54,15 +54,11 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { convenience init?(coder: NSCoder) { if #available(OSX 10.13, *) { - guard let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.options.rawValue) else { - coder.failWithError(CocoaError.error(.coderValueNotFound)) - return nil - } - guard let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue) else { - coder.failWithError(CocoaError.error(.coderValueNotFound)) - return nil - } - guard let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue) else { + guard + let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.options.rawValue), + let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue), + let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue) + else { coder.failWithError(CocoaError.error(.coderValueNotFound)) return nil } @@ -70,12 +66,13 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { } else { // For NSCoding - guard let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as? Data else { - return nil - } - guard let bookmarkURL = coder.decodeObject(forKey: Keys.bookmarkURL.rawValue) as? URL else { + guard + let bookmarkData = coder.decodeObject(forKey: Keys.bookmarkData.rawValue) as? Data, + let bookmarkURL = coder.decodeObject(forKey: Keys.bookmarkURL.rawValue) as? URL + else { return nil } + let options = coder.decodeDouble(forKey: Keys.options.rawValue) self.init(data: bookmarkData, options: options, url: bookmarkURL) From 3d2483e69e7a4de84a546d070999d0c67e8a53c9 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 27 Dec 2020 19:28:05 +0800 Subject: [PATCH 33/37] ahhh! --- Source/Controllers/Other/SecureBookmarkData.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Controllers/Other/SecureBookmarkData.swift b/Source/Controllers/Other/SecureBookmarkData.swift index ccebd55df..d1443e1ed 100644 --- a/Source/Controllers/Other/SecureBookmarkData.swift +++ b/Source/Controllers/Other/SecureBookmarkData.swift @@ -55,7 +55,7 @@ extension SecureBookmarkData: NSCoding, NSSecureCoding { convenience init?(coder: NSCoder) { if #available(OSX 10.13, *) { guard - let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.options.rawValue), + let bookmarkData = coder.decodeObject(of: NSData.self, forKey: Keys.bookmarkData.rawValue), let options = coder.decodeObject(of: NSNumber.self, forKey: Keys.options.rawValue), let bookmarkURL = coder.decodeObject(of: NSURL.self, forKey: Keys.bookmarkURL.rawValue) else { From 97d3184dfd9e3b6f9351f18a21cae961eef733da Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 27 Dec 2020 20:48:42 +0800 Subject: [PATCH 34/37] Further feedback from @kaspik --- Source/Controllers/Other/SecureBookmark.swift | 4 ++-- .../Other/SecureBookmarkManager.swift | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 99c20d5c9..1abdb97f1 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -22,7 +22,7 @@ final class SecureBookmark: NSObject { super.init() } - func getEncodedData() -> Data { + func getEncodedData() -> Data? { if #available(OSX 10.13, *) { do{ let codedData = try NSKeyedArchiver.archivedData(withRootObject: bookmarkData, requiringSecureCoding: true) @@ -31,7 +31,7 @@ final class SecureBookmark: NSObject { catch{ os_log("Failed to encode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) Crashlytics.crashlytics().log("Failed to encode data, Error: \(error.localizedDescription)") - return Data() // hmmmmmmm JCS. + return nil } } else { diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index c7949d292..0509147b7 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -50,8 +50,8 @@ import os.log observer = UserDefaults.standard.observe(\.SPSecureBookmarks, options: [.new, .old], changeHandler: { _, change in if self.iChangedTheBookmarks == false { - os_log("SPSecureBookmarks changed NOT by SecureBookmarkManager.", log: self.log, type: .debug) - Crashlytics.crashlytics().log("SPSecureBookmarks changed NOT by SecureBookmarkManager.") + os_log("SPSecureBookmarks changed, but NOT by SecureBookmarkManager.", log: self.log, type: .debug) + Crashlytics.crashlytics().log("SPSecureBookmarks changed, but NOT by SecureBookmarkManager.") self.bookmarks.removeAll() self.bookmarks = change.newValue! } @@ -142,7 +142,7 @@ import os.log // reset bookmarks iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) } /// addBookmark @@ -176,7 +176,11 @@ import os.log os_log("Attempting to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") - let spData = sp.getEncodedData() + guard let spData = sp.getEncodedData() else { + os_log("Failed to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) + Crashlytics.crashlytics().log("Failed to getEncodedData for: \(sp.debugDescription)") + return false + } os_log("SUCCESS: Adding %@ to bookmarks", log: log, type: .debug, url.absoluteString) Crashlytics.crashlytics().log("SUCCESS: Adding \(url.absoluteString) to bookmarks") @@ -186,7 +190,7 @@ import os.log os_log("Updating UserDefaults", log: log, type: .debug) Crashlytics.crashlytics().log("Updating UserDefaults") iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) return true @@ -279,7 +283,7 @@ import os.log resolvedBookmarks.removeAll(where: { $0 == urlForBookmark }) bookmarks.remove(at: index) iChangedTheBookmarks = true - prefs.set(bookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SASecureBookmarks) os_log("Successfully revoked bookmark for: %@", log: log, type: .debug, filename) Crashlytics.crashlytics().log("Successfully revoked bookmark for: \(filename)") return true From 0cce102aa13ecab0a004567e8df7f1ef17ed5241 Mon Sep 17 00:00:00 2001 From: James Stout Date: Sun, 27 Dec 2020 20:56:01 +0800 Subject: [PATCH 35/37] display a window with a reminder of their stale bookmarks --- .../Localization/en.lproj/Localizable.strings | 5 +- .../SPBundleHTMLOutputController.m | 12 ++-- Source/Controllers/SPAppController.h | 3 +- Source/Controllers/SPAppController.m | 57 ++++++++++++++++++- Source/Other/Data/SPConstants.h | 1 + Source/Other/Data/SPConstants.m | 3 +- 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/Resources/Localization/en.lproj/Localizable.strings b/Resources/Localization/en.lproj/Localizable.strings index f719c1a59..f790d7e30 100644 --- a/Resources/Localization/en.lproj/Localizable.strings +++ b/Resources/Localization/en.lproj/Localizable.strings @@ -3187,4 +3187,7 @@ "Yes" = "Yes"; /* Shown to remind users of their stale bookmarks */ -"A reminder of your stale secure bookmarks:\n\n%@\n" = "A reminder of your stale secure bookmarks:\n\n%@\n"; +"A reminder of your stale secure bookmarks:

%@
" = "A reminder of your stale secure bookmarks:

%@
"; + +/* Title for Stale Secure Bookmarks help window */ +"Stale Secure Bookmarks" = "Stale Secure Bookmarks"; diff --git a/Source/Controllers/BundleSupport/SPBundleHTMLOutputController.m b/Source/Controllers/BundleSupport/SPBundleHTMLOutputController.m index ed79f8b86..5269c5c1f 100644 --- a/Source/Controllers/BundleSupport/SPBundleHTMLOutputController.m +++ b/Source/Controllers/BundleSupport/SPBundleHTMLOutputController.m @@ -94,9 +94,12 @@ - (void)displayHTMLContent:(NSString *)content withOptions:(NSDictionary *)displ { [[self window] orderFront:nil]; // [[self window] makeKeyAndOrderFront:nil]; - - // only do this if invoked with SPConnectionShownSocketHelp = YES in the displayOptions - if(displayOptions.count > 0 && [[displayOptions objectForKey:SPConnectionShownSocketHelp] boolValue] == YES){ + + BOOL shownSocketHelp = [[displayOptions objectForKey:SPConnectionShownSocketHelp] boolValue]; + BOOL staleBookmarksHelp = [[displayOptions objectForKey:SPStaleBookmarksHelp] boolValue]; + + // only do this if invoked with SPConnectionShownSocketHelp = YES or SPStaleBookmarksHelp == YES in the displayOptions + if(displayOptions.count > 0 && (shownSocketHelp == YES || staleBookmarksHelp == YES )){ if([displayOptions objectForKey:@"frame"] != nil){ SPLog(@"Changing frame rect"); @@ -106,7 +109,8 @@ - (void)displayHTMLContent:(NSString *)content withOptions:(NSDictionary *)displ // save the current frame for restore origFrame = CGRectMake(tmpFrame.origin.x,tmpFrame.origin.y, tmpFrame.size.width, tmpFrame.size.height ); restoreFrame = YES; - windowType = SPConnectionShownSocketHelp; + windowType = (shownSocketHelp == YES) ? SPConnectionShownSocketHelp : ((staleBookmarksHelp == YES) ? SPStaleBookmarksHelp : @"" ); + SPLog(@"windowType: %@", windowType); // set the new wider frame [[self window] setFrame:CGRectMake([frameDict[@"x"] doubleValue], [frameDict[@"y"] doubleValue], [frameDict[@"w"] doubleValue], [frameDict[@"h"] doubleValue]) display:YES]; diff --git a/Source/Controllers/SPAppController.h b/Source/Controllers/SPAppController.h index aeb65aede..a33e31e6e 100755 --- a/Source/Controllers/SPAppController.h +++ b/Source/Controllers/SPAppController.h @@ -85,7 +85,8 @@ - (void)setSpfSessionDocData:(NSDictionary *)data; // Others - +- (NSString *)generateHTML:(NSString *)theTitle theHTML:(NSString *)someHTML; +- (void)displayStaleBookmarkReminderWindow:(NSString *)staleBookmarksString; - (void)registerActivity:(NSDictionary *)commandDict; - (void)removeRegisteredActivity:(NSInteger)pid; - (NSArray *)runningActivities; diff --git a/Source/Controllers/SPAppController.m b/Source/Controllers/SPAppController.m index 670bc9a72..f97e7514e 100755 --- a/Source/Controllers/SPAppController.m +++ b/Source/Controllers/SPAppController.m @@ -52,6 +52,8 @@ #import "PSMTabBarControl.h" #import "SPFunctions.h" #import "SPBundleManager.h" +#import "MGTemplateEngine.h" +#import "ICUTemplateMatcher.h" #import "sequel-ace-Swift.h" @@ -217,12 +219,14 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification message:[NSString stringWithFormat:NSLocalizedString(@"You have stale secure bookmarks:\n\n%@\n\nWould you like to re-request access now?", @"Would you like to re-request access now?"), staleBookmarksString] primaryButtonTitle:NSLocalizedString(@"Yes", @"Yes") primaryButtonHandler:^{ + SPLog(@"re-request access now"); [self->prefsController showWindow:self]; [self->prefsController displayPreferencePane:self->prefsController->fileItem]; - - [self->prefsController.window makeKeyAndOrderFront:nil]; - + + // display a window with a reminder of their stale bookmarks + [self displayStaleBookmarkReminderWindow:staleBookmarksString]; + } cancelButtonHandler:^{ SPLog(@"No not now"); }]; @@ -282,6 +286,53 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification } } +- (void)displayStaleBookmarkReminderWindow:(NSString *)staleBookmarksString { + + // display a window with a reminder of their stale bookmarks + NSDictionary *displayOptionsDict = @{SPStaleBookmarksHelp : @YES, @"frame" : @{@"x" : @225, @"y" : @536, @"w" : @400, @"h" : @300}}; + SPBundleHTMLOutputController *bundleController = [[SPBundleHTMLOutputController alloc] init]; + NSString *inputHTML = [NSString stringWithFormat:NSLocalizedString(@"A reminder of your stale secure bookmarks:

%@
", @"A reminder of your stale secure bookmarks:

%@
"), staleBookmarksString]; + NSString *outputHTML = [self generateHTML:NSLocalizedString(@"Stale Secure Bookmarks",@"Title for Stale Secure Bookmarks help window" ) theHTML:inputHTML]; + [bundleController displayHTMLContent:outputHTML withOptions:displayOptionsDict]; + [SPBundleManager.sharedSPBundleManager addHTMLOutputController:bundleController]; + +} + +- (NSString *)generateHTML:(NSString *)theTitle theHTML:(NSString *)someHTML { + + MGTemplateEngine *engine = [[MGTemplateEngine alloc] init]; + [engine setMatcher:[ICUTemplateMatcher matcherWithTemplateEngine:engine]]; + + NSError *error; + + NSString *helpHTMLTemplate = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:SPHTMLHelpTemplate ofType:@"html"] + encoding:NSUTF8StringEncoding + error:&error]; + + if (helpHTMLTemplate == nil) { + helpHTMLTemplate = @"{{body}}"; //fallback + SPLog(@"%@", [NSString stringWithFormat:@"Error reading “%@.html”!
%@", SPHTMLHelpTemplate, [error localizedFailureReason]]); + } + + NSString *addBodyClass = @""; + // Add CSS class if running in dark UI mode (10.14+) + if (@available(macOS 10.14, *)) { + NSString *match = [[[self frontDocumentWindow] effectiveAppearance] bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]]; + // aqua is already the default theme + if ([NSAppearanceNameDarkAqua isEqualToString:match]) { + addBodyClass = @"dark"; + } + } + + return [engine processTemplate:helpHTMLTemplate withVariables:@{ + @"bodyClass": addBodyClass, + @"title": theTitle, + @"body": someHTML, + }]; +} + + + - (void)externalApplicationWantsToOpenADatabaseConnection:(NSNotification *)notification { NSDictionary *userInfo = [notification userInfo]; diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 47d71ff19..9f88185c2 100755 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -529,6 +529,7 @@ extern NSString *SPFavoriteSSLCACertFileLocationKey; extern NSString *SPFavoriteUseCompressionKey; extern NSString *SPConnectionFavoritesChangedNotification; extern NSString *SPConnectionShownSocketHelp; +extern NSString *SPStaleBookmarksHelp; extern NSString *SPFFormatKey; extern NSString *SPFVersionKey; diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 435f3240a..a291ba152 100755 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -341,7 +341,8 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPFavoriteSSLCACertFileLocationKey = @"sslCACertFileLocation"; NSString *SPFavoriteUseCompressionKey = @"useCompression"; NSString *SPConnectionFavoritesChangedNotification = @"SPConnectionFavoritesChanged"; -NSString *SPConnectionShownSocketHelp = @"SPConnectionShownSocketHelp"; +NSString *SPConnectionShownSocketHelp = @"SPConnectionShownSocketHelp"; +NSString *SPStaleBookmarksHelp = @"SPStaleBookmarksHelp"; NSString *SPFFormatKey = @"format"; NSString *SPFVersionKey = @"version"; From d0e6eb058fff9290c6108c96ced0349aa611999e Mon Sep 17 00:00:00 2001 From: James Stout Date: Tue, 29 Dec 2020 18:36:59 +0800 Subject: [PATCH 36/37] format --- Source/Controllers/Other/SecureBookmark.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmark.swift b/Source/Controllers/Other/SecureBookmark.swift index 1abdb97f1..73c7489cc 100644 --- a/Source/Controllers/Other/SecureBookmark.swift +++ b/Source/Controllers/Other/SecureBookmark.swift @@ -6,9 +6,9 @@ // Copyright © 2020 Sequel-Ace. All rights reserved. // +import Firebase import Foundation import os.log -import Firebase final class SecureBookmark: NSObject { private let bookmarkData: SecureBookmarkData @@ -29,7 +29,7 @@ final class SecureBookmark: NSObject { return codedData } catch{ - os_log("Failed to encode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + os_log("Failed to encode data, Error: %@", log: OSLog.default, type: .error, error.localizedDescription) Crashlytics.crashlytics().log("Failed to encode data, Error: \(error.localizedDescription)") return nil } @@ -48,7 +48,7 @@ final class SecureBookmark: NSObject { return retData } catch{ - os_log("Failed to decode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + os_log("Failed to decode data, Error: %@", log: OSLog.default, type: .error, error.localizedDescription) Crashlytics.crashlytics().log("Failed to decode data, Error: \(error.localizedDescription)") return emptySucureBookmarkData } @@ -59,7 +59,7 @@ final class SecureBookmark: NSObject { return retData } catch{ - os_log("Failed to decode data, Error: %@", log: OSLog.`default`, type: .error, error.localizedDescription) + os_log("Failed to decode data, Error: %@", log: OSLog.default, type: .error, error.localizedDescription) Crashlytics.crashlytics().log("Failed to encode data, Error: \(error.localizedDescription)") return emptySucureBookmarkData } From 8731ca5bc22947c22164be171e91b07e0be35545 Mon Sep 17 00:00:00 2001 From: James Stout Date: Tue, 29 Dec 2020 18:37:16 +0800 Subject: [PATCH 37/37] added migrateBookmarks --- .../Other/SecureBookmarkManager.swift | 104 +++++++++++++++++- Source/Other/Data/SPConstants.h | 2 + Source/Other/Data/SPConstants.m | 4 +- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/Source/Controllers/Other/SecureBookmarkManager.swift b/Source/Controllers/Other/SecureBookmarkManager.swift index 0509147b7..34eb44f76 100644 --- a/Source/Controllers/Other/SecureBookmarkManager.swift +++ b/Source/Controllers/Other/SecureBookmarkManager.swift @@ -27,18 +27,21 @@ import os.log private var resolvedBookmarks: [URL] = [] private let URLBookmarkResolutionWithSecurityScope = URL.BookmarkResolutionOptions(rawValue: 1 << 10) + private let URLBookmarkCreationWithSecurityScope = URL.BookmarkCreationOptions(rawValue: 1 << 11) + private let log: OSLog private let prefs: UserDefaults = UserDefaults.standard private var observer: NSKeyValueObservation? private var iChangedTheBookmarks: Bool = false + private var bookmarksHaveBeenMigrated: Bool = false override init() { if let bundleID = Bundle.main.bundleIdentifier { log = OSLog(subsystem: bundleID, category: "secureBookmarks") } else{ - log = OSLog.`default` + log = OSLog.default } os_log("SecureBookmarkManager init.", log: log, type: .info) @@ -73,8 +76,19 @@ import os.log os_log("bookmarks = %@", log: log, type: .info, bookmarks) - // I think part of init of this manager should be to re-request access - reRequestSecureAccessToBookmarks() + let bookmarksHaveBeenMigrated = prefs.bool(forKey: SPSecureBookmarksHaveBeenMigrated) + os_log("bookmarksHaveBeenMigrated: %d", log: log, type: .debug, bookmarksHaveBeenMigrated) + + if bookmarksHaveBeenMigrated == false { + if migrateBookmarks() == true { + // after migration, re-request. + reRequestSecureAccessToBookmarks() + } + } + else { + // I think part of init of this manager should be to re-request access + reRequestSecureAccessToBookmarks() + } os_log("resolvedBookmarks count = %i", log: log, type: .info, resolvedBookmarks.count) os_log("staleBookmarks count = %i", log: log, type: .info, staleBookmarks.count) @@ -83,12 +97,96 @@ import os.log Crashlytics.crashlytics().log("staleBookmarks count = \(staleBookmarks.count)") } + /// migrateBookmarks + // load up old bookmarks into array + // loop though resolving them to URLS + // Attempt to create secure bookmark + // Attempt to create SecureBookmark object + // Attempt to getEncodedData for object + // Add to bookmarks and save to prefs + private func migrateBookmarks() -> Bool { + os_log("migrating old format Bookmarks", log: log, type: .debug) + + var migratedBookmarks: [Dictionary] = [] + + for bookmarkDict in bookmarks { + for (key, urlData) in bookmarkDict { + os_log("Bookmark URL = %@", log: log, type: .info, key) + + var bookmarkDataIsStale = false + + do { + os_log("Attempting to resolve bookmark data for %@", log: log, type: .debug, key) + Crashlytics.crashlytics().log("Attempting to resolve bookmark data for: \(key)") + // always resolve with just URLBookmarkResolutionWithSecurityScope + let urlForBookmark = try URL(resolvingBookmarkData: urlData, options: [URLBookmarkResolutionWithSecurityScope], relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale) + + if bookmarkDataIsStale { + os_log("The bookmark is outdated and needs to be regenerated: key = %@", log: log, type: .error, key) + Crashlytics.crashlytics().log("The bookmark is outdated and needs to be regenerated: key = \(key)") + staleBookmarks.append(key) + continue + } else { + os_log("Resolved bookmark: %@", log: log, type: .info, key) + Crashlytics.crashlytics().log("Resolved bookmark: \(key)") + } + + os_log("Attempting to create SecureBookmark object for %@", log: log, type: .debug, urlForBookmark.absoluteString) + Crashlytics.crashlytics().log("Attempting to create SecureBookmark object for: \(urlForBookmark.absoluteString)") + // NSURLBookmarkCreationWithSecurityScope is a guess .. could be read only, BUT read only fails to resolve! so it has to be securityScope + let sp = SecureBookmark(data: urlData, options: Double(URLBookmarkCreationWithSecurityScope.rawValue), url: urlForBookmark) + + os_log("Attempting to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) + Crashlytics.crashlytics().log("Attempting getEncodedData for: \(sp.debugDescription)") + + guard let spData = sp.getEncodedData() else { + os_log("Failed to getEncodedData for %@", log: log, type: .debug, sp.debugDescription) + Crashlytics.crashlytics().log("Failed to getEncodedData for: \(sp.debugDescription)") + staleBookmarks.append(key) + continue + } + + os_log("SUCCESS: Migrated %@", log: log, type: .debug, urlForBookmark.absoluteString) + Crashlytics.crashlytics().log("SUCCESS: Migrated: \(urlForBookmark.absoluteString)") + migratedBookmarks.append([urlForBookmark.absoluteString: spData]) + + } + catch { + staleBookmarks.append(key) + os_log("Error resolving bookmark: key = %@. Error: %@", log: log, type: .error, key, error.localizedDescription) + Crashlytics.crashlytics().log("Error resolving bookmark: key = \(key). Error: \(error.localizedDescription)") + continue + } + } + } + + bookmarksHaveBeenMigrated = true + iChangedTheBookmarks = true + + // update prefs - keep a copy of the old bookmark data for the moment + prefs.set(true, forKey: SPSecureBookmarksHaveBeenMigrated) + prefs.set(migratedBookmarks, forKey: SASecureBookmarks) + prefs.set(bookmarks, forKey: SPSecureBookmarksOldFormat) // backup + + return true + } + /// reRequestSecureAccessToBookmarks // loops through current bookmarks from prefs and re-requests secure access // NOTE: when re-requesting access (resolvingBookmarkData) you only need to use // URLBookmarkResolutionWithSecurityScope, not the options it was originally created with // otherwise it will be markes as stale. private func reRequestSecureAccessToBookmarks() { + + // re-read - must do this after migration, could put it in an if, but it only happens once per app start. + guard let secureBookmarks = prefs.array(forKey: SASecureBookmarks) as? [[String: Data]], secureBookmarks.isNotEmpty else { + os_log("Could not get secureBookmarks from prefs.", log: log, type: .error) + Crashlytics.crashlytics().log("Could not get secureBookmarks from prefs.") + return + } + + bookmarks = secureBookmarks + let bmCopy = bookmarks // start afresh diff --git a/Source/Other/Data/SPConstants.h b/Source/Other/Data/SPConstants.h index 62ad59ef0..71fa4d561 100755 --- a/Source/Other/Data/SPConstants.h +++ b/Source/Other/Data/SPConstants.h @@ -425,6 +425,8 @@ extern NSString *SPFileName24HourTimeTokenName; extern NSString *SPFileNameFavoriteTokenName; extern NSString *SPFileNameTableTokenName; extern NSString *SASecureBookmarks; +extern NSString *SPSecureBookmarksOldFormat; +extern NSString *SPSecureBookmarksHaveBeenMigrated; // Misc extern NSString *SPContentFilters; diff --git a/Source/Other/Data/SPConstants.m b/Source/Other/Data/SPConstants.m index 22d179f5e..27b024bf9 100755 --- a/Source/Other/Data/SPConstants.m +++ b/Source/Other/Data/SPConstants.m @@ -218,7 +218,9 @@ - (SPTableViewType)tableViewTypeEnumFromString{ NSString *SPCSVFieldImportMappingAlignment = @"CSVFieldImportMappingAlignment"; NSString *SPImportClipboardTempFileNamePrefix = @"~/tmp/_SP_ClipBoard_Import_File_"; NSString *SPLastExportSettings = @"LastExportSettings"; -NSString *SASecureBookmarks = @"SPSecureBookmarks"; +NSString *SASecureBookmarks = @"SPSecureBookmarks"; +NSString *SPSecureBookmarksOldFormat = @"SPSecureBookmarksOldFormat"; +NSString *SPSecureBookmarksHaveBeenMigrated = @"SPSecureBookmarksHaveBeenMigrated"; // Export filename tokens NSString *SPFileNameDatabaseTokenName = @"database"; NSString *SPFileNameHostTokenName = @"host";