diff --git a/Sources/Valet/Internal/Service.swift b/Sources/Valet/Internal/Service.swift index 0497b60c..37ecb489 100644 --- a/Sources/Valet/Internal/Service.swift +++ b/Sources/Valet/Internal/Service.swift @@ -23,7 +23,7 @@ import Foundation internal enum Service: CustomStringConvertible, Equatable { case standard(Identifier, Configuration) - case sharedAccessGroup(Identifier, Configuration) + case sharedAccessGroup(SharedAccessGroupIdentifier, Configuration) #if os(macOS) case standardOverride(service: Identifier, Configuration) @@ -48,7 +48,11 @@ internal enum Service: CustomStringConvertible, Equatable { "VAL_\(configuration.description)_initWithIdentifier:accessibility:_\(identifier)_\(accessibilityDescription)" } - internal static func sharedAccessGroup(with configuration: Configuration, identifier: Identifier, accessibilityDescription: String) -> String { + internal static func sharedAccessGroup(with configuration: Configuration, identifier: SharedAccessGroupIdentifier, accessibilityDescription: String) -> String { + "VAL_\(configuration.description)_initWithSharedAccessGroupIdentifier:accessibility:_\(identifier.groupIdentifier)_\(accessibilityDescription)" + } + + internal static func sharedAccessGroup(with configuration: Configuration, explicitlySetIdentifier identifier: Identifier, accessibilityDescription: String) -> String { "VAL_\(configuration.description)_initWithSharedAccessGroupIdentifier:accessibility:_\(identifier)_\(accessibilityDescription)" } diff --git a/Sources/Valet/SecureEnclave.swift b/Sources/Valet/SecureEnclave.swift index 42b059af..4d80c3b8 100644 --- a/Sources/Valet/SecureEnclave.swift +++ b/Sources/Valet/SecureEnclave.swift @@ -25,26 +25,24 @@ public final class SecureEnclave { // MARK: Internal Methods - /// - Parameters: - /// - service: The service of the keychain slice we want to check if we can access. - /// - identifier: A non-empty identifier that scopes the slice of keychain we want to access. + /// - Parameter service: The service of the keychain slice we want to check if we can access. /// - Returns: `true` if the keychain is accessible for reading and writing, `false` otherwise. /// - Note: Determined by writing a value to the keychain and then reading it back out. - internal static func canAccessKeychain(with service: Service, identifier: Identifier) -> Bool { + internal static func canAccessKeychain(with service: Service) -> Bool { // To avoid prompting the user for Touch ID or passcode, create a Valet with our identifier and accessibility and ask it if it can access the keychain. let noPromptValet: Valet switch service { #if os(macOS) - case .standardOverride: - fallthrough + case let .standardOverride(identifier, _): + noPromptValet = .valet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) #endif - case .standard: + case let .standard(identifier, _): noPromptValet = .valet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) #if os(macOS) - case .sharedAccessGroupOverride: - fallthrough + case let .sharedAccessGroupOverride(identifier, _): + noPromptValet = .sharedAccessGroupValet(withExplicitlySet: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) #endif - case .sharedAccessGroup: + case let .sharedAccessGroup(identifier, _): noPromptValet = .sharedAccessGroupValet(with: identifier, accessibility: .whenPasscodeSetThisDeviceOnly) } diff --git a/Sources/Valet/SecureEnclaveValet.swift b/Sources/Valet/SecureEnclaveValet.swift index d634a4de..a711ae3a 100644 --- a/Sources/Valet/SecureEnclaveValet.swift +++ b/Sources/Valet/SecureEnclaveValet.swift @@ -47,7 +47,7 @@ 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: Identifier, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet { + public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet { let key = Service.sharedAccessGroup(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 identifier: Identifier, accessControl: SecureEnclaveAccessControl) { + private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) { self.init( - identifier: identifier, - service: .sharedAccessGroup(identifier, .secureEnclave(accessControl)), + identifier: groupIdentifier.asIdentifier, + service: .sharedAccessGroup(groupIdentifier, .secureEnclave(accessControl)), accessControl: accessControl) } @@ -116,7 +116,7 @@ public final class SecureEnclaveValet: NSObject { /// - Note: Determined by writing a value to the keychain and then reading it back out. Will never prompt the user for Face ID, Touch ID, or password. @objc public func canAccessKeychain() -> Bool { - SecureEnclave.canAccessKeychain(with: service, identifier: identifier) + SecureEnclave.canAccessKeychain(with: service) } /// - Parameters: @@ -249,12 +249,14 @@ extension SecureEnclaveValet { } /// - Parameters: - /// - identifier: A non-empty string that must correspond with the value for keychain-access-groups in your Entitlements file. + /// - appIDPrefix: The application's App ID prefix. This string 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 cooresponds to a value in keychain-access-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. - @objc(sharedAccessGroupValetWithIdentifier:accessControl:) - public class func 🚫swift_sharedAccessGroupValet(with identifier: String, accessControl: SecureEnclaveAccessControl) -> SecureEnclaveValet? { - guard let identifier = Identifier(nonEmpty: identifier) else { + /// - 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 { return nil } return sharedAccessGroupValet(with: identifier, accessControl: accessControl) diff --git a/Sources/Valet/SharedAccessGroupIdentifier.swift b/Sources/Valet/SharedAccessGroupIdentifier.swift new file mode 100644 index 00000000..0dbc3f92 --- /dev/null +++ b/Sources/Valet/SharedAccessGroupIdentifier.swift @@ -0,0 +1,57 @@ +// +// SharedAccessGroupIdentifier.swift +// Valet +// +// Created by Dan Federman on 2/25/20. +// Copyright © 2020 Square, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//    http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + + +public struct SharedAccessGroupIdentifier: CustomStringConvertible { + + // MARK: Initialization + + /// A representation of a shared access group identifier. + /// - Parameters: + /// - appIDPrefix: The application's App ID prefix. This string 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 cooresponds to a value in keychain-access-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?(appIDPrefix: String, nonEmptyGroup groupIdentifier: String?) { + guard !appIDPrefix.isEmpty, let groupIdentifier = groupIdentifier, !groupIdentifier.isEmpty else { + return nil + } + + self.appIDPrefix = appIDPrefix + self.groupIdentifier = groupIdentifier + } + + // MARK: CustomStringConvertible + + public var description: String { + return appIDPrefix + "." + groupIdentifier + } + + // MARK: Internal Properties + + internal let appIDPrefix: 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)! + } +} diff --git a/Sources/Valet/SinglePromptSecureEnclaveValet.swift b/Sources/Valet/SinglePromptSecureEnclaveValet.swift index 1e31c68d..ec3f6d3b 100644 --- a/Sources/Valet/SinglePromptSecureEnclaveValet.swift +++ b/Sources/Valet/SinglePromptSecureEnclaveValet.swift @@ -48,10 +48,10 @@ public final class SinglePromptSecureEnclaveValet: NSObject { } /// - Parameters: - /// - identifier: A non-empty string that must correspond with the value for keychain-access-groups in your Entitlements file. + /// - 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: Identifier, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet { + public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet { let key = Service.sharedAccessGroup(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 identifier: Identifier, accessControl: SecureEnclaveAccessControl) { + private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, accessControl: SecureEnclaveAccessControl) { self.init( - identifier: identifier, - service: .sharedAccessGroup(identifier, .singlePromptSecureEnclave(accessControl)), + identifier: groupIdentifier.asIdentifier, + service: .sharedAccessGroup(groupIdentifier, .singlePromptSecureEnclave(accessControl)), accessControl: accessControl) } @@ -120,7 +120,7 @@ public final class SinglePromptSecureEnclaveValet: NSObject { /// - Note: Determined by writing a value to the keychain and then reading it back out. Will never prompt the user for Face ID, Touch ID, or password. @objc public func canAccessKeychain() -> Bool { - SecureEnclave.canAccessKeychain(with: service, identifier: identifier) + SecureEnclave.canAccessKeychain(with: service) } /// - Parameters: @@ -290,12 +290,14 @@ extension SinglePromptSecureEnclaveValet { } /// - Parameters: - /// - identifier: A non-empty string that must correspond with the value for keychain-access-groups in your Entitlements file. + /// - appIDPrefix: The application's App ID prefix. This string 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 cooresponds to a value in keychain-access-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. - @objc(sharedAccessGroupValetWithIdentifier:accessControl:) - public class func 🚫swift_sharedAccessGroupValet(with identifier: String, accessControl: SecureEnclaveAccessControl) -> SinglePromptSecureEnclaveValet? { - guard let identifier = Identifier(nonEmpty: identifier) else { + /// - 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 { return nil } return sharedAccessGroupValet(with: identifier, accessControl: accessControl) diff --git a/Sources/Valet/Valet.swift b/Sources/Valet/Valet.swift index fcd3de9c..8d81316b 100644 --- a/Sources/Valet/Valet.swift +++ b/Sources/Valet/Valet.swift @@ -47,16 +47,16 @@ public final class Valet: NSObject { /// - identifier: A non-empty string that 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: Identifier, accessibility: Accessibility) -> Valet { - findOrCreate(identifier, configuration: .valet(accessibility), sharedAccessGroup: true) + public class func sharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessibility: Accessibility) -> Valet { + findOrCreate(identifier, configuration: .valet(accessibility)) } /// - Parameters: /// - identifier: A non-empty string that 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: Identifier, accessibility: CloudAccessibility) -> Valet { - findOrCreate(identifier, configuration: .iCloud(accessibility), sharedAccessGroup: true) + public class func iCloudSharedAccessGroupValet(with identifier: SharedAccessGroupIdentifier, accessibility: CloudAccessibility) -> Valet { + findOrCreate(identifier, configuration: .iCloud(accessibility)) } #if os(macOS) @@ -118,24 +118,33 @@ public final class Valet: NSObject { // MARK: Private Class Functions - private class func findOrCreate(_ identifier: Identifier, configuration: Configuration, sharedAccessGroup: Bool = false) -> Valet { - let service: Service = sharedAccessGroup ? .sharedAccessGroup(identifier, configuration) : .standard(identifier, configuration) + private class func findOrCreate(_ identifier: Identifier, configuration: Configuration) -> Valet { + let service: Service = .standard(identifier, configuration) let key = service.description as NSString if let existingValet = identifierToValetMap.object(forKey: key) { return existingValet } else { - let valet: Valet - if sharedAccessGroup { - valet = Valet(sharedAccess: identifier, configuration: configuration) - } else { - valet = Valet(identifier: identifier, configuration: configuration) - } + let valet = Valet(identifier: identifier, configuration: configuration) + identifierToValetMap.setObject(valet, forKey: key) + return valet + } + } + + private class func findOrCreate(_ identifier: SharedAccessGroupIdentifier, configuration: Configuration) -> Valet { + let service: Service = .sharedAccessGroup(identifier, configuration) + let key = service.description as NSString + if let existingValet = identifierToValetMap.object(forKey: key) { + return existingValet + + } else { + let valet = Valet(sharedAccess: identifier, configuration: configuration) identifierToValetMap.setObject(valet, forKey: key) return valet } } + #if os(macOS) private class func findOrCreate(explicitlySet identifier: Identifier, configuration: Configuration, sharedAccessGroup: Bool = false) -> Valet { let service: Service = sharedAccessGroup ? .sharedAccessGroupOverride(service: identifier, configuration) : .standardOverride(service: identifier, configuration) @@ -170,10 +179,10 @@ public final class Valet: NSObject { configuration: configuration) } - private convenience init(sharedAccess identifier: Identifier, configuration: Configuration) { + private convenience init(sharedAccess groupIdentifier: SharedAccessGroupIdentifier, configuration: Configuration) { self.init( - identifier: identifier, - service: .sharedAccessGroup(identifier, configuration), + identifier: groupIdentifier.asIdentifier, + service: .sharedAccessGroup(groupIdentifier, configuration), configuration: configuration) } @@ -363,13 +372,13 @@ public final class Valet: NSObject { let accessibilityDescription = "AccessibleAlways" let serviceAttribute: String switch service { - case .sharedAccessGroup: - serviceAttribute = Service.sharedAccessGroup(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) + case let .sharedAccessGroup(sharedAccessGroupIdentifier, _): + serviceAttribute = Service.sharedAccessGroup(with: configuration, identifier: sharedAccessGroupIdentifier, accessibilityDescription: accessibilityDescription) case .standard: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #if os(macOS) case .sharedAccessGroupOverride: - serviceAttribute = Service.sharedAccessGroup(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) + serviceAttribute = Service.sharedAccessGroup(with: configuration, explicitlySetIdentifier: identifier, accessibilityDescription: accessibilityDescription) case .standardOverride: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #endif @@ -398,13 +407,13 @@ public final class Valet: NSObject { let accessibilityDescription = "AccessibleAlwaysThisDeviceOnly" let serviceAttribute: String switch service { - case .sharedAccessGroup: + case let .sharedAccessGroup(identifier, _): serviceAttribute = Service.sharedAccessGroup(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, identifier: identifier, accessibilityDescription: accessibilityDescription) + serviceAttribute = Service.sharedAccessGroup(with: configuration, explicitlySetIdentifier: identifier, accessibilityDescription: accessibilityDescription) case .standardOverride: serviceAttribute = Service.standard(with: configuration, identifier: identifier, accessibilityDescription: accessibilityDescription) #endif @@ -474,13 +483,15 @@ extension Valet { } /// - Parameters: - /// - identifier: A non-empty string that must correspond with the value for keychain-access-groups in your Entitlements file. + /// - appIDPrefix: The application's App ID prefix. This string 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 cooresponds to a value in keychain-access-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(valetWithSharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_vanillaSharedAccessGroupValet(with identifier: String, accessibility: Accessibility) -> Valet? { - guard let identifier = Identifier(nonEmpty: identifier) else { + @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 { return nil } return sharedAccessGroupValet(with: identifier, accessibility: accessibility) @@ -491,9 +502,9 @@ 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(iCloudValetWithSharedAccessGroupIdentifier:accessibility:) - public class func 🚫swift_iCloudSharedAccessGroupValet(with identifier: String, accessibility: CloudAccessibility) -> Valet? { - guard let identifier = Identifier(nonEmpty: identifier) else { + @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 { return nil } return iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility) @@ -585,15 +596,27 @@ internal extension Valet { // MARK: Permutations - class func permutations(with identifier: Identifier, shared: Bool = false) -> [Valet] { + class func permutations(with identifier: Identifier) -> [Valet] { Accessibility.allCases.map { accessibility in - shared ? .sharedAccessGroupValet(with: identifier, accessibility: accessibility) : .valet(with: identifier, accessibility: accessibility) + .valet(with: identifier, accessibility: accessibility) + } + } + + class func permutations(with identifier: SharedAccessGroupIdentifier) -> [Valet] { + Accessibility.allCases.map { accessibility in + .sharedAccessGroupValet(with: identifier, accessibility: accessibility) + } + } + + class func iCloudPermutations(with identifier: Identifier) -> [Valet] { + CloudAccessibility.allCases.map { cloudAccessibility in + .iCloudValet(with: identifier, accessibility: cloudAccessibility) } } - class func iCloudPermutations(with identifier: Identifier, shared: Bool = false) -> [Valet] { + class func iCloudPermutations(with identifier: SharedAccessGroupIdentifier) -> [Valet] { CloudAccessibility.allCases.map { cloudAccessibility in - shared ? .iCloudSharedAccessGroupValet(with: identifier, accessibility: cloudAccessibility) : .iCloudValet(with: identifier, accessibility: cloudAccessibility) + .iCloudSharedAccessGroupValet(with: identifier, accessibility: cloudAccessibility) } } diff --git a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/SynchronizableBackwardsCompatibilityTests.swift b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/SynchronizableBackwardsCompatibilityTests.swift index e6b73f21..970c828b 100644 --- a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/SynchronizableBackwardsCompatibilityTests.swift +++ b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/SynchronizableBackwardsCompatibilityTests.swift @@ -47,7 +47,7 @@ extension CloudIntegrationTests { return } - try Valet.iCloudCurrentAndLegacyPermutations(with: Valet.sharedAccessGroupIdentifier, shared: true).forEach { permutation, legacyValet in + try Valet.iCloudCurrentAndLegacyPermutations(with: Valet.sharedAccessGroupIdentifier).forEach { permutation, legacyValet in legacyValet.setString(passcode, forKey: key) XCTAssertNotNil(legacyValet.string(forKey: key)) diff --git a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift index ec509b67..b97cdeb4 100644 --- a/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift +++ b/Tests/ValetIntegrationTests/BackwardsCompatibilityTests/ValetBackwardsCompatibilityTests.swift @@ -30,7 +30,18 @@ import XCTest internal extension Valet { var legacyIdentifier: String { - return identifier.description + switch service { + case let .sharedAccessGroup(sharedAccessGroupIdentifier, _): + return sharedAccessGroupIdentifier.groupIdentifier + case let .standard(identifier, _): + return identifier.description + #if os(macOS) + case let .sharedAccessGroupOverride(identifier, _): + return identifier.description + case let .standardOverride(identifier, _): + return identifier.description + #endif + } } var legacyAccessibility: VALLegacyAccessibility { @@ -77,14 +88,26 @@ internal extension Valet { // MARK: Permutations - class func currentAndLegacyPermutations(with identifier: Identifier, shared: Bool = false) -> [(Valet, VALLegacyValet)] { - permutations(with: identifier, shared: shared).map { + class func currentAndLegacyPermutations(with identifier: Identifier) -> [(Valet, VALLegacyValet)] { + permutations(with: identifier).map { + ($0, $0.legacyValet) + } + } + + class func currentAndLegacyPermutations(with identifier: SharedAccessGroupIdentifier) -> [(Valet, VALLegacyValet)] { + permutations(with: identifier).map { ($0, $0.legacyValet) } } - class func iCloudCurrentAndLegacyPermutations(with identifier: Identifier, shared: Bool = false) -> [(Valet, VALSynchronizableValet)] { - iCloudPermutations(with: identifier, shared: shared).map { + class func iCloudCurrentAndLegacyPermutations(with identifier: Identifier) -> [(Valet, VALSynchronizableValet)] { + iCloudPermutations(with: identifier).map { + ($0, $0.legacyValet as! VALSynchronizableValet) + } + } + + class func iCloudCurrentAndLegacyPermutations(with identifier: SharedAccessGroupIdentifier) -> [(Valet, VALSynchronizableValet)] { + iCloudPermutations(with: identifier).map { ($0, $0.legacyValet as! VALSynchronizableValet) } } @@ -114,7 +137,7 @@ class ValetBackwardsCompatibilityIntegrationTests: ValetIntegrationTests { guard testEnvironmentIsSigned() else { return } - try Valet.currentAndLegacyPermutations(with: Valet.sharedAccessGroupIdentifier, shared: true).forEach { permutation, legacyValet in + try Valet.currentAndLegacyPermutations(with: Valet.sharedAccessGroupIdentifier).forEach { permutation, legacyValet in legacyValet.setString(passcode, forKey: key) XCTAssertNotNil(legacyValet.string(forKey: key)) @@ -149,7 +172,7 @@ class ValetBackwardsCompatibilityIntegrationTests: ValetIntegrationTests { guard testEnvironmentIsSigned() else { return } - let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.description, accessibility: .always)! + let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.groupIdentifier, accessibility: .always)! alwaysAccessibleLegacyValet.setString(passcode, forKey: key) let valet = Valet.sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlock) @@ -161,7 +184,7 @@ class ValetBackwardsCompatibilityIntegrationTests: ValetIntegrationTests { guard testEnvironmentIsSigned() else { return } - let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.description, accessibility: .alwaysThisDeviceOnly)! + let alwaysAccessibleLegacyValet = VALLegacyValet(sharedAccessGroupIdentifier: Valet.sharedAccessGroupIdentifier.groupIdentifier, accessibility: .alwaysThisDeviceOnly)! alwaysAccessibleLegacyValet.setString(passcode, forKey: key) let valet = Valet.sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessibility: .afterFirstUnlockThisDeviceOnly) diff --git a/Tests/ValetIntegrationTests/CloudIntegrationTests.swift b/Tests/ValetIntegrationTests/CloudIntegrationTests.swift index fd28433b..c9db877b 100644 --- a/Tests/ValetIntegrationTests/CloudIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/CloudIntegrationTests.swift @@ -30,7 +30,7 @@ class CloudIntegrationTests: XCTestCase static let accessibility = CloudAccessibility.whenUnlocked var allPermutations: [Valet] { return (testEnvironmentIsSigned() - ? Valet.iCloudPermutations(with: CloudIntegrationTests.identifier) + Valet.iCloudPermutations(with: ValetIntegrationTests.identifier, shared: true) + ? Valet.iCloudPermutations(with: CloudIntegrationTests.identifier.asIdentifier) + Valet.iCloudPermutations(with: CloudIntegrationTests.identifier) : []) } let key = "key" diff --git a/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift b/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift index 6d020e80..9c1cea83 100644 --- a/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/SecureEnclaveIntegrationTests.swift @@ -95,22 +95,9 @@ class SecureEnclaveIntegrationTests: XCTestCase guard testEnvironmentIsSigned() else { return } - - let sharedAccessGroupIdentifier: Identifier - #if os(iOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-iOS-Test-Host-App")! - #elseif os(macOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! - #elseif os(tvOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-tvOS-Test-Host-App")! - #elseif os(watchOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension")! - #else - XCTFail() - #endif let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in - return .sharedAccessGroupValet(with: sharedAccessGroupIdentifier, accessControl: accessControl) + return .sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) } for permutation in permutations { diff --git a/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift b/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift index 8fe5527f..39dc7e2c 100644 --- a/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/SinglePromptSecureEnclaveIntegrationTests.swift @@ -151,19 +151,8 @@ class SinglePromptSecureEnclaveIntegrationTests: XCTestCase return } - let sharedAccessGroupIdentifier: Identifier - #if os(iOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-iOS-Test-Host-App")! - #elseif os(macOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-macOS-Test-Host-App")! - #elseif os(tvOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "com.squareup.Valet-tvOS-Test-Host-App")! - #else - XCTFail() - #endif - let permutations: [SecureEnclaveValet] = SecureEnclaveAccessControl.allValues().compactMap { accessControl in - return .sharedAccessGroupValet(with: sharedAccessGroupIdentifier, accessControl: accessControl) + return .sharedAccessGroupValet(with: Valet.sharedAccessGroupIdentifier, accessControl: accessControl) } for permutation in permutations { diff --git a/Tests/ValetIntegrationTests/ValetIntegrationTests.swift b/Tests/ValetIntegrationTests/ValetIntegrationTests.swift index fd39ec6b..36ac3c45 100644 --- a/Tests/ValetIntegrationTests/ValetIntegrationTests.swift +++ b/Tests/ValetIntegrationTests/ValetIntegrationTests.swift @@ -60,20 +60,18 @@ internal extension Valet { // MARK: Shared Access Group - static var sharedAccessGroupIdentifier: Identifier = { - let sharedAccessGroupIdentifier: Identifier + static var sharedAccessGroupIdentifier: SharedAccessGroupIdentifier = { #if os(iOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-iOS-Test-Host-App")! + return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-iOS-Test-Host-App")! #elseif os(macOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-macOS-Test-Host-App")! + return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-macOS-Test-Host-App")! #elseif os(tvOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.Valet-tvOS-Test-Host-App")! + return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.Valet-tvOS-Test-Host-App")! #elseif os(watchOS) - sharedAccessGroupIdentifier = Identifier(nonEmpty: "9XUJ7M53NG.com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension")! + return SharedAccessGroupIdentifier(appIDPrefix: "9XUJ7M53NG", nonEmptyGroup: "com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension")! #else XCTFail() #endif - return sharedAccessGroupIdentifier }() } @@ -81,15 +79,15 @@ internal extension Valet { class ValetIntegrationTests: XCTestCase { - static let identifier = Valet.sharedAccessGroupIdentifier + static let sharedAccessGroupIdentifier = Valet.sharedAccessGroupIdentifier var allPermutations: [Valet] { - return Valet.permutations(with: ValetIntegrationTests.identifier) - + (testEnvironmentIsSigned() ? Valet.permutations(with: ValetIntegrationTests.identifier, shared: true) : []) + return Valet.permutations(with: ValetIntegrationTests.sharedAccessGroupIdentifier.asIdentifier) + + (testEnvironmentIsSigned() ? Valet.permutations(with: ValetIntegrationTests.sharedAccessGroupIdentifier) : []) } - let vanillaValet = Valet.valet(with: identifier, accessibility: .whenUnlocked) + let vanillaValet = Valet.valet(with: sharedAccessGroupIdentifier.asIdentifier, accessibility: .whenUnlocked) // FIXME: Need a different flavor (Synchronizable must be tested in a signed environment) - let anotherFlavor = Valet.iCloudValet(with: identifier, accessibility: .whenUnlocked) + let anotherFlavor = Valet.iCloudValet(with: sharedAccessGroupIdentifier.asIdentifier, accessibility: .whenUnlocked) let key = "key" let passcode = "topsecret" @@ -131,7 +129,7 @@ class ValetIntegrationTests: XCTestCase } func test_init_createsCorrectBackingService_sharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier Accessibility.allCases.forEach { accessibility in let backingService = Valet.sharedAccessGroupValet(with: identifier, accessibility: accessibility).service @@ -149,7 +147,7 @@ class ValetIntegrationTests: XCTestCase } func test_init_createsCorrectBackingService_cloudSharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier CloudAccessibility.allCases.forEach { accessibility in let backingService = Valet.iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility).service @@ -172,7 +170,7 @@ class ValetIntegrationTests: XCTestCase return } - Valet.permutations(with: Valet.sharedAccessGroupIdentifier, shared: true).forEach { permutation in + Valet.permutations(with: Valet.sharedAccessGroupIdentifier).forEach { permutation in XCTAssertTrue(permutation.canAccessKeychain(), "\(permutation) could not access keychain.") } } diff --git a/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m index 1625d0cf..03bca127 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALSecureEnclaveValetTests.m @@ -26,6 +26,25 @@ - (NSString *)identifier; return @"identifier"; } +- (NSString *)appIDPrefix; +{ + return @"9XUJ7M53NG"; +} + +- (NSString *)sharedAccessGroupIdentifier; +{ +#if TARGET_OS_IPHONE + return @"com.squareup.Valet-iOS-Test-Host-App"; +#elif TARGET_OS_WATCH + return @"com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension"; +#elif TARGET_OS_MAC + return @"com.squareup.Valet-macOS-Test-Host-App"; +#else + // This will fail + return @""; +#endif +} + - (void)test_valetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet valetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; @@ -62,35 +81,35 @@ - (void)test_valetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); XCTAssertEqual([valet class], [VALSecureEnclaveValet class]); } - (void)test_sharedAccessGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSecureEnclaveValet *const valet = [VALSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertNil(valet); } diff --git a/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m index 6f551d22..e3f17d68 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALSinglePromptSecureEnclaveValetTests.m @@ -26,6 +26,25 @@ - (NSString *)identifier; return @"identifier"; } +- (NSString *)appIDPrefix; +{ + return @"9XUJ7M53NG"; +} + +- (NSString *)sharedAccessGroupIdentifier; +{ +#if TARGET_OS_IPHONE + return @"com.squareup.Valet-iOS-Test-Host-App"; +#elif TARGET_OS_WATCH + return @"com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension"; +#elif TARGET_OS_MAC + return @"com.squareup.Valet-macOS-Test-Host-App"; +#else + // This will fail + return @""; +#endif +} + - (void)test_valetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { if (@available(tvOS 11.0, *)) { @@ -73,7 +92,7 @@ - (void)test_valetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlDevicePasscode; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlDevicePasscode]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlDevicePasscode); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -82,7 +101,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlUserPresence; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlUserPresence]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlUserPresence]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlUserPresence); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -91,7 +110,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricAny; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricAny]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricAny); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -100,7 +119,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectValet_VALSecureEnclaveAccessControlBiometricCurrentSet; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithIdentifier:self.identifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertEqual(valet.accessControl, VALSecureEnclaveAccessControlBiometricCurrentSet); XCTAssertEqual([valet class], [VALSinglePromptSecureEnclaveValet class]); } @@ -109,7 +128,7 @@ - (void)test_sharedAccessGroupValetWithIdentifier_accessControl_returnsCorrectVa - (void)test_sharedAccessGroupValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { if (@available(tvOS 11.0, *)) { - VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; + VALSinglePromptSecureEnclaveValet *const valet = [VALSinglePromptSecureEnclaveValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessControl:VALSecureEnclaveAccessControlBiometricCurrentSet]; XCTAssertNil(valet); } } diff --git a/Tests/ValetObjectiveCBridgeTests/VALValetTests.m b/Tests/ValetObjectiveCBridgeTests/VALValetTests.m index a30004c2..c29c482d 100644 --- a/Tests/ValetObjectiveCBridgeTests/VALValetTests.m +++ b/Tests/ValetObjectiveCBridgeTests/VALValetTests.m @@ -26,6 +26,25 @@ - (NSString *)identifier; return @"identifier"; } +- (NSString *)appIDPrefix; +{ + return @"9XUJ7M53NG"; +} + +- (NSString *)sharedAccessGroupIdentifier; +{ +#if TARGET_OS_IPHONE + return @"com.squareup.Valet-iOS-Test-Host-App"; +#elif TARGET_OS_WATCH + return @"com.squareup.ValetTouchIDTestApp.watchkitapp.watchkitextension"; +#elif TARGET_OS_MAC + return @"com.squareup.Valet-macOS-Test-Host-App"; +#else + // This will fail + return @""; +#endif +} + - (void)test_valetWithIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; { VALValet *const valet = [VALValet valetWithIdentifier:self.identifier accessibility:VALAccessibilityWhenUnlocked]; @@ -89,62 +108,62 @@ - (void)test_iCloudValetWithIdentifier_accessibility_returnsNilWhenIdentifierIsE - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenPasscodeSetThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenPasscodeSetThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenPasscodeSetThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityWhenUnlockedThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityWhenUnlockedThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlockedThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALAccessibilityAfterFirstUnlockThisDeviceOnly; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlockThisDeviceOnly); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_valetWithSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet valetWithSharedAccessGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; + VALValet *const valet = [VALValet sharedAccessGroupValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessibility:VALAccessibilityAfterFirstUnlockThisDeviceOnly]; XCTAssertNil(valet); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityWhenUnlocked; { - VALValet *const valet = [VALValet iCloudValetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALCloudAccessibilityWhenUnlocked]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityWhenUnlocked]; XCTAssertEqual(valet.accessibility, VALAccessibilityWhenUnlocked); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsCorrectValet_VALCloudAccessibilityAfterFirstUnlock; { - VALValet *const valet = [VALValet iCloudValetWithSharedAccessGroupIdentifier:self.identifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:self.sharedAccessGroupIdentifier accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertEqual(valet.accessibility, VALAccessibilityAfterFirstUnlock); XCTAssertEqual([valet class], [VALValet class]); } - (void)test_iCloudValetWithSharedAccessGroupIdentifier_accessibility_returnsNilWhenIdentifierIsEmpty; { - VALValet *const valet = [VALValet iCloudValetWithSharedAccessGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; + VALValet *const valet = [VALValet iCloudValetWithAppIDPrefix:self.appIDPrefix sharedAccessGroupIdentifier:@"" accessibility:VALCloudAccessibilityAfterFirstUnlock]; XCTAssertNil(valet); } diff --git a/Tests/ValetTests/SecureEnclaveTests.swift b/Tests/ValetTests/SecureEnclaveTests.swift index 734c2cea..7ad2a763 100644 --- a/Tests/ValetTests/SecureEnclaveTests.swift +++ b/Tests/ValetTests/SecureEnclaveTests.swift @@ -41,7 +41,7 @@ class SecureEnclaveTests: XCTestCase } func test_init_createsCorrectBackingService_sharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier SecureEnclaveAccessControl.allValues().forEach { accessControl in let backingService = SecureEnclaveValet.sharedAccessGroupValet(with: identifier, accessControl: accessControl).service diff --git a/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift b/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift index 8dad1dc7..ebc22717 100644 --- a/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift +++ b/Tests/ValetTests/SinglePromptSecureEnclaveTests.swift @@ -43,7 +43,7 @@ class SinglePromptSecureEnclaveTests: XCTestCase } func test_init_createsCorrectBackingService_sharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier SecureEnclaveAccessControl.allValues().forEach { accessControl in let backingService = SinglePromptSecureEnclaveValet.sharedAccessGroupValet(with: identifier, accessControl: accessControl).service diff --git a/Tests/ValetTests/ValetTests.swift b/Tests/ValetTests/ValetTests.swift index 0dc86b06..a0fce3f8 100644 --- a/Tests/ValetTests/ValetTests.swift +++ b/Tests/ValetTests/ValetTests.swift @@ -41,7 +41,7 @@ class ValetTests: XCTestCase } func test_init_createsCorrectBackingService_sharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier Accessibility.allCases.forEach { accessibility in let backingService = Valet.sharedAccessGroupValet(with: identifier, accessibility: accessibility).service @@ -59,7 +59,7 @@ class ValetTests: XCTestCase } func test_init_createsCorrectBackingService_cloudSharedAccess() { - let identifier = ValetTests.identifier + let identifier = Valet.sharedAccessGroupIdentifier CloudAccessibility.allCases.forEach { accessibility in let backingService = Valet.iCloudSharedAccessGroupValet(with: identifier, accessibility: accessibility).service diff --git a/Valet.xcodeproj/project.pbxproj b/Valet.xcodeproj/project.pbxproj index e5903e78..01480f89 100644 --- a/Valet.xcodeproj/project.pbxproj +++ b/Valet.xcodeproj/project.pbxproj @@ -128,25 +128,29 @@ 165CDDCB204B26D400C96C2E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165CDDCA204B26D400C96C2E /* ViewController.swift */; }; 165CDDCE204B26D400C96C2E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 165CDDCC204B26D400C96C2E /* Main.storyboard */; }; 165CDDD0204B26D500C96C2E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 165CDDCF204B26D500C96C2E /* Assets.xcassets */; }; - 167E251023D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; - 167E251123D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; - 167E251223D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; 167E24F123D4B89800889121 /* SinglePromptSecureEnclaveIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1612FD0622A9C95400FC1142 /* SinglePromptSecureEnclaveIntegrationTests.swift */; }; - 167E250723D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; - 167E250823D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; - 167E250923D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; 167E24FE23D6235000889121 /* KeychainErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E24FD23D6235000889121 /* KeychainErrorTests.swift */; }; 167E24FF23D6235000889121 /* KeychainErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E24FD23D6235000889121 /* KeychainErrorTests.swift */; }; 167E250023D6235000889121 /* KeychainErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E24FD23D6235000889121 /* KeychainErrorTests.swift */; }; 167E250223D624EF00889121 /* MigrationErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250123D624EF00889121 /* MigrationErrorTests.swift */; }; 167E250323D624EF00889121 /* MigrationErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250123D624EF00889121 /* MigrationErrorTests.swift */; }; 167E250423D624EF00889121 /* MigrationErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250123D624EF00889121 /* MigrationErrorTests.swift */; }; + 167E250723D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; + 167E250823D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; + 167E250923D62CAA00889121 /* CloudAccessibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */; }; + 167E251023D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; + 167E251123D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; + 167E251223D6328E00889121 /* ConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167E250F23D6328E00889121 /* ConfigurationTests.swift */; }; 168909381F7199D60057F636 /* Valet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26E6827C1BA8B3F900EFF4EA /* Valet.framework */; }; 168909391F7199D60057F636 /* Valet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 26E6827C1BA8B3F900EFF4EA /* Valet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 1693E29523B2D24600F8D97A /* KeychainError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1693E29423B2D24600F8D97A /* KeychainError.swift */; }; 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 */; }; 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 */; }; @@ -417,11 +421,12 @@ 165CDDCF204B26D500C96C2E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 165CDDD1204B26D500C96C2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 165CDDD5204B26F700C96C2E /* Valet tvOS Test Host App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Valet tvOS Test Host App.entitlements"; sourceTree = ""; }; - 167E250F23D6328E00889121 /* ConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationTests.swift; sourceTree = ""; }; - 167E250623D62CAA00889121 /* CloudAccessibilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudAccessibilityTests.swift; sourceTree = ""; }; 167E24FD23D6235000889121 /* KeychainErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainErrorTests.swift; sourceTree = ""; }; 167E250123D624EF00889121 /* MigrationErrorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationErrorTests.swift; sourceTree = ""; }; + 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 = ""; }; 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 = ""; }; @@ -635,6 +640,7 @@ 1612FD7822A9CAAB00FC1142 /* SecureEnclaveAccessControl.swift */, 1612FD7922A9CAAB00FC1142 /* SinglePromptSecureEnclaveValet.swift */, 1612FD7B22A9CAAB00FC1142 /* SecureEnclaveValet.swift */, + 16967AE72405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift */, 1612FD7C22A9CAAB00FC1142 /* Identifier.swift */, 1612FD7022A9CAAB00FC1142 /* Internal */, ); @@ -1552,6 +1558,7 @@ 1612FD9F22A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA322A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD7F22A9CAAB00FC1142 /* MigrationError.swift in Sources */, + 16967AEA2405CC7500DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, 1612FD9722A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1596,6 +1603,7 @@ 1612FDA022A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA422A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD8022A9CAAB00FC1142 /* MigrationError.swift in Sources */, + 16967AEB2405CC7600DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, 1612FD9822A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1613,6 +1621,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 16967AE82405CAF800DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, 1693E29523B2D24600F8D97A /* KeychainError.swift in Sources */, 1612FDB922A9CAAB00FC1142 /* SecureEnclaveValet.swift in Sources */, 1612FD8D22A9CAAB00FC1142 /* Valet.swift in Sources */, @@ -1647,6 +1656,7 @@ 1612FD9E22A9CAAB00FC1142 /* Configuration.swift in Sources */, 1612FDA222A9CAAB00FC1142 /* Accessibility.swift in Sources */, 1612FD7E22A9CAAB00FC1142 /* MigrationError.swift in Sources */, + 16967AE92405CC7400DC2B2D /* SharedAccessGroupIdentifier.swift in Sources */, 1612FD9622A9CAAB00FC1142 /* Service.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;