Refactored KeychainPasswordItem so that we don't have to pass all class fields to keychainQuery()

This commit is contained in:
dankito 2020-10-02 02:27:42 +02:00
parent 543a9d81b9
commit 48841b5214
1 changed files with 35 additions and 25 deletions

View File

@ -68,18 +68,19 @@ struct KeychainPasswordItem {
let service: String let service: String
private(set) var account: String private(set) var account: String?
let accessGroup: String? let accessGroup: String?
// MARK: Intialization // MARK: Intialization
init(service: String, account: String, accessGroup: String? = nil) { init(service: String, account: String?, accessGroup: String? = nil) {
self.service = service self.service = service
self.account = account self.account = account
self.accessGroup = accessGroup self.accessGroup = accessGroup
} }
// MARK: Keychain access // MARK: Keychain access
func readPassword() throws -> String { func readPassword() throws -> String {
@ -87,7 +88,7 @@ struct KeychainPasswordItem {
Build a query to find the item that matches the service, account and Build a query to find the item that matches the service, account and
access group. access group.
*/ */
var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) var query = keychainQuery()
query[kSecMatchLimit as String] = kSecMatchLimitOne query[kSecMatchLimit as String] = kSecMatchLimitOne
query[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnAttributes as String] = kCFBooleanTrue
query[kSecReturnData as String] = kCFBooleanTrue query[kSecReturnData as String] = kCFBooleanTrue
@ -125,7 +126,7 @@ struct KeychainPasswordItem {
var attributesToUpdate = [String : AnyObject]() var attributesToUpdate = [String : AnyObject]()
attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject? attributesToUpdate[kSecValueData as String] = encodedPassword as AnyObject?
let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) let query = keychainQuery()
let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
// Throw an error if an unexpected status was returned. // Throw an error if an unexpected status was returned.
@ -136,7 +137,7 @@ struct KeychainPasswordItem {
No password was found in the keychain. Create a dictionary to save No password was found in the keychain. Create a dictionary to save
as a new keychain item. as a new keychain item.
*/ */
var newItem = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) var newItem = keychainQuery()
newItem[kSecValueData as String] = encodedPassword as AnyObject? newItem[kSecValueData as String] = encodedPassword as AnyObject?
// Add a the new item to the keychain. // Add a the new item to the keychain.
@ -152,7 +153,7 @@ struct KeychainPasswordItem {
var attributesToUpdate = [String : AnyObject]() var attributesToUpdate = [String : AnyObject]()
attributesToUpdate[kSecAttrAccount as String] = newAccountName as AnyObject? attributesToUpdate[kSecAttrAccount as String] = newAccountName as AnyObject?
let query = KeychainPasswordItem.keychainQuery(withService: service, account: self.account, accessGroup: accessGroup) let query = keychainQuery()
let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
// Throw an error if an unexpected status was returned. // Throw an error if an unexpected status was returned.
@ -163,16 +164,42 @@ struct KeychainPasswordItem {
func deleteItem() throws { func deleteItem() throws {
// Delete the existing item from the keychain. // Delete the existing item from the keychain.
let query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) let query = keychainQuery()
let status = SecItemDelete(query as CFDictionary) let status = SecItemDelete(query as CFDictionary)
// Throw an error if an unexpected status was returned. // Throw an error if an unexpected status was returned.
guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) } guard status == noErr || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) }
} }
private func keychainQuery() -> [String : AnyObject] {
var query = [String : AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrService as String] = service as AnyObject?
if let account = account {
query[kSecAttrAccount as String] = account as AnyObject?
}
if let accessGroup = accessGroup {
query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
}
if let accessControl = accessControl {
query[kSecAttrAccessControl as String] = accessControl as AnyObject?
}
if let authenticationContext = authenticationContext {
query[kSecUseAuthenticationContext as String] = authenticationContext as AnyObject?
}
return query
}
static func passwordItems(forService service: String, accessGroup: String? = nil) throws -> [KeychainPasswordItem] { static func passwordItems(forService service: String, accessGroup: String? = nil) throws -> [KeychainPasswordItem] {
// Build a query for all items that match the service and access group. // Build a query for all items that match the service and access group.
var query = KeychainPasswordItem.keychainQuery(withService: service, accessGroup: accessGroup) var query = KeychainPasswordItem(service: service, account: nil, accessGroup: accessGroup).keychainQuery()
query[kSecMatchLimit as String] = kSecMatchLimitAll query[kSecMatchLimit as String] = kSecMatchLimitAll
query[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnAttributes as String] = kCFBooleanTrue
query[kSecReturnData as String] = kCFBooleanFalse query[kSecReturnData as String] = kCFBooleanFalse
@ -204,21 +231,4 @@ struct KeychainPasswordItem {
return passwordItems return passwordItems
} }
// MARK: Convenience
private static func keychainQuery(withService service: String, account: String? = nil, accessGroup: String? = nil) -> [String : AnyObject] {
var query = [String : AnyObject]()
query[kSecClass as String] = kSecClassGenericPassword
query[kSecAttrService as String] = service as AnyObject?
if let account = account {
query[kSecAttrAccount as String] = account as AnyObject?
}
if let accessGroup = accessGroup {
query[kSecAttrAccessGroup as String] = accessGroup as AnyObject?
}
return query
}
} }