Implement keychain groups
This commit is contained in:
parent
0e12f0192a
commit
20fa5d9e48
|
@ -72,6 +72,14 @@ public final class AccountManager: UnreadCountProvider {
|
||||||
return CombinedRefreshProgress(downloadProgressArray: downloadProgressArray)
|
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) {
|
public init(accountsFolder: String) {
|
||||||
self.accountsFolder = accountsFolder
|
self.accountsFolder = accountsFolder
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,15 @@ import Foundation
|
||||||
|
|
||||||
public struct CredentialsManager {
|
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 {
|
public static func storeCredentials(_ credentials: Credentials, server: String) throws {
|
||||||
|
|
||||||
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
|
||||||
|
@ -20,6 +29,10 @@ public struct CredentialsManager {
|
||||||
query[kSecAttrSecurityDomain as String] = credentials.type.rawValue
|
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 secretData = credentials.secret.data(using: String.Encoding.utf8)!
|
||||||
let attributes: [String: Any] = [kSecValueData as String: secretData]
|
let attributes: [String: Any] = [kSecValueData as String: secretData]
|
||||||
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
||||||
|
@ -59,6 +72,10 @@ public struct CredentialsManager {
|
||||||
query[kSecAttrSecurityDomain as String] = type.rawValue
|
query[kSecAttrSecurityDomain as String] = type.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let securityGroup = keychainGroup {
|
||||||
|
query[kSecAttrAccessGroup as String] = securityGroup
|
||||||
|
}
|
||||||
|
|
||||||
var item: CFTypeRef?
|
var item: CFTypeRef?
|
||||||
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
let status = SecItemCopyMatching(query as CFDictionary, &item)
|
||||||
|
|
||||||
|
@ -93,6 +110,10 @@ public struct CredentialsManager {
|
||||||
query[kSecAttrSecurityDomain as String] = type.rawValue
|
query[kSecAttrSecurityDomain as String] = type.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let securityGroup = keychainGroup {
|
||||||
|
query[kSecAttrAccessGroup as String] = securityGroup
|
||||||
|
}
|
||||||
|
|
||||||
let status = SecItemDelete(query as CFDictionary)
|
let status = SecItemDelete(query as CFDictionary)
|
||||||
guard status == errSecSuccess || status == errSecItemNotFound else {
|
guard status == errSecSuccess || status == errSecItemNotFound else {
|
||||||
throw CredentialsError.unhandledError(status: status)
|
throw CredentialsError.unhandledError(status: status)
|
||||||
|
|
|
@ -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
|
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
|
||||||
let _ = DetailViewControllerWebViewProvider.shared
|
let _ = DetailViewControllerWebViewProvider.shared
|
||||||
|
AccountManager.shared = AccountManager()
|
||||||
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))
|
|
||||||
|
|
||||||
AppDefaults.shared = UserDefaults.init(suiteName: "group.\(Bundle.main.bundleIdentifier!)")!
|
AppDefaults.shared = UserDefaults.init(suiteName: "group.\(Bundle.main.bundleIdentifier!)")!
|
||||||
|
|
||||||
registerBackgroundTasks()
|
registerBackgroundTasks()
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>AppGroup</key>
|
||||||
|
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
|
<key>AppIdentifierPrefix</key>
|
||||||
|
<string>$(AppIdentifierPrefix)</string>
|
||||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.ranchero.NetNewsWire.FeedRefresh</string>
|
<string>com.ranchero.NetNewsWire.FeedRefresh</string>
|
||||||
|
|
|
@ -6,5 +6,9 @@
|
||||||
<array>
|
<array>
|
||||||
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>AppGroup</key>
|
||||||
|
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
|
<key>AppIdentifierPrefix</key>
|
||||||
|
<string>$(AppIdentifierPrefix)</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
|
|
|
@ -6,5 +6,9 @@
|
||||||
<array>
|
<array>
|
||||||
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -23,11 +23,7 @@ class ShareViewController: SLComposeServiceViewController, ShareFolderPickerCont
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
let rootID = Bundle.main.bundleIdentifier!.replacingOccurrences(of: ".Share-Extension", with: "")
|
AccountManager.shared = AccountManager()
|
||||||
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))
|
|
||||||
|
|
||||||
pickerData = FlattenedAccountFolderPickerData()
|
pickerData = FlattenedAccountFolderPickerData()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue