From c9e6e6f2538fbe6924187e40ea7db8a3760055af Mon Sep 17 00:00:00 2001 From: Eric Jensen Date: Tue, 27 Sep 2022 13:21:18 -0400 Subject: [PATCH] =?UTF-8?q?Enable=20updating=20an=20item=E2=80=99s=20acces?= =?UTF-8?q?sible=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the query to fetch the existing item was requiring that the existing item’s accessibility attribute was the same as the new accessibility attribute. This would return no results, which caused the new item code path to be executed and failed since there is an existing item, just with a different accessibility attribute --- Sources/Keychain.swift | 10 +--------- Tests/KeychainTests.swift | 5 +++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Sources/Keychain.swift b/Sources/Keychain.swift index 4ed26f8..22575ae 100644 --- a/Sources/Keychain.swift +++ b/Sources/Keychain.swift @@ -75,12 +75,11 @@ public final class Keychain { public func store(_ storable: T, service: String = defaultService, accessGroup: String? = defaultAccessGroup) throws { let newData = try JSONEncoder().encode(storable) - var query = self.query(for: storable, service: service, accessGroup: accessGroup) + var query = self.query(forAccount: storable.account, service: service, accessGroup: accessGroup) let existingData = try data(forAccount: storable.account, service: service, accessGroup: accessGroup) var status = noErr let newAttributes: [String: Any] = [Constants.valueData: newData, Constants.accessible: storable.accessible.rawValue] if existingData != nil { - try data(forAccount: storable.account, service: service, accessGroup: accessGroup) status = securityItemManager.update(withQuery: query, attributesToUpdate: newAttributes) } else { query.merge(newAttributes) { $1 } @@ -148,15 +147,8 @@ public final class Keychain { return query } - func query(for storable: KeychainStorable, service: String, accessGroup: String?) -> [String: Any] { - var query = self.query(forAccount: storable.account, service: service, accessGroup: accessGroup) - query[Constants.accessible] = storable.accessible.rawValue - return query - } - // MARK: - Data - @discardableResult func data(forAccount account: String, service: String, accessGroup: String?) throws -> Data? { var query = self.query(forAccount: account, service: service, accessGroup: accessGroup) query[Constants.matchLimit] = Constants.matchLimitOne diff --git a/Tests/KeychainTests.swift b/Tests/KeychainTests.swift index 434d09d..5eba189 100644 --- a/Tests/KeychainTests.swift +++ b/Tests/KeychainTests.swift @@ -56,6 +56,9 @@ final class MockSecurityItemManager: SecurityItemManaging { } struct Credential: KeychainStorable { + static var accessible: Keychain.AccessibleOption = .whenUnlocked + var accessible: Keychain.AccessibleOption { Self.accessible } + let email: String let password: String let pin: Int @@ -109,6 +112,7 @@ class KeychainTests: XCTestCase { } func cleanup() { + Credential.accessible = .whenUnlocked Keychain.resetDefaults() do { let credentials = [credential, credentialTwo] @@ -168,6 +172,7 @@ class KeychainTests: XCTestCase { XCTAssertEqual(retrievedValue?.password, credential.password) XCTAssertEqual(retrievedValue?.pin, credential.pin) XCTAssertEqual(retrievedValue?.dob, credential.dob) + Credential.accessible = .afterFirstUnlock XCTAssertNoThrow(try keychain.store(updatedCredential)) let updatedValue: Credential? = try! keychain.retrieveValue(forAccount: Email.test) XCTAssertNotNil(updatedValue)