diff --git a/ui/BankingiOSApp/BankingiOSApp/Security/KeychainPasswordItem.swift b/ui/BankingiOSApp/BankingiOSApp/Security/KeychainPasswordItem.swift index 2d8f8c91..1f283f95 100644 --- a/ui/BankingiOSApp/BankingiOSApp/Security/KeychainPasswordItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/Security/KeychainPasswordItem.swift @@ -68,18 +68,19 @@ struct KeychainPasswordItem { let service: String - private(set) var account: String + private(set) var account: String? let accessGroup: String? // MARK: Intialization - init(service: String, account: String, accessGroup: String? = nil) { + init(service: String, account: String?, accessGroup: String? = nil) { self.service = service self.account = account self.accessGroup = accessGroup } + // MARK: Keychain access func readPassword() throws -> String { @@ -87,7 +88,7 @@ struct KeychainPasswordItem { Build a query to find the item that matches the service, account and access group. */ - var query = KeychainPasswordItem.keychainQuery(withService: service, account: account, accessGroup: accessGroup) + var query = keychainQuery() query[kSecMatchLimit as String] = kSecMatchLimitOne query[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnData as String] = kCFBooleanTrue @@ -125,7 +126,7 @@ struct KeychainPasswordItem { var attributesToUpdate = [String : 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) // 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 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? // Add a the new item to the keychain. @@ -152,7 +153,7 @@ struct KeychainPasswordItem { var attributesToUpdate = [String : 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) // Throw an error if an unexpected status was returned. @@ -163,16 +164,42 @@ struct KeychainPasswordItem { func deleteItem() throws { // 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) // Throw an error if an unexpected status was returned. 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] { // 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[kSecReturnAttributes as String] = kCFBooleanTrue query[kSecReturnData as String] = kCFBooleanFalse @@ -204,21 +231,4 @@ struct KeychainPasswordItem { 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 - } }