From 6b28cc659ee5668d688b1a5701fbc4ef939dd4ff Mon Sep 17 00:00:00 2001 From: Christoffer Winterkvist Date: Mon, 8 Oct 2018 20:37:47 +0200 Subject: [PATCH] Implement handling of restricted applications - Checks if SIP is enabled - Queries the plist to check readability - Adds `restricted` to Application - Adds flow for opening system preferences and giving Gray Full Disk Access --- Resources/Info.plist | 2 +- .../Data Sources/ApplicationsDataSource.swift | 13 ++++++++ .../Logic/ApplicationsLogicController.swift | 14 +++++--- Sources/Applications/Models/Application.swift | 1 + ...ApplicationsCollectionViewController.swift | 32 ++++++++++++++++--- .../Views/ApplicationGridView.swift | 6 ---- .../Logic/SystemLogicController.swift | 2 -- 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Resources/Info.plist b/Resources/Info.plist index 8b10a6a..79cb198 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.7.0 + 0.8.0 CFBundleVersion 1 LSApplicationCategoryType diff --git a/Sources/Applications/Data Sources/ApplicationsDataSource.swift b/Sources/Applications/Data Sources/ApplicationsDataSource.swift index cfd7428..398caf8 100644 --- a/Sources/Applications/Data Sources/ApplicationsDataSource.swift +++ b/Sources/Applications/Data Sources/ApplicationsDataSource.swift @@ -46,6 +46,19 @@ class ApplicationsDataSource: NSObject, NSCollectionViewDataSource { view.iconView.image = self.iconController.icon(for: model) view.titleLabel.stringValue = model.name view.update(with: model.appearance) + + switch model.appearance { + case .dark: + view.subtitleLabel.stringValue = "Dark appearance" + case .light: + view.subtitleLabel.stringValue = "Light appearance" + case .system: + view.subtitleLabel.stringValue = "System appearance" + } + + if model.restricted { + view.subtitleLabel.stringValue = "🔐 Locked" + } } } } diff --git a/Sources/Applications/Logic/ApplicationsLogicController.swift b/Sources/Applications/Logic/ApplicationsLogicController.swift index 8f0b266..5593cb8 100644 --- a/Sources/Applications/Logic/ApplicationsLogicController.swift +++ b/Sources/Applications/Logic/ApplicationsLogicController.swift @@ -88,7 +88,8 @@ class ApplicationsLogicController { private func processApplications(_ appUrls: [URL], at directoryUrl: URL) throws -> [Application] { var applications = [Application]() - + let shell = Shell() + let sip = shell.execute(command: "csrutil status").contains("enabled") let libraryDirectory = try FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, @@ -113,20 +114,25 @@ class ApplicationsLogicController { var resolvedAppPreferenceUrl = appPreferenceUrl var applicationPlist: NSDictionary? = nil - if let plist = NSDictionary.init(contentsOfFile: appContainerPreferenceUrl.path) { + if FileManager.default.fileExists(atPath: appContainerPreferenceUrl.path), + let plist = NSDictionary.init(contentsOfFile: appContainerPreferenceUrl.path) { applicationPlist = plist resolvedAppPreferenceUrl = appContainerPreferenceUrl } else if let plist = NSDictionary.init(contentsOfFile: appPreferenceUrl.path) { applicationPlist = plist } - guard let resolvedPlist = applicationPlist else { continue } + // Check if Gray has enough priviliges to change appearance for application + let restricted = sip && + FileManager.default.fileExists(atPath: appContainerPreferenceUrl.path) && + NSDictionary.init(contentsOfFile: appContainerPreferenceUrl.path) == nil let app = Application(bundleIdentifier: bundleIdentifier, name: bundleName, url: url, preferencesUrl: resolvedAppPreferenceUrl, - appearance: resolvedPlist.appearance()) + appearance: applicationPlist?.appearance() ?? .system, + restricted: restricted) applications.append(app) } return applications.sorted(by: { $0.name.lowercased() < $1.name.lowercased() }) diff --git a/Sources/Applications/Models/Application.swift b/Sources/Applications/Models/Application.swift index 426a53e..a1ce9ec 100644 --- a/Sources/Applications/Models/Application.swift +++ b/Sources/Applications/Models/Application.swift @@ -12,4 +12,5 @@ struct Application: Hashable { let url: URL let preferencesUrl: URL let appearance: Appearance + let restricted: Bool } diff --git a/Sources/Applications/View Controllers/ApplicationsCollectionViewController.swift b/Sources/Applications/View Controllers/ApplicationsCollectionViewController.swift index 5c958ae..19debc6 100644 --- a/Sources/Applications/View Controllers/ApplicationsCollectionViewController.swift +++ b/Sources/Applications/View Controllers/ApplicationsCollectionViewController.swift @@ -37,6 +37,19 @@ class ApplicationsCollectionViewController: NSViewController, NSCollectionViewDe view.addSubview(collectionView, pin: true) } + private func showPermissionsDialog(for application: Application, handler completion : (Bool)->Void) { + let alert = NSAlert() + alert.messageText = "Additional privileges needed" + alert.informativeText = """ + To be able to change the appearance of apps like Mail, Messages, Safari and Home, you need to grant permission Full Disk Access. + + """ + alert.alertStyle = .informational + alert.addButton(withTitle: "Open Security & Preferences") + alert.addButton(withTitle: "OK") + completion(alert.runModal() == .alertFirstButtonReturn) + } + // MARK: - NSCollectionViewDelegate func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { @@ -68,11 +81,20 @@ class ApplicationsCollectionViewController: NSViewController, NSCollectionViewDe context.duration = duration context.allowsImplicitAnimation = true item.view.animator().layer?.setAffineTransform(.identity) - item.update(with: newAppearance, duration: 0.5) { [weak self] in - guard let strongSelf = self else { return } - strongSelf.delegate?.applicationCollectionViewController(strongSelf, - toggleAppearance: newAppearance, - application: application) + }, completionHandler: { + if application.restricted { + self.showPermissionsDialog(for: application) { result in + guard result else { return } + let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")! + NSWorkspace.shared.open(url) + } + } else { + item.update(with: newAppearance, duration: 0.5) { [weak self] in + guard let strongSelf = self else { return } + strongSelf.delegate?.applicationCollectionViewController(strongSelf, + toggleAppearance: newAppearance, + application: application) + } } }) }) diff --git a/Sources/Applications/Views/ApplicationGridView.swift b/Sources/Applications/Views/ApplicationGridView.swift index 0fb4e51..3c73c38 100644 --- a/Sources/Applications/Views/ApplicationGridView.swift +++ b/Sources/Applications/Views/ApplicationGridView.swift @@ -79,19 +79,16 @@ class ApplicationGridView: NSCollectionViewItem { view.animator().layer?.backgroundColor = NSColor(named: "Dark")?.cgColor titleLabel.animator().textColor = .white subtitleLabel.animator().textColor = .controlAccentColor - subtitleLabel.animator().stringValue = "Dark appearance" view.layer?.borderWidth = 0.0 case .system: view.animator().layer?.backgroundColor = NSColor.gray.cgColor titleLabel.animator().textColor = .white subtitleLabel.animator().textColor = .lightGray - subtitleLabel.animator().stringValue = "System appearance" view.layer?.borderWidth = 0.0 case .light: view.animator().layer?.backgroundColor = .white titleLabel.animator().textColor = .black subtitleLabel.animator().textColor = .controlAccentColor - subtitleLabel.animator().stringValue = "Light appearance" view.layer?.borderColor = NSColor.gray.withAlphaComponent(0.25).cgColor view.layer?.borderWidth = 1.5 } @@ -104,13 +101,11 @@ class ApplicationGridView: NSCollectionViewItem { view.layer?.backgroundColor = NSColor(named: "Dark")?.cgColor titleLabel.textColor = .white subtitleLabel.textColor = .controlAccentColor - subtitleLabel.stringValue = "Dark appearance" view.layer?.borderWidth = 0.0 case .light: view.layer?.backgroundColor = NSColor(named: "Light")?.cgColor titleLabel.textColor = .black subtitleLabel.textColor = .controlAccentColor - subtitleLabel.stringValue = "Light appearance" view.layer?.borderColor = NSColor.gray.withAlphaComponent(0.25).cgColor view.layer?.borderWidth = 1.5 case .system: @@ -129,7 +124,6 @@ class ApplicationGridView: NSCollectionViewItem { default: break } - subtitleLabel.stringValue = "System appearance" } } } diff --git a/Sources/SystemPreferences/Logic/SystemLogicController.swift b/Sources/SystemPreferences/Logic/SystemLogicController.swift index 595fb71..0cdb968 100644 --- a/Sources/SystemPreferences/Logic/SystemLogicController.swift +++ b/Sources/SystemPreferences/Logic/SystemLogicController.swift @@ -74,8 +74,6 @@ class SystemLogicController { procNotFound: if retryOnInternalError { self.requestPermission(retryOnInternalError: false, then: process) - } else { - } default: break