diff --git a/README.md b/README.md index ec64b193..bd235923 100644 --- a/README.md +++ b/README.md @@ -125,17 +125,29 @@ NSString *const myLuggageCombination = [myValet stringForKey:username error:nil] In addition to allowing the storage of strings, Valet allows the storage of `Data` objects via `setObject(_ object: Data, forKey key: Key)` and `object(forKey key: String)`. Valets created with a different class type, via a different initializer, or with a different accessibility attribute will not be able to read or modify values in `myValet`. -### Sharing Secrets Among Multiple Applications +### Sharing Secrets Among Multiple Applications Using a Keychain Sharing Entitlement ```swift -let mySharedValet = Valet.sharedAccessGroupValet(with: SharedAccessGroupIdentifier(appIDPrefix: "AppID12345", nonEmptyGroup: "Druidia")!, accessibility: .whenUnlocked) +let mySharedValet = Valet.sharedGroupValet(with: SharedGroupIdentifier(appIDPrefix: "AppID12345", nonEmptyGroup: "Druidia")!, accessibility: .whenUnlocked) ``` ```objc -VALValet *const mySharedValet = [VALValet sharedAccessGroupValetWithAppIDPrefix:@"AppID12345" sharedAccessGroupIdentifier:@"Druidia" accessibility:VALAccessibilityWhenUnlocked]; +VALValet *const mySharedValet = [VALValet sharedGroupValetWithAppIDPrefix:@"AppID12345" sharedGroupIdentifier:@"Druidia" accessibility:VALAccessibilityWhenUnlocked]; ``` -This instance can be used to store and retrieve data securely across any app written by the same developer that has `AppID12345.Druidia` (or `$(AppIdentifierPrefix)Druidia`) set as a value for the `keychain-access-groups` key in the app’s `Entitlements`, where `AppID12345` is the application’s [App ID prefix](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps#2974920). This Valet is accessible when the device is unlocked. Note that `myValet` and `mySharedValet` can not read or modify one another’s values because the two Valets were created with different initializers. All Valet types can share secrets across applications written by the same developer by using the `sharedAccessGroupValet` initializer. +This instance can be used to store and retrieve data securely across any app written by the same developer that has `AppID12345.Druidia` (or `$(AppIdentifierPrefix)Druidia`) set as a value for the `keychain-access-groups` key in the app’s `Entitlements`, where `AppID12345` is the application’s [App ID prefix](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps#2974920). This Valet is accessible when the device is unlocked. Note that `myValet` and `mySharedValet` can not read or modify one another’s values because the two Valets were created with different initializers. All Valet types can share secrets across applications written by the same developer by using the `sharedGroupValet` initializer. + +### Sharing Secrets Among Multiple Applications Using an App Groups Entitlement + +```swift +let mySharedValet = Valet.sharedGroupValet(with: SharedGroupIdentifier(groupPrefix: "group", nonEmptyGroup: "Druidia")!, accessibility: .whenUnlocked) +``` + +```objc +VALValet *const mySharedValet = [VALValet sharedGroupValetWithGroupPrefix:@"group" sharedGroupIdentifier:@"Druidia" accessibility:VALAccessibilityWhenUnlocked]; +``` + +This instance can be used to store and retrieve data securely across any app written by the same developer that has `group.Druidia` set as a value for the `com.apple.security.application-groups` key in the app’s `Entitlements`. This Valet is accessible when the device is unlocked. Note that `myValet` and `mySharedValet` cannot read or modify one another’s values because the two Valets were created with different initializers. All Valet types can share secrets across applications written by the same developer by using the `sharedGroupValet` initializer. Note that on macOS, the `groupPrefix` [must be the App ID prefix](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups#discussion). ### Sharing Secrets Across Devices with iCloud diff --git a/Sources/Valet/Internal/Service.swift b/Sources/Valet/Internal/Service.swift index fce1572b..016048d7 100644 --- a/Sources/Valet/Internal/Service.swift +++ b/Sources/Valet/Internal/Service.swift @@ -23,11 +23,11 @@ import Foundation internal enum Service: CustomStringConvertible, Equatable { case standard(Identifier, Configuration) - case sharedAccessGroup(SharedAccessGroupIdentifier, Configuration) + case sharedGroup(SharedGroupIdentifier, Configuration) #if os(macOS) case standardOverride(service: Identifier, Configuration) - case sharedAccessGroupOverride(service: SharedAccessGroupIdentifier, Configuration) + case sharedGroupOverride(service: SharedGroupIdentifier, Configuration) #endif // MARK: Equatable @@ -48,11 +48,11 @@ internal enum Service: CustomStringConvertible, Equatable { "VAL_\(configuration.description)_initWithIdentifier:accessibility:_\(identifier)_\(accessibilityDescription)" } - internal static func sharedAccessGroup(with configuration: Configuration, identifier: SharedAccessGroupIdentifier, accessibilityDescription: String) -> String { + internal static func sharedGroup(with configuration: Configuration, identifier: SharedGroupIdentifier, accessibilityDescription: String) -> String { "VAL_\(configuration.description)_initWithSharedAccessGroupIdentifier:accessibility:_\(identifier.groupIdentifier)_\(accessibilityDescription)" } - internal static func sharedAccessGroup(with configuration: Configuration, explicitlySetIdentifier identifier: Identifier, accessibilityDescription: String) -> String { + internal static func sharedGroup(with configuration: Configuration, explicitlySetIdentifier identifier: Identifier, accessibilityDescription: String) -> String { "VAL_\(configuration.description)_initWithSharedAccessGroupIdentifier:accessibility:_\(identifier)_\(accessibilityDescription)" } @@ -73,7 +73,7 @@ internal enum Service: CustomStringConvertible, Equatable { case let .standard(_, desiredConfiguration): configuration = desiredConfiguration - case let .sharedAccessGroup(identifier, desiredConfiguration): + case let .sharedGroup(identifier, desiredConfiguration): baseQuery[kSecAttrAccessGroup as String] = identifier.description configuration = desiredConfiguration @@ -81,7 +81,7 @@ internal enum Service: CustomStringConvertible, Equatable { case let .standardOverride(_, desiredConfiguration): configuration = desiredConfiguration - case let .sharedAccessGroupOverride(identifier, desiredConfiguration): + case let .sharedGroupOverride(identifier, desiredConfiguration): baseQuery[kSecAttrAccessGroup as String] = identifier.description configuration = desiredConfiguration #endif @@ -111,19 +111,19 @@ internal enum Service: CustomStringConvertible, Equatable { switch self { case let .standard(identifier, configuration): service = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: configuration.accessibility.description) - case let .sharedAccessGroup(identifier, configuration): - service = Service.sharedAccessGroup(with: configuration, identifier: identifier, accessibilityDescription: configuration.accessibility.description) + case let .sharedGroup(identifier, configuration): + service = Service.sharedGroup(with: configuration, identifier: identifier, accessibilityDescription: configuration.accessibility.description) #if os(macOS) case let .standardOverride(identifier, _): service = identifier.description - case let .sharedAccessGroupOverride(identifier, _): + case let .sharedGroupOverride(identifier, _): service = identifier.groupIdentifier #endif } switch self { case let .standard(_, configuration), - let .sharedAccessGroup(_, configuration): + let .sharedGroup(_, configuration): switch configuration { case .valet, .iCloud: // Nothing to do here. @@ -138,7 +138,7 @@ internal enum Service: CustomStringConvertible, Equatable { #if os(macOS) case .standardOverride, - .sharedAccessGroupOverride: + .sharedGroupOverride: return service #endif } diff --git a/Sources/Valet/SecureEnclave.swift b/Sources/Valet/SecureEnclave.swift index 4d80c3b8..d8be2908 100644 --- a/Sources/Valet/SecureEnclave.swift +++ b/Sources/Valet/SecureEnclave.swift @@ -39,11 +39,11 @@ public final class SecureEnclave { case let .standard(identifier, _): noPromptValet = .valet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) #if os(macOS) - case let .sharedAccessGroupOverride(identifier, _): - noPromptValet = .sharedAccessGroupValet(withExplicitlySet: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) + case let .sharedGroupOverride(identifier, _): + noPromptValet = .sharedGroupValet(withExplicitlySet: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) #endif - case let .sharedAccessGroup(identifier, _): - noPromptValet = .sharedAccessGroupValet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) + case let .sharedGroup(identifier, _): + noPromptValet = .sharedGroupValet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) } return noPromptValet.canAccessKeychain() diff --git a/Sources/Valet/SecureEnclaveValet.swift b/Sources/Valet/SecureEnclaveValet.swift index a711ae3a..7ea6a602 100644 --- a/Sources/Valet/SecureEnclaveValet.swift +++ b/Sources/Valet/SecureEnclaveValet.swift @@ -47,8 +47,8 @@ public final class SecureEnclaveValet: NSObject { /// - identifier: A non-empty string that must correspond with the value for keychain-access-groups in your Entitlements file. /// - accessControl: The desired access control for the SecureEnclaveValet. /// - Returns: A SecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. - public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet { - let key = Service.sharedAccessGroup(identifier, .secureEnclave(accessControl)).description as NSString + public class func sharedGroupValet(with identifier: SharedGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet { + let key = Service.sharedGroup(identifier, .secureEnclave(accessControl)).description as NSString if let existingValet = identifierToValetMap.object(forKey: key) { return existingValet @@ -84,10 +84,10 @@ public final class SecureEnclaveValet: NSObject { accessControl: accessControl) } - private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) { + private convenience init(sharedAccess groupIdentifier: SharedGroupIdentifier, accessControl: SecureEnclaveAccessControl) { self.init( identifier: groupIdentifier.asIdentifier, - service: .sharedAccessGroup(groupIdentifier, .secureEnclave(accessControl)), + service: .sharedGroup(groupIdentifier, .secureEnclave(accessControl)), accessControl: accessControl) } @@ -254,12 +254,26 @@ extension SecureEnclaveValet { /// - accessControl: The desired access control for the SecureEnclaveValet. /// - Returns: A SecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps - @objc(sharedAccessGroupValetWithAppIDPrefix:sharedAccessGroupIdentifier:accessControl:) - public class func 🚫swift_sharedAccessGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(sharedGroupValetWithAppIDPrefix:sharedGroupIdentifier:accessControl:) + public class func 🚫swift_sharedGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { return nil } - return sharedAccessGroupValet(with: identifier, accessControl: accessControl) + return sharedGroupValet(with: identifier, accessControl: accessControl) + } + + /// - Parameters: + /// - groupPrefix: On iOS, iPadOS, watchOS, and tvOS, this prefix must equal "group". On macOS, this prefix is the application's App ID prefix, which can be found by inspecting the application's provisioning profile, or viewing the application's App ID Configuration on developer.apple.com. This string must not be empty. + /// - identifier: An identifier that corresponds to a value in com.apple.security.application-groups in the application's Entitlements file. This string must not be empty. + /// - accessControl: The desired access control for the SecureEnclaveValet. + /// - Returns: A SecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. + /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps + @objc(sharedGroupValetWithGroupPrefix:sharedGroupIdentifier:accessControl:) + public class func 🚫swift_sharedGroupValet(groupPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet? { + guard let identifier = SharedGroupIdentifier(groupPrefix: groupPrefix, nonEmptyGroup: identifier) else { + return nil + } + return sharedGroupValet(with: identifier, accessControl: accessControl) } /// - Parameter key: The key to look up in the keychain. diff --git a/Sources/Valet/SharedAccessGroupIdentifier.swift b/Sources/Valet/SharedGroupIdentifier.swift similarity index 56% rename from Sources/Valet/SharedAccessGroupIdentifier.swift rename to Sources/Valet/SharedGroupIdentifier.swift index 0dbc3f92..a8973df8 100644 --- a/Sources/Valet/SharedAccessGroupIdentifier.swift +++ b/Sources/Valet/SharedGroupIdentifier.swift @@ -1,5 +1,5 @@ // -// SharedAccessGroupIdentifier.swift +// SharedGroupIdentifier.swift // Valet // // Created by Dan Federman on 2/25/20. @@ -21,7 +21,7 @@ import Foundation -public struct SharedAccessGroupIdentifier: CustomStringConvertible { +public struct SharedGroupIdentifier: CustomStringConvertible { // MARK: Initialization @@ -35,23 +35,47 @@ public struct SharedAccessGroupIdentifier: CustomStringConvertible { return nil } - self.appIDPrefix = appIDPrefix + self.prefix = appIDPrefix + self.groupIdentifier = groupIdentifier + } + + /// A representation of a shared app group identifier. + /// - Parameters: + /// - groupPrefix: On iOS, iPadOS, watchOS, and tvOS, this prefix must equal "group". On macOS, this prefix is the application's App ID prefix, which can be found by inspecting the application's provisioning profile, or viewing the application's App ID Configuration on developer.apple.com. This string must not be empty. + /// - groupIdentifier: An identifier that corresponds to a value in com.apple.security.application-groups in the application's Entitlements file. This string must not be empty. + /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps + public init?(groupPrefix: String, nonEmptyGroup groupIdentifier: String?) { + #if os(macOS) + guard !groupPrefix.isEmpty, let groupIdentifier = groupIdentifier, !groupIdentifier.isEmpty else { + return nil + } + #else + guard groupPrefix == Self.appGroupPrefix, let groupIdentifier = groupIdentifier, !groupIdentifier.isEmpty else { + return nil + } + #endif + + self.prefix = groupPrefix self.groupIdentifier = groupIdentifier } // MARK: CustomStringConvertible public var description: String { - return appIDPrefix + "." + groupIdentifier + prefix + "." + groupIdentifier } // MARK: Internal Properties - internal let appIDPrefix: String + internal let prefix: String internal let groupIdentifier: String internal var asIdentifier: Identifier { // It is safe to force unwrap because we've already validated that our description is non-empty. Identifier(nonEmpty: description)! } + + // MARK: Private Static Properties + + private static let appGroupPrefix = "group" } diff --git a/Sources/Valet/SinglePromptSecureEnclaveValet.swift b/Sources/Valet/SinglePromptSecureEnclaveValet.swift index ec3f6d3b..53bd00f5 100644 --- a/Sources/Valet/SinglePromptSecureEnclaveValet.swift +++ b/Sources/Valet/SinglePromptSecureEnclaveValet.swift @@ -51,8 +51,8 @@ public final class SinglePromptSecureEnclaveValet: NSObject { /// - identifier: A non-empty identifier that must correspond with the value for keychain-access-groups in your Entitlements file. /// - accessControl: The desired access control for the SinglePromptSecureEnclaveValet. /// - Returns: A SinglePromptSecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. - public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet { - let key = Service.sharedAccessGroup(identifier, .singlePromptSecureEnclave(accessControl)).description as NSString + public class func sharedGroupValet(with identifier: SharedGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet { + let key = Service.sharedGroup(identifier, .singlePromptSecureEnclave(accessControl)).description as NSString if let existingValet = identifierToValetMap.object(forKey: key) { return existingValet @@ -88,10 +88,10 @@ public final class SinglePromptSecureEnclaveValet: NSObject { accessControl: accessControl) } - private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) { + private convenience init(sharedAccess groupIdentifier: SharedGroupIdentifier, accessControl: SecureEnclaveAccessControl) { self.init( identifier: groupIdentifier.asIdentifier, - service: .sharedAccessGroup(groupIdentifier, .singlePromptSecureEnclave(accessControl)), + service: .sharedGroup(groupIdentifier, .singlePromptSecureEnclave(accessControl)), accessControl: accessControl) } @@ -295,12 +295,26 @@ extension SinglePromptSecureEnclaveValet { /// - accessControl: The desired access control for the SinglePromptSecureEnclaveValet. /// - Returns: A SinglePromptSecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps - @objc(sharedAccessGroupValetWithAppIDPrefix:sharedAccessGroupIdentifier:accessControl:) - public class func 🚫swift_sharedAccessGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(sharedGroupValetWithAppIDPrefix:sharedGroupIdentifier:accessControl:) + public class func 🚫swift_sharedGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { return nil } - return sharedAccessGroupValet(with: identifier, accessControl: accessControl) + return sharedGroupValet(with: identifier, accessControl: accessControl) + } + + /// - Parameters: + /// - groupPrefix: On iOS, iPadOS, watchOS, and tvOS, this prefix must equal "group". On macOS, this prefix is the application's App ID prefix, which can be found by inspecting the application's provisioning profile, or viewing the application's App ID Configuration on developer.apple.com. This string must not be empty. + /// - identifier: An identifier that corresponds to a value in com.apple.security.application-groups in the application's Entitlements file. This string must not be empty. + /// - accessControl: The desired access control for the SinglePromptSecureEnclaveValet. + /// - Returns: A SinglePromptSecureEnclaveValet that reads/writes keychain elements that can be shared across applications written by the same development team. + /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps + @objc(sharedGroupValetWithGroupPrefix:sharedGroupIdentifier:accessControl:) + public class func 🚫swift_sharedGroupValet(groupPrefix: String, nonEmptyIdentifier identifier: String, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet? { + guard let identifier = SharedGroupIdentifier(groupPrefix: groupPrefix, nonEmptyGroup: identifier) else { + return nil + } + return sharedGroupValet(with: identifier, accessControl: accessControl) } /// - Parameter key: The key to look up in the keychain. diff --git a/Sources/Valet/Valet.swift b/Sources/Valet/Valet.swift index fad2e99b..addc6978 100644 --- a/Sources/Valet/Valet.swift +++ b/Sources/Valet/Valet.swift @@ -47,7 +47,7 @@ public final class Valet: NSObject { /// - identifier: The identifier for the Valet's shared access group. Must correspond with the value for keychain-access-groups in your Entitlements file. /// - accessibility: The desired accessibility for the Valet. /// - Returns: A Valet that reads/writes keychain elements that can be shared across applications written by the same development team. - public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessibility: Accessibility) -> Valet { + public class func sharedGroupValet(with identifier: SharedGroupIdentifier, accessibility: Accessibility) -> Valet { findOrCreate(identifier, configuration: .valet(accessibility)) } @@ -55,7 +55,7 @@ public final class Valet: NSObject { /// - identifier: The identifier for the Valet's shared access group. Must correspond with the value for keychain-access-groups in your Entitlements file. /// - accessibility: The desired accessibility for the Valet. /// - Returns: A Valet (synchronized with iCloud) that reads/writes keychain elements that can be shared across applications written by the same development team. - public class func iCloudSharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessibility: CloudAccessibility) -> Valet { + public class func iCloudSharedGroupValet(with identifier: SharedGroupIdentifier, accessibility: CloudAccessibility) -> Valet { findOrCreate(identifier, configuration: .iCloud(accessibility)) } @@ -89,7 +89,7 @@ public final class Valet: NSObject { /// - Returns: A Valet that reads/writes keychain elements that can be shared across applications written by the same development team. /// - Warning: Using an explicitly set kSecAttrService bypasses this project’s guarantee that one Valet type will not have access to one another type’s key:value pairs. To maintain this guarantee, ensure that each Valet’s identifier is globally unique. /// - SeeAlso: https://github.com/square/Valet/issues/140 - public class func sharedAccessGroupValet(withExplicitlySet identifier: SharedAccessGroupIdentifier, accessibility: Accessibility) -> Valet { + public class func sharedGroupValet(withExplicitlySet identifier: SharedGroupIdentifier, accessibility: Accessibility) -> Valet { findOrCreate(explicitlySet: identifier, configuration: .valet(accessibility)) } @@ -100,7 +100,7 @@ public final class Valet: NSObject { /// - Returns: A Valet (synchronized with iCloud) that reads/writes keychain elements that can be shared across applications written by the same development team. /// - Warning: Using an explicitly set kSecAttrService bypasses this project’s guarantee that one Valet type will not have access to one another type’s key:value pairs. To maintain this guarantee, ensure that each Valet’s identifier is globally unique. /// - SeeAlso: https://github.com/square/Valet/issues/140 - public class func iCloudSharedAccessGroupValet(withExplicitlySet identifier: SharedAccessGroupIdentifier, accessibility: CloudAccessibility) -> Valet { + public class func iCloudSharedGroupValet(withExplicitlySet identifier: SharedGroupIdentifier, accessibility: CloudAccessibility) -> Valet { findOrCreate(explicitlySet: identifier, configuration: .iCloud(accessibility)) } #endif @@ -131,8 +131,8 @@ public final class Valet: NSObject { } } - private class func findOrCreate(_ identifier: SharedAccessGroupIdentifier, configuration: Configuration) -> Valet { - let service: Service = .sharedAccessGroup(identifier, configuration) + private class func findOrCreate(_ identifier: SharedGroupIdentifier, configuration: Configuration) -> Valet { + let service: Service = .sharedGroup(identifier, configuration) let key = service.description as NSString if let existingValet = identifierToValetMap.object(forKey: key) { return existingValet @@ -159,8 +159,8 @@ public final class Valet: NSObject { } } - private class func findOrCreate(explicitlySet identifier: SharedAccessGroupIdentifier, configuration: Configuration) -> Valet { - let service: Service = .sharedAccessGroupOverride(service: identifier, configuration) + private class func findOrCreate(explicitlySet identifier: SharedGroupIdentifier, configuration: Configuration) -> Valet { + let service: Service = .sharedGroupOverride(service: identifier, configuration) let key = service.description + configuration.description + configuration.accessibility.description + identifier.description as NSString if let existingValet = identifierToValetMap.object(forKey: key) { return existingValet @@ -188,10 +188,10 @@ public final class Valet: NSObject { configuration: configuration) } - private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, configuration: Configuration) { + private convenience init(sharedAccess groupIdentifier: SharedGroupIdentifier, configuration: Configuration) { self.init( identifier: groupIdentifier.asIdentifier, - service: .sharedAccessGroup(groupIdentifier, configuration), + service: .sharedGroup(groupIdentifier, configuration), configuration: configuration) } @@ -212,10 +212,10 @@ public final class Valet: NSObject { baseKeychainQuery = service.generateBaseQuery() } - private init(overrideSharedAccess identifier: SharedAccessGroupIdentifier, configuration: Configuration) { + private init(overrideSharedAccess identifier: SharedGroupIdentifier, configuration: Configuration) { self.identifier = identifier.asIdentifier self.configuration = configuration - service = .sharedAccessGroupOverride(service: identifier, configuration) + service = .sharedGroupOverride(service: identifier, configuration) accessibility = configuration.accessibility baseKeychainQuery = service.generateBaseQuery() } @@ -382,13 +382,13 @@ public final class Valet: NSObject { let accessibilityDescription = "AccessibleAlways" let serviceAttribute: String switch service { - case let .sharedAccessGroup(sharedAccessGroupIdentifier, _): - serviceAttribute = Service.sharedAccessGroup(with: configuration, identifier: sharedAccessGroupIdentifier, accessibilityDescription: accessibilityDescription) + case let .sharedGroup(sharedGroupIdentifier, _): + serviceAttribute = Service.sharedGroup(with: configuration, identifier: sharedGroupIdentifier, accessibilityDescription: accessibilityDescription) case .standard: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #if os(macOS) - case let .sharedAccessGroupOverride(sharedAccessGroupIdentifier, _): - serviceAttribute = sharedAccessGroupIdentifier.description + case let .sharedGroupOverride(sharedGroupIdentifier, _): + serviceAttribute = sharedGroupIdentifier.description case .standardOverride: serviceAttribute = identifier.description #endif @@ -417,13 +417,13 @@ public final class Valet: NSObject { let accessibilityDescription = "AccessibleAlwaysThisDeviceOnly" let serviceAttribute: String switch service { - case let .sharedAccessGroup(identifier, _): - serviceAttribute = Service.sharedAccessGroup(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) + case let .sharedGroup(identifier, _): + serviceAttribute = Service.sharedGroup(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) case .standard: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #if os(macOS) - case .sharedAccessGroupOverride: - serviceAttribute = Service.sharedAccessGroup(with: configuration, explicitlySetIdentifier: identifier, accessibilityDescription: accessibilityDescription) + case .sharedGroupOverride: + serviceAttribute = Service.sharedGroup(with: configuration, explicitlySetIdentifier: identifier, accessibilityDescription: accessibilityDescription) case .standardOverride: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #endif @@ -499,12 +499,27 @@ extension Valet { /// - Returns: A Valet that reads/writes keychain elements that can be shared across applications written by the same development team. /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps @available(swift, obsoleted: 1.0) - @objc(sharedAccessGroupValetWithAppIDPrefix:sharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_vanillaSharedAccessGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessibility: Accessibility) -> Valet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(sharedGroupValetWithAppIDPrefix:sharedGroupIdentifier:accessibility:) + public class func 🚫swift_vanillaSharedGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessibility: Accessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { return nil } - return sharedAccessGroupValet(with: identifier, accessibility: accessibility) + return sharedGroupValet(with: identifier, accessibility: accessibility) + } + + /// - Parameters: + /// - groupPrefix: On iOS, iPadOS, watchOS, and tvOS, this prefix must equal "group". On macOS, this prefix is the application's App ID prefix, which can be found by inspecting the application's provisioning profile, or viewing the application's App ID Configuration on developer.apple.com. This string must not be empty. + /// - identifier: An identifier that corresponds to a value in com.apple.security.application-groups in the application's Entitlements file. This string must not be empty. + /// - accessibility: The desired accessibility for the Valet. + /// - Returns: A Valet that reads/writes keychain elements that can be shared across applications written by the same development team. + /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps + @available(swift, obsoleted: 1.0) + @objc(sharedGroupValetWithGroupPrefix:sharedGroupIdentifier:accessibility:) + public class func 🚫swift_vanillaSharedGroupValet(groupPrefix: String, nonEmptyIdentifier identifier: String, accessibility: Accessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(groupPrefix: groupPrefix, nonEmptyGroup: identifier) else { + return nil + } + return sharedGroupValet(with: identifier, accessibility: accessibility) } /// - Parameters: @@ -513,12 +528,26 @@ extension Valet { /// - accessibility: The desired accessibility for the Valet. /// - Returns: A Valet that reads/writes iCloud-shared keychain elements that can be shared across applications written by the same development team. @available(swift, obsoleted: 1.0) - @objc(iCloudValetWithAppIDPrefix:sharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_iCloudSharedAccessGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessibility: CloudAccessibility) -> Valet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(iCloudValetWithAppIDPrefix:sharedGroupIdentifier:accessibility:) + public class func 🚫swift_iCloudSharedGroupValet(appIDPrefix: String, nonEmptyIdentifier identifier: String, accessibility: CloudAccessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + return nil + } + return iCloudSharedGroupValet(with: identifier, accessibility: accessibility) + } + + /// - Parameters: + /// - groupPrefix: On iOS, iPadOS, watchOS, and tvOS, this prefix must equal "group". On macOS, this prefix is the application's App ID prefix, which can be found by inspecting the application's provisioning profile, or viewing the application's App ID Configuration on developer.apple.com. This string must not be empty. + /// - identifier: An identifier that corresponds to a value in com.apple.security.application-groups in the application's Entitlements file. This string must not be empty. + /// - accessibility: The desired accessibility for the Valet. + /// - Returns: A Valet that reads/writes iCloud-shared keychain elements that can be shared across applications written by the same development team. + @available(swift, obsoleted: 1.0) + @objc(iCloudValetWithGroupPrefix:sharedGroupIdentifier:accessibility:) + public class func 🚫swift_iCloudSharedGroupValet(groupPrefix: String, nonEmptyIdentifier identifier: String, accessibility: CloudAccessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(groupPrefix: groupPrefix, nonEmptyGroup: identifier) else { return nil } - return iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility) + return iCloudSharedGroupValet(with: identifier, accessibility: accessibility) } #if os(macOS) @@ -564,9 +593,9 @@ extension Valet { /// - SeeAlso: https://github.com/square/Valet/issues/140 /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps @available(swift, obsoleted: 1.0) - @objc(valetWithAppIDPrefix:explicitlySetSharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_sharedAccessGroupValet(appIDPrefix: String, withExplicitlySet identifier: String, accessibility: Accessibility) -> Valet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(valetWithAppIDPrefix:explicitlySetSharedGroupIdentifier:accessibility:) + public class func 🚫swift_sharedGroupValet(appIDPrefix: String, withExplicitlySet identifier: String, accessibility: Accessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { return nil } return findOrCreate(explicitlySet: identifier, configuration: .valet(accessibility)) @@ -582,9 +611,9 @@ extension Valet { /// - SeeAlso: https://github.com/square/Valet/issues/140 /// - SeeAlso: https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps @available(swift, obsoleted: 1.0) - @objc(iCloudValetWithAppIDPrefix:explicitlySetSharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_iCloudSharedAccessGroupValet(appIDPrefix: String, withExplicitlySet identifier: String, accessibility: CloudAccessibility) -> Valet? { - guard let identifier = SharedAccessGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { + @objc(iCloudValetWithAppIDPrefix:explicitlySetSharedGroupIdentifier:accessibility:) + public class func 🚫swift_iCloudSharedGroupValet(appIDPrefix: String, withExplicitlySet identifier: String, accessibility: CloudAccessibility) -> Valet? { + guard let identifier = SharedGroupIdentifier(appIDPrefix: appIDPrefix, nonEmptyGroup: identifier) else { return nil } return findOrCreate(explicitlySet: identifier, configuration: .iCloud(accessibility)) @@ -617,9 +646,9 @@ internal extension Valet { } } - class func permutations(with identifier: SharedAccessGroupIdentifier) -> [Valet] { + class func permutations(with identifier: SharedGroupIdentifier) -> [Valet] { Accessibility.allCases.map { accessibility in - .sharedAccessGroupValet(with: identifier, accessibility: accessibility) + .sharedGroupValet(with: identifier, accessibility: accessibility) } } @@ -629,9 +658,9 @@ internal extension Valet { } } - class func iCloudPermutations(with identifier: SharedAccessGroupIdentifier) -> [Valet] { + class func iCloudPermutations(with identifier: SharedGroupIdentifier) -> [Valet] { CloudAccessibility.allCases.map { cloudAccessibility in - .iCloudSharedAccessGroupValet(with: identifier, accessibility: cloudAccessibility) + .iCloudSharedGroupValet(with: identifier, accessibility: cloudAccessibility) } } @@ -642,9 +671,9 @@ internal extension Valet { } } - class func permutations(withExplictlySet identifier: SharedAccessGroupIdentifier) -> [Valet] { + class func permutations(withExplictlySet identifier: SharedGroupIdentifier) -> [Valet] { Accessibility.allCases.map { accessibility in - .sharedAccessGroupValet(withExplicitlySet: identifier, accessibility: accessibility) + .sharedGroupValet(withExplicitlySet: identifier, accessibility: accessibility) } } @@ -654,9 +683,9 @@ internal extension Valet { } } - class func iCloudPermutations(withExplictlySet identifier: SharedAccessGroupIdentifier) -> [Valet] { + class func iCloudPermutations(withExplictlySet identifier: SharedGroupIdentifier) -> [Valet] { CloudAccessibility.allCases.map { cloudAccessibility in - .iCloudSharedAccessGroupValet(withExplicitlySet: identifier, accessibility: cloudAccessibility) + .iCloudSharedGroupValet(withExplicitlySet: identifier, accessibility: cloudAccessibility) } } #endif diff --git a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift index c25653d7..1d5f9d2b 100644 --- a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift +++ b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift @@ -31,12 +31,12 @@ internal extension Valet { var legacyIdentifier: String { switch service { - case let .sharedAccessGroup(sharedAccessGroupIdentifier, _): + case let .sharedGroup(sharedAccessGroupIdentifier, _): return sharedAccessGroupIdentifier.groupIdentifier case let .standard(identifier, _): return identifier.description #if os(macOS) - case let .sharedAccessGroupOverride(identifier, _): + case let .sharedGroupOverride(identifier, _): return identifier.groupIdentifier case let .standardOverride(identifier, _): return identifier.description @@ -60,11 +60,11 @@ internal extension Valet { switch service { case .standard: return VALLegacyValet(identifier: legacyIdentifier, accessibility: legacyAccessibility)! - case .sharedAccessGroup: + case .sharedGroup: return VALLegacyValet(sharedAccessGroupIdentifier: legacyIdentifier, accessibility: legacyAccessibility)! #if os(macOS) case .standardOverride, - .sharedAccessGroupOverride: + .sharedGroupOverride: fatalError("There is no legacy Valet for a service override valet") #endif } @@ -72,11 +72,11 @@ internal extension Valet { switch service { case .standard: return VALSynchronizableValet(identifier: legacyIdentifier, accessibility: legacyAccessibility)! - case .sharedAccessGroup: + case .sharedGroup: return VALSynchronizableValet(sharedAccessGroupIdentifier: legacyIdentifier, accessibility: legacyAccessibility)! #if os(macOS) case .standardOverride, - .sharedAccessGroupOverride: + .sharedGroupOverride: fatalError("There is no legacy Valet for a service override valet") #endif } @@ -94,7 +94,7 @@ internal extension Valet { } } - class func currentAndLegacyPermutations(with identifier: SharedAccessGroupIdentifier) -> [(Valet, VALLegacyValet)] { + class func currentAndLegacyPermutations(with identifier: SharedGroupIdentifier) -> [(Valet, VALLegacyValet)] { permutations(with: identifier).map { ($0, $0.legacyValet) } @@ -106,7 +106,7 @@ internal extension Valet { } } - class func iCloudCurrentAndLegacyPermutations(with identifier: SharedAccessGroupIdentifier) -> [(Valet, VALSynchronizableValet)] { + class func iCloudCurrentAndLegacyPermutations(with identifier: SharedGroupIdentifier) -> [(Valet, VALSynchronizableValet)] { iCloudPermutations(with: identifier).map { ($0, $0.legacyValet as! VALSynchronizableValet) } @@ -175,7 +175,7 @@ class ValetBackwardsCompatibilityIntegrationTests: ValetIntegrationTests { let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.groupIdentifier, accessibility: .always)! alwaysAccessibleLegacyValet.setString(passcode, forKey: key) - let valet = Valet.sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlock) + let valet = Valet.sharedGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlock) XCTAssertNoThrow(try valet.migrateObjectsFromAlwaysAccessibleValet(removeOnCompletion: true)) XCTAssertEqual(try valet.string(forKey: key), passcode) } @@ -187,7 +187,7 @@ class ValetBackwardsCompatibilityIntegrationTests: ValetIntegrationTests { let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.groupIdentifier, accessibility: .alwaysThisDeviceOnly)! alwaysAccessibleLegacyValet.setString(passcode, forKey: key) - let valet = Valet.sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlockThisDeviceOnly) + let valet = Valet.sharedGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlockThisDeviceOnly) XCTAssertNoThrow(try valet.migrateObjectsFromAlwaysAccessibleThisDeviceOnlyValet(removeOnCompletion: true)) XCTAssertEqual(try valet.string(forKey: key), passcode) } diff --git a/Tests/ValetIntegrationTests/MacTests.swift b/Tests/ValetIntegrationTests/MacTests.swift index 33db7d6d..7407879a 100644 --- a/Tests/ValetIntegrationTests/MacTests.swift +++ b/Tests/ValetIntegrationTests/MacTests.swift @@ -145,14 +145,14 @@ class ValetMacTests: XCTestCase try $0.removeAllObjects() } - let explicitlySetSharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-macOS-Test-Host-App")! - try Valet.permutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true).forEach { + let explicitlySetSharedGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-macOS-Test-Host-App")! + try Valet.permutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true).forEach { XCTAssertTrue($0.canAccessKeychain()) try $0.removeAllObjects() } - try Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true).forEach { + try Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true).forEach { XCTAssertTrue($0.canAccessKeychain()) try $0.removeAllObjects() @@ -182,15 +182,15 @@ class ValetMacTests: XCTestCase try $0.removeAllObjects() } - let explicitlySetSharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-macOS-Test-Host-App")! - try Valet.permutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true).forEach { + let explicitlySetSharedGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-macOS-Test-Host-App")! + try Valet.permutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true).forEach { try $0.setString(passcode, forKey: key) XCTAssertEqual(try $0.string(forKey: key), passcode) try $0.removeAllObjects() } - try Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true).forEach { + try Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true).forEach { try $0.setString(passcode, forKey: key) XCTAssertEqual(try $0.string(forKey: key), passcode) @@ -212,15 +212,15 @@ class ValetMacTests: XCTestCase XCTAssertTrue(permutation === permutations2[index], "Two iCloud Valets with \(accessibilityValues[index]) were not identical") } - let explicitlySetSharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! - permutations1 = Valet.permutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) - permutations2 = Valet.permutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) + let explicitlySetSharedGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! + permutations1 = Valet.permutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) + permutations2 = Valet.permutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) for (index, permutation) in permutations1.enumerated() { XCTAssertTrue(permutation === permutations2[index], "Two shared Valets with \(accessibilityValues[index]) were not identical") } - permutations1 = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) - permutations2 = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) + permutations1 = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) + permutations2 = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) for (index, permutation) in permutations1.enumerated() { XCTAssertTrue(permutation === permutations2[index], "Two shared iCloud Valets with \(accessibilityValues[index]) were not identical") } @@ -238,13 +238,13 @@ class ValetMacTests: XCTestCase XCTAssertEqual(accessibilityValues[index], permutation.accessibility) } - let explicitlySetSharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! - permutations = Valet.permutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) + let explicitlySetSharedGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! + permutations = Valet.permutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) for (index, permutation) in permutations.enumerated() { XCTAssertEqual(accessibilityValues[index], permutation.accessibility) } - permutations = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedAccessGroupIdentifier, shared: true) + permutations = Valet.iCloudPermutations(withExplictlySet: explicitlySetSharedGroupIdentifier, shared: true) for (index, permutation) in permutations.enumerated() { XCTAssertEqual(accessibilityValues[index], permutation.accessibility) } diff --git a/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift b/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift index 9c1cea83..509f47c7 100644 --- a/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift @@ -97,14 +97,31 @@ class SecureEnclaveIntegrationTests: XCTestCase } let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in - return .sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) + return .sharedGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) } for permutation in permutations { XCTAssertTrue(permutation.canAccessKeychain()) } } - + + #if !os(macOS) + // We can't test app groups on macOS without a paid developer account, which we don't have. + func test_canAccessKeychain_sharedAppGroup() { + guard testEnvironmentIsSigned() else { + return + } + + let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in + return .sharedGroupValet(with: Valet.sharedAppGroupIdentifier, accessControl: accessControl) + } + + for permutation in permutations { + XCTAssertTrue(permutation.canAccessKeychain()) + } + } + #endif + // MARK: Migration func test_migrateObjectsMatchingQuery_failsForBadQuery() diff --git a/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift b/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift index 39dc7e2c..029b5ec8 100644 --- a/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift @@ -152,14 +152,34 @@ class SinglePromptSecureEnclaveIntegrationTests: XCTestCase } let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in - return .sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) + return .sharedGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) } for permutation in permutations { XCTAssertTrue(permutation.canAccessKeychain()) } } - + + #if !os(macOS) + // We can't test app groups on macOS without a paid developer account, which we don't have. + func test_canAccessKeychain_sharedAppGroup() { + guard #available(tvOS 11.0, *) else { + return + } + guard testEnvironmentIsSigned() else { + return + } + + let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in + return .sharedGroupValet(with: Valet.sharedAppGroupIdentifier, accessControl: accessControl) + } + + for permutation in permutations { + XCTAssertTrue(permutation.canAccessKeychain()) + } + } + #endif + // MARK: Migration func test_migrateObjectsMatchingQuery_failsForBadQuery() diff --git a/Tests/ValetIntegrationTests/ValetIntegrationTests.swift b/Tests/ValetIntegrationTests/ValetIntegrationTests.swift index 259a99d4..7f2e8bb3 100644 --- a/Tests/ValetIntegrationTests/ValetIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/ValetIntegrationTests.swift @@ -60,15 +60,31 @@ internal extension Valet { // MARK: Shared Access Group - static var sharedAccessGroupIdentifier: SharedAccessGroupIdentifier = { + static var sharedAccessGroupIdentifier: SharedGroupIdentifier = { #if os(iOS) - return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-iOS-Test-Host-App")! + return SharedGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-iOS-Test-Host-App")! #elseif os(macOS) - return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-macOS-Test-Host-App")! + return SharedGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-macOS-Test-Host-App")! #elseif os(tvOS) - return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-tvOS-Test-Host-App")! + return SharedGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-tvOS-Test-Host-App")! #elseif os(watchOS) - return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension")! + return SharedGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension")! + #else + XCTFail() + #endif + }() + + // MARK: Shared App Group + + static var sharedAppGroupIdentifier: SharedGroupIdentifier = { + #if os(iOS) + return SharedGroupIdentifier(groupPrefix: "group", nonEmptyGroup: "valet.test")! + #elseif os(macOS) + return SharedGroupIdentifier(groupPrefix: "9XUJ7M53NG", nonEmptyGroup: "valet.test")! + #elseif os(tvOS) + return SharedGroupIdentifier(groupPrefix: "group", nonEmptyGroup: "valet.test")! + #elseif os(watchOS) + return SharedGroupIdentifier(groupPrefix: "group", nonEmptyGroup: "valet.test")! #else XCTFail() #endif @@ -80,9 +96,17 @@ internal extension Valet { class ValetIntegrationTests: XCTestCase { static let sharedAccessGroupIdentifier = Valet.sharedAccessGroupIdentifier + static let sharedAppGroupIdentifier = Valet.sharedAppGroupIdentifier var allPermutations: [Valet] { + var signedPermutations = Valet.permutations(with: ValetIntegrationTests.sharedAccessGroupIdentifier) + #if !os(macOS) + // We can't test app groups on macOS without a paid developer account, which we don't have. + signedPermutations += Valet.permutations(with: ValetIntegrationTests.sharedAppGroupIdentifier) + #endif return Valet.permutations(with: ValetIntegrationTests.sharedAccessGroupIdentifier.asIdentifier) - + (testEnvironmentIsSigned() ? Valet.permutations(with: ValetIntegrationTests.sharedAccessGroupIdentifier) : []) + + (testEnvironmentIsSigned() + ? signedPermutations + : []) } let vanillaValet = Valet.valet(with: sharedAccessGroupIdentifier.asIdentifier, accessibility: .whenUnlocked) @@ -132,8 +156,8 @@ class ValetIntegrationTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier Accessibility.allCases.forEach { accessibility in - let backingService = Valet.sharedAccessGroupValet(with: identifier, accessibility: accessibility).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .valet(accessibility))) + let backingService = Valet.sharedGroupValet(with: identifier, accessibility: accessibility).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .valet(accessibility))) } } @@ -150,8 +174,8 @@ class ValetIntegrationTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier CloudAccessibility.allCases.forEach { accessibility in - let backingService = Valet.iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .iCloud(accessibility))) + let backingService = Valet.iCloudSharedGroupValet(with: identifier, accessibility: accessibility).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .iCloud(accessibility))) } } @@ -175,6 +199,20 @@ class ValetIntegrationTests: XCTestCase } } + #if !os(macOS) + // We can't test app groups on macOS without a paid developer account, which we don't have. + func test_canAccessKeychain_sharedAppGroup() + { + guard testEnvironmentIsSigned() else { + return + } + + Valet.permutations(with: Valet.sharedAppGroupIdentifier).forEach { permutation in + XCTAssertTrue(permutation.canAccessKeychain(), "\(permutation) could not access keychain.") + } + } + #endif + func test_canAccessKeychain_Performance() { measure { diff --git a/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m index 03bca127..03bd9b8d 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m @@ -45,6 +45,25 @@ - (NSString *)sharedAccessGroupIdentifier; #endif } +- (NSString *)groupPrefix; +{ +#if TARGET_OS_IPHONE + return @"group"; +#elif TARGET_OS_WATCH + return @"group"; +#elif TARGET_OS_MAC + return self.appIDPrefix; +#else + // This will fail + return @""; +#endif +} + +- (NSString *)sharedAppGroupIdentifier; +{ + return @"valet.test"; +} + - (void)test_valetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet valetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; @@ -81,35 +100,69 @@ - (void)test_valetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + XCTAssertNil(valet); +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; +{ + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); + XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; +{ + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); + XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; +{ + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); + XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; +{ + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); + XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +{ + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertNil(valet); } diff --git a/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m index e3f17d68..6782236c 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m @@ -45,6 +45,25 @@ - (NSString *)sharedAccessGroupIdentifier; #endif } +- (NSString *)groupPrefix; +{ +#if TARGET_OS_IPHONE + return @"group"; +#elif TARGET_OS_WATCH + return @"group"; +#elif TARGET_OS_MAC + return self.appIDPrefix; +#else + // This will fail + return @""; +#endif +} + +- (NSString *)sharedAppGroupIdentifier; +{ + return @"valet.test"; +} + - (void)test_valetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { if (@available(tvOS 11.0, *)) { @@ -92,7 +111,7 @@ - (void)test_valetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -101,7 +120,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -110,7 +129,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -119,7 +138,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -128,7 +147,51 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + XCTAssertNil(valet); + } +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; +{ + if (@available(tvOS 11.0, *)) { + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); + XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); + } +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; +{ + if (@available(tvOS 11.0, *)) { + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); + XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); + } +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; +{ + if (@available(tvOS 11.0, *)) { + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); + XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); + } +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; +{ + if (@available(tvOS 11.0, *)) { + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); + XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); + } +} + +- (void)test_sharedAppGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +{ + if (@available(tvOS 11.0, *)) { + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertNil(valet); } } diff --git a/Tests/ValetObjectiveCBridgeTests/VALValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALValetTests.m index fadf4e52..4014d04c 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALValetTests.m @@ -45,6 +45,25 @@ - (NSString *)sharedAccessGroupIdentifier; #endif } +- (NSString *)groupPrefix; +{ +#if TARGET_OS_IPHONE + return @"group"; +#elif TARGET_OS_WATCH + return @"group"; +#elif TARGET_OS_MAC + return self.appIDPrefix; +#else + // This will fail + return @""; +#endif +} + +- (NSString *)sharedAppGroupIdentifier; +{ + return @"valet.test"; +} + - (void)test_valetWithIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; { VALValet *const valet = [VALValet valetWithIdentifier:self.identifier accessibility:VALAccessibilityWhenUnlocked]; @@ -108,62 +127,123 @@ - (void)test_iCloudValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsE - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenPasscodeSetThisDeviceOnly; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenPasscodeSetThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlockedThisDeviceOnly; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlockedThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlockThisDeviceOnly; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlockThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet sharedGroupValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + XCTAssertNil(valet); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; + XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlock; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; + XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenPasscodeSetThisDeviceOnly; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; + XCTAssertEqual(valet.accessibility, VALAccessibilityWhenPasscodeSetThisDeviceOnly); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlockedThisDeviceOnly; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; + XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlockedThisDeviceOnly); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlockThisDeviceOnly; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlockThisDeviceOnly); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_valetWithSharedAppGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +{ + VALValet *const valet = [VALValet sharedGroupValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertNil(valet); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; + XCTAssertNil(valet); +} + +- (void)test_iCloudValetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityWhenUnlocked; +{ + VALValet *const valet = [VALValet iCloudValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; + XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_iCloudValetWithSharedAppGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityAfterFirstUnlock; +{ + VALValet *const valet = [VALValet iCloudValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:self.sharedAppGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; + XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); + XCTAssertEqual([valet class], [VALValet class]); +} + +- (void)test_iCloudValetWithSharedAppGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +{ + VALValet *const valet = [VALValet iCloudValetWithGroupPrefix:self.groupPrefix sharedGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertNil(valet); } @@ -232,64 +312,64 @@ - (void)test_iCloudValetWithExplicitlySetIdentifier_accessibility_returnsNilWhen XCTAssertNil(valet); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlock; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenPasscodeSetThisDeviceOnly; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenPasscodeSetThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenPasscodeSetThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlockedThisDeviceOnly; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlockedThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlockedThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlockThisDeviceOnly; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlockThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlockThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_valetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +- (void)test_valetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet valetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertNil(valet); } -- (void)test_iCloudValetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityWhenUnlocked; +- (void)test_iCloudValetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_iCloudValetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityAfterFirstUnlock; +- (void)test_iCloudValetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALCloudAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } -- (void)test_iCloudValetWithExplicitlySetSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; +- (void)test_iCloudValetWithExplicitlySetSharedGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedAccessGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix explicitlySetSharedGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertNil(valet); } diff --git a/Tests/ValetTests/SecureEnclaveTests.swift b/Tests/ValetTests/SecureEnclaveTests.swift index 7ad2a763..bc1a7893 100644 --- a/Tests/ValetTests/SecureEnclaveTests.swift +++ b/Tests/ValetTests/SecureEnclaveTests.swift @@ -44,8 +44,8 @@ class SecureEnclaveTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier SecureEnclaveAccessControl.allValues().forEach { accessControl in - let backingService = SecureEnclaveValet.sharedAccessGroupValet(with: identifier, accessControl: accessControl).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .secureEnclave(accessControl))) + let backingService = SecureEnclaveValet.sharedGroupValet(with: identifier, accessControl: accessControl).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .secureEnclave(accessControl))) } } diff --git a/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift b/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift index ebc22717..89d32585 100644 --- a/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift +++ b/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift @@ -46,8 +46,8 @@ class SinglePromptSecureEnclaveTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier SecureEnclaveAccessControl.allValues().forEach { accessControl in - let backingService = SinglePromptSecureEnclaveValet.sharedAccessGroupValet(with: identifier, accessControl: accessControl).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .singlePromptSecureEnclave(accessControl))) + let backingService = SinglePromptSecureEnclaveValet.sharedGroupValet(with: identifier, accessControl: accessControl).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .singlePromptSecureEnclave(accessControl))) } } diff --git a/Tests/ValetTests/ValetTests.swift b/Tests/ValetTests/ValetTests.swift index a0fce3f8..d9a9e7b4 100644 --- a/Tests/ValetTests/ValetTests.swift +++ b/Tests/ValetTests/ValetTests.swift @@ -44,8 +44,8 @@ class ValetTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier Accessibility.allCases.forEach { accessibility in - let backingService = Valet.sharedAccessGroupValet(with: identifier, accessibility: accessibility).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .valet(accessibility))) + let backingService = Valet.sharedGroupValet(with: identifier, accessibility: accessibility).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .valet(accessibility))) } } @@ -62,8 +62,8 @@ class ValetTests: XCTestCase let identifier = Valet.sharedAccessGroupIdentifier CloudAccessibility.allCases.forEach { accessibility in - let backingService = Valet.iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility).service - XCTAssertEqual(backingService, Service.sharedAccessGroup(identifier, .iCloud(accessibility))) + let backingService = Valet.iCloudSharedGroupValet(with: identifier, accessibility: accessibility).service + XCTAssertEqual(backingService, Service.sharedGroup(identifier, .iCloud(accessibility))) } } diff --git a/Valet iOS Test Host App/Valet iOS Test Host App.entitlements b/Valet iOS Test Host App/Valet iOS Test Host App.entitlements index 6ac9c834..f00985b7 100644 --- a/Valet iOS Test Host App/Valet iOS Test Host App.entitlements +++ b/Valet iOS Test Host App/Valet iOS Test Host App.entitlements @@ -2,6 +2,10 @@ + com.apple.security.application-groups + + group.valet.test + keychain-access-groups $(AppIdentifierPrefix)com.squareup.Valet-iOS-Test-Host-App diff --git a/Valet macOS Test Host App/Valet_macOS_Test_Host_App.entitlements b/Valet macOS Test Host App/Valet_macOS_Test_Host_App.entitlements index a7e49f67..9e73df49 100644 --- a/Valet macOS Test Host App/Valet_macOS_Test_Host_App.entitlements +++ b/Valet macOS Test Host App/Valet_macOS_Test_Host_App.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + $(TeamIdentifierPrefix)valet.test + com.apple.security.files.user-selected.read-only keychain-access-groups diff --git a/Valet tvOS Test Host App/Valet tvOS Test Host App.entitlements b/Valet tvOS Test Host App/Valet tvOS Test Host App.entitlements index 064929f8..2225c3e1 100644 --- a/Valet tvOS Test Host App/Valet tvOS Test Host App.entitlements +++ b/Valet tvOS Test Host App/Valet tvOS Test Host App.entitlements @@ -2,6 +2,10 @@ + com.apple.security.application-groups + + group.valet.test + keychain-access-groups $(AppIdentifierPrefix)com.squareup.Valet-tvOS-Test-Host-App diff --git a/Valet watchOS Test Host App Extension/Valet watchOS Test Host App Extension.entitlements b/Valet watchOS Test Host App Extension/Valet watchOS Test Host App Extension.entitlements index 033442ae..8a57f3d8 100644 --- a/Valet watchOS Test Host App Extension/Valet watchOS Test Host App Extension.entitlements +++ b/Valet watchOS Test Host App Extension/Valet watchOS Test Host App Extension.entitlements @@ -2,6 +2,10 @@ + com.apple.security.application-groups + + group.valet.test + keychain-access-groups $(AppIdentifierPrefix)com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension diff --git a/Valet watchOS Test Host App/Valet watchOS Test Host App.entitlements b/Valet watchOS Test Host App/Valet watchOS Test Host App.entitlements new file mode 100644 index 00000000..62171960 --- /dev/null +++ b/Valet watchOS Test Host App/Valet watchOS Test Host App.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.valet.test + + + diff --git a/Valet.xcodeproj/project.pbxproj b/Valet.xcodeproj/project.pbxproj index 01480f89..6a10b12e 100644 --- a/Valet.xcodeproj/project.pbxproj +++ b/Valet.xcodeproj/project.pbxproj @@ -147,10 +147,10 @@ 1693E29623B2D24600F8D97A /* KeychainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1693E29423B2D24600F8D97A /* KeychainError.swift */; }; 1693E29723B2D24600F8D97A /* KeychainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1693E29423B2D24600F8D97A /* KeychainError.swift */; }; 1693E29823B2D24600F8D97A /* KeychainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1693E29423B2D24600F8D97A /* KeychainError.swift */; }; - 16967AE82405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */; }; - 16967AE92405CC7400DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */; }; - 16967AEA2405CC7500DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */; }; - 16967AEB2405CC7600DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */; }; + 16967AE82405CAF800DC2B2D /* SharedGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */; }; + 16967AE92405CC7400DC2B2D /* SharedGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */; }; + 16967AEA2405CC7500DC2B2D /* SharedGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */; }; + 16967AEB2405CC7600DC2B2D /* SharedGroupIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */; }; 169E9A6723D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 169E9A6423D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m */; }; 169E9A6823D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 169E9A6423D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m */; }; 169E9A6923D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 169E9A6423D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m */; }; @@ -415,6 +415,7 @@ 16212CD923072C6F00C84B17 /* ValetDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValetDefines.h; path = LegacyValet/ValetDefines.h; sourceTree = ""; }; 16212CDA23072C6F00C84B17 /* VALLegacySecureEnclaveValet_Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VALLegacySecureEnclaveValet_Protected.h; path = LegacyValet/VALLegacySecureEnclaveValet_Protected.h; sourceTree = ""; }; 16212CFB23072CB700C84B17 /* LegacyValet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LegacyValet.h; path = LegacyValet/Public/LegacyValet.h; sourceTree = ""; }; + 163F018D23D77E9800BBD3E3 /* Valet watchOS Test Host App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Valet watchOS Test Host App.entitlements"; sourceTree = ""; }; 165CDDC6204B26D400C96C2E /* Valet tvOS Test Host App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Valet tvOS Test Host App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 165CDDC8204B26D400C96C2E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 165CDDCA204B26D400C96C2E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -426,7 +427,7 @@ 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudAccessibilityTests.swift; sourceTree = ""; }; 167E250F23D6328E00889121 /* ConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = ""; }; 1693E29423B2D24600F8D97A /* KeychainError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainError.swift; sourceTree = ""; }; - 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccessGroupIdentifier.swift; sourceTree = ""; }; + 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedGroupIdentifier.swift; sourceTree = ""; }; 169E9A6423D181DC001B69F5 /* VALSinglePromptSecureEnclaveValetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VALSinglePromptSecureEnclaveValetTests.m; sourceTree = ""; }; 169E9A6523D181DC001B69F5 /* VALValetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VALValetTests.m; sourceTree = ""; }; 169E9A6623D181DC001B69F5 /* VALSecureEnclaveValetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VALSecureEnclaveValetTests.m; sourceTree = ""; }; @@ -640,7 +641,7 @@ 1612FD7822A9CAAB00FC1142 /* SecureEnclaveAccessControl.swift */, 1612FD7922A9CAAB00FC1142 /* SinglePromptSecureEnclaveValet.swift */, 1612FD7B22A9CAAB00FC1142 /* SecureEnclaveValet.swift */, - 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */, + 16967AE72405CAF800DC2B2D /* SharedGroupIdentifier.swift */, 1612FD7C22A9CAAB00FC1142 /* Identifier.swift */, 1612FD7022A9CAAB00FC1142 /* Internal */, ); @@ -742,6 +743,7 @@ 16DF6AF4204B496800F8E0A4 /* Valet watchOS Test Host App */ = { isa = PBXGroup; children = ( + 163F018D23D77E9800BBD3E3 /* Valet watchOS Test Host App.entitlements */, 16DF6AF5204B496800F8E0A4 /* Interface.storyboard */, 16DF6AF8204B496800F8E0A4 /* Assets.xcassets */, 16DF6AFA204B496800F8E0A4 /* Info.plist */, @@ -1558,7 +1560,7 @@ 1612FD9F22A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA322A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD7F22A9CAAB00FC1142 /* MigrationError.swift in Sources */, - 16967AEA2405CC7500DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, + 16967AEA2405CC7500DC2B2D /* SharedGroupIdentifier.swift in Sources */, 1612FD9722A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1603,7 +1605,7 @@ 1612FDA022A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA422A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD8022A9CAAB00FC1142 /* MigrationError.swift in Sources */, - 16967AEB2405CC7600DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, + 16967AEB2405CC7600DC2B2D /* SharedGroupIdentifier.swift in Sources */, 1612FD9822A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1621,7 +1623,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 16967AE82405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, + 16967AE82405CAF800DC2B2D /* SharedGroupIdentifier.swift in Sources */, 1693E29523B2D24600F8D97A /* KeychainError.swift in Sources */, 1612FDB922A9CAAB00FC1142 /* SecureEnclaveValet.swift in Sources */, 1612FD8D22A9CAAB00FC1142 /* Valet.swift in Sources */, @@ -1656,7 +1658,7 @@ 1612FD9E22A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA222A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD7E22A9CAAB00FC1142 /* MigrationError.swift in Sources */, - 16967AE92405CC7400DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, + 16967AE92405CC7400DC2B2D /* SharedGroupIdentifier.swift in Sources */, 1612FD9622A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2354,6 +2356,7 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Valet watchOS Test Host App/Valet watchOS Test Host App.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; DEBUG_INFORMATION_FORMAT = dwarf; @@ -2383,6 +2386,7 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Valet watchOS Test Host App/Valet watchOS Test Host App.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO;