diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index 08b27bd97..21edfcbe5 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -72,6 +72,14 @@ public final class AccountManager: UnreadCountProvider { return CombinedRefreshProgress(downloadProgressArray: downloadProgressArray) } + public convenience init() { + let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String + let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) + let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString + let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7)) + self.init(accountsFolder: String(accountsFolderPath)) + } + public init(accountsFolder: String) { self.accountsFolder = accountsFolder diff --git a/Frameworks/Account/Credentials/CredentialsManager.swift b/Frameworks/Account/Credentials/CredentialsManager.swift index 6ac291cca..b52b6c30c 100644 --- a/Frameworks/Account/Credentials/CredentialsManager.swift +++ b/Frameworks/Account/Credentials/CredentialsManager.swift @@ -10,6 +10,15 @@ import Foundation public struct CredentialsManager { + private static var keychainGroup: String? = { + guard let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String else { + return nil + } + let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String + let appGroupSuffix = appGroup.suffix(appGroup.count - 6) + return "\(appIdentifierPrefix)\(appGroupSuffix)" + }() + public static func storeCredentials(_ credentials: Credentials, server: String) throws { var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword, @@ -20,6 +29,10 @@ public struct CredentialsManager { query[kSecAttrSecurityDomain as String] = credentials.type.rawValue } + if let securityGroup = keychainGroup { + query[kSecAttrAccessGroup as String] = securityGroup + } + let secretData = credentials.secret.data(using: String.Encoding.utf8)! let attributes: [String: Any] = [kSecValueData as String: secretData] let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) @@ -59,6 +72,10 @@ public struct CredentialsManager { query[kSecAttrSecurityDomain as String] = type.rawValue } + if let securityGroup = keychainGroup { + query[kSecAttrAccessGroup as String] = securityGroup + } + var item: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &item) @@ -93,6 +110,10 @@ public struct CredentialsManager { query[kSecAttrSecurityDomain as String] = type.rawValue } + if let securityGroup = keychainGroup { + query[kSecAttrAccessGroup as String] = securityGroup + } + let status = SecItemDelete(query as CFDictionary) guard status == errSecSuccess || status == errSecItemNotFound else { throw CredentialsError.unhandledError(status: status) diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 79b2a0854..2bce61ac6 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -55,12 +55,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele // Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views let _ = DetailViewControllerWebViewProvider.shared - - let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.\(Bundle.main.bundleIdentifier!)") - let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString - let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7)) - AccountManager.shared = AccountManager(accountsFolder: String(accountsFolderPath)) - + AccountManager.shared = AccountManager() AppDefaults.shared = UserDefaults.init(suiteName: "group.\(Bundle.main.bundleIdentifier!)")! registerBackgroundTasks() diff --git a/iOS/Resources/Info.plist b/iOS/Resources/Info.plist index cee7acd46..c5fa02fdd 100644 --- a/iOS/Resources/Info.plist +++ b/iOS/Resources/Info.plist @@ -2,6 +2,10 @@ + AppGroup + group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + AppIdentifierPrefix + $(AppIdentifierPrefix) BGTaskSchedulerPermittedIdentifiers com.ranchero.NetNewsWire.FeedRefresh diff --git a/iOS/Resources/NetNewsWire.entitlements b/iOS/Resources/NetNewsWire.entitlements index d6b369115..05d04e805 100644 --- a/iOS/Resources/NetNewsWire.entitlements +++ b/iOS/Resources/NetNewsWire.entitlements @@ -6,5 +6,9 @@ group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + keychain-access-groups + + $(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + diff --git a/iOS/ShareExtension/Info.plist b/iOS/ShareExtension/Info.plist index 2ecaf1743..d25defda1 100644 --- a/iOS/ShareExtension/Info.plist +++ b/iOS/ShareExtension/Info.plist @@ -2,6 +2,10 @@ + AppGroup + group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + AppIdentifierPrefix + $(AppIdentifierPrefix) CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements b/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements index d6b369115..05d04e805 100644 --- a/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements +++ b/iOS/ShareExtension/NetNewsWire_iOS_ShareExtension.entitlements @@ -6,5 +6,9 @@ group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + keychain-access-groups + + $(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS + diff --git a/iOS/ShareExtension/ShareViewController.swift b/iOS/ShareExtension/ShareViewController.swift index 0fd4550f8..f3b21e2ec 100644 --- a/iOS/ShareExtension/ShareViewController.swift +++ b/iOS/ShareExtension/ShareViewController.swift @@ -23,11 +23,7 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont override func viewDidLoad() { - let rootID = Bundle.main.bundleIdentifier!.replacingOccurrences(of: ".Share-Extension", with: "") - let accountsURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.\(rootID)") - let accountsFolder = accountsURL!.appendingPathComponent("Accounts").absoluteString - let accountsFolderPath = accountsFolder.suffix(from: accountsFolder.index(accountsFolder.startIndex, offsetBy: 7)) - AccountManager.shared = AccountManager(accountsFolder: String(accountsFolderPath)) + AccountManager.shared = AccountManager() pickerData = FlattenedAccountFolderPickerData()