diff --git a/Sources/BrowserServicesKit/Autofill/AutofillUserScript+SecureVault.swift b/Sources/BrowserServicesKit/Autofill/AutofillUserScript+SecureVault.swift index ca5170f5c..8fa8ab389 100644 --- a/Sources/BrowserServicesKit/Autofill/AutofillUserScript+SecureVault.swift +++ b/Sources/BrowserServicesKit/Autofill/AutofillUserScript+SecureVault.swift @@ -31,7 +31,7 @@ public protocol AutofillSecureVaultDelegate: AnyObject { var tld: TLD? { get } func autofillUserScript(_: AutofillUserScript, didRequestAutoFillInitDataForDomain domain: String, completionHandler: @escaping ( - [SecureVaultModels.WebsiteAccount], + [SecureVaultModels.WebsiteCredentials], [SecureVaultModels.Identity], [SecureVaultModels.CreditCard], SecureVaultModels.CredentialsProvider @@ -434,8 +434,8 @@ extension AutofillUserScript { func getAvailableInputTypes(_ message: UserScriptMessage, _ replyHandler: @escaping MessageReplyHandler) { let domain = hostForMessage(message) let email = emailDelegate?.autofillUserScriptDidRequestSignedInStatus(self) ?? false - vaultDelegate?.autofillUserScript(self, didRequestAutoFillInitDataForDomain: domain) { accounts, identities, cards, credentialsProvider in - let response = RequestAvailableInputTypesResponse(accounts: accounts, + vaultDelegate?.autofillUserScript(self, didRequestAutoFillInitDataForDomain: domain) { credentials, identities, cards, credentialsProvider in + let response = RequestAvailableInputTypesResponse(credentials: credentials, identities: identities, cards: cards, email: email, @@ -515,28 +515,31 @@ extension AutofillUserScript { func pmGetAutoFillInitData(_ message: UserScriptMessage, _ replyHandler: @escaping MessageReplyHandler) { let domain = hostForMessage(message) - vaultDelegate?.autofillUserScript(self, didRequestAutoFillInitDataForDomain: domain) { accounts, identities, cards, credentialsProvider in - let credentials: [CredentialObject] + vaultDelegate?.autofillUserScript(self, didRequestAutoFillInitDataForDomain: domain) { credentials, identities, cards, credentialsProvider in + let credentialObjects: [CredentialObject] if credentialsProvider.locked { - credentials = [CredentialObject(id: "provider_locked", username: "", credentialsProvider: credentialsProvider.name.rawValue)] + credentialObjects = [CredentialObject(id: "provider_locked", username: "", credentialsProvider: credentialsProvider.name.rawValue)] } else { guard let autofillWebsiteAccountMatcher = self.vaultDelegate?.autofillWebsiteAccountMatcher else { - credentials = accounts.compactMap { - guard let id = $0.id else { return nil } - return CredentialObject(id: id, username: $0.username ?? "", credentialsProvider: credentialsProvider.name.rawValue) + credentialObjects = credentials.compactMap { + if let id = $0.account.id { + return CredentialObject(id: id, username: $0.account.username ?? "", credentialsProvider: credentialsProvider.name.rawValue) + } else { + return nil + } } return } - let accountMatches = autofillWebsiteAccountMatcher.findMatches(accounts: accounts, for: domain) - credentials = self.buildCredentialObjects(accountMatches, credentialsProvider: credentialsProvider) + let accountMatches = autofillWebsiteAccountMatcher.findMatches(accounts: credentials.map(\.account), for: domain) + credentialObjects = self.buildCredentialObjects(accountMatches, credentialsProvider: credentialsProvider) } let identities: [IdentityObject] = identities.compactMap(IdentityObject.from(identity:)) let cards: [CreditCardObject] = cards.compactMap(CreditCardObject.autofillInitializationValueFrom(card:)) let success = RequestAutoFillInitDataResponse.AutofillInitSuccess(serializedInputContext: self.serializedInputContext, - credentials: credentials, + credentials: credentialObjects, creditCards: cards, identities: identities) @@ -810,8 +813,8 @@ extension AutofillUserScript.RequestAvailableInputTypesResponse { cards: [SecureVaultModels.CreditCard], email: Bool, credentialsProvider: SecureVaultModels.CredentialsProvider) { - let username = credentialsProvider.locked || credentials.filter({ $0.account.username?.isEmpty == false }).count > 0 - let password = credentialsProvider.locked || credentials.count > 0 + let username = credentialsProvider.locked || credentials.hasAtLeastOneUsername + let password = credentialsProvider.locked || credentials.hasAtLeastOnePassword let credentials = AutofillUserScript.AvailableInputTypesSuccess.AvailableInputTypesCredentials(username: username, password: password) let success = AutofillUserScript.AvailableInputTypesSuccess( credentials: credentials, @@ -825,6 +828,22 @@ extension AutofillUserScript.RequestAvailableInputTypesResponse { } +private extension Array where Element == SecureVaultModels.WebsiteCredentials { + var hasAtLeastOneUsername: Bool { + let elementsWithUsername = filter { + $0.account.username?.isEmpty == false + } + return !elementsWithUsername.isEmpty + } + + var hasAtLeastOnePassword: Bool { + let elementsWithPassword = filter { + $0.password?.isEmpty == false + } + return !elementsWithPassword.isEmpty + } +} + extension AutofillUserScript.AvailableInputTypesSuccess.AvailableInputTypesIdentities { init(identities: [SecureVaultModels.Identity]) { diff --git a/Sources/BrowserServicesKit/SecureVault/AutofillDatabaseProvider.swift b/Sources/BrowserServicesKit/SecureVault/AutofillDatabaseProvider.swift index c5890bc62..b145523ea 100644 --- a/Sources/BrowserServicesKit/SecureVault/AutofillDatabaseProvider.swift +++ b/Sources/BrowserServicesKit/SecureVault/AutofillDatabaseProvider.swift @@ -29,6 +29,8 @@ public protocol AutofillDatabaseProvider: SecureStorageDatabaseProvider { @discardableResult func storeWebsiteCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) throws -> Int64 + func websiteCredentialsForDomain(_ domain: String) throws -> [SecureVaultModels.WebsiteCredentials] + func websiteCredentialsForTopLevelDomain(_ domain: String) throws -> [SecureVaultModels.WebsiteCredentials] func websiteCredentialsForAccountId(_ accountId: Int64) throws -> SecureVaultModels.WebsiteCredentials? func websiteAccountsForDomain(_ domain: String) throws -> [SecureVaultModels.WebsiteAccount] func websiteAccountsForTopLevelDomain(_ eTLDplus1: String) throws -> [SecureVaultModels.WebsiteAccount] @@ -386,6 +388,53 @@ public final class DefaultAutofillDatabaseProvider: GRDBSecureStorageDatabasePro return nil } + public func websiteCredentialsForDomain(_ domain: String) throws -> [SecureVaultModels.WebsiteCredentials] { + try db.read { + try websiteCredentialsForDomain(domain, in: $0) + } + } + + private func websiteCredentialsForDomain(_ domain: String, in database: Database) throws -> [SecureVaultModels.WebsiteCredentials] { + let accounts = try SecureVaultModels.WebsiteAccount + .filter(SecureVaultModels.WebsiteAccount.Columns.domain.like(domain)) + .fetchAll(database) + + return try websiteCredentialsForAccounts(accounts, in: database) + } + + private func websiteCredentialsForAccounts(_ accounts: [SecureVaultModels.WebsiteAccount], in database: Database) throws -> [SecureVaultModels.WebsiteCredentials] { + let accountIDs = accounts.compactMap(\.id) + + var result = [SecureVaultModels.WebsiteCredentials]() + + for accountID in accountIDs { + guard let accountIDInt = Int64(accountID) else { + continue + } + guard let credentials = try websiteCredentialsForAccountId(accountIDInt, in: database) else { + continue + } + result.append(credentials) + } + + return result + } + + public func websiteCredentialsForTopLevelDomain(_ eTLDplus1: String) throws -> [SecureVaultModels.WebsiteCredentials] { + try db.read { + return try websiteCredentialsForTopLevelDomain(eTLDplus1, in: $0) + } + } + + private func websiteCredentialsForTopLevelDomain(_ eTLDplus1: String, in database: Database) throws -> [SecureVaultModels.WebsiteCredentials] { + let query = SecureVaultModels.WebsiteAccount + .filter(Column(SecureVaultModels.WebsiteAccount.Columns.domain.name) == eTLDplus1 || + Column(SecureVaultModels.WebsiteAccount.Columns.domain.name).like("%." + eTLDplus1)) + let accounts = try query.fetchAll(database) + + return try websiteCredentialsForAccounts(accounts, in: database) + } + public func websiteCredentialsForAccountId(_ accountId: Int64) throws -> SecureVaultModels.WebsiteCredentials? { try db.read { try websiteCredentialsForAccountId(accountId, in: $0) diff --git a/Sources/BrowserServicesKit/SecureVault/AutofillSecureVault.swift b/Sources/BrowserServicesKit/SecureVault/AutofillSecureVault.swift index 348aabf83..79ffab561 100644 --- a/Sources/BrowserServicesKit/SecureVault/AutofillSecureVault.swift +++ b/Sources/BrowserServicesKit/SecureVault/AutofillSecureVault.swift @@ -61,6 +61,8 @@ public protocol AutofillSecureVault: SecureVault { func hasAccountFor(username: String?, domain: String?) throws -> Bool func updateLastUsedFor(accountId: Int64) throws + func websiteCredentialsFor(domain: String) throws -> [SecureVaultModels.WebsiteCredentials] + func websiteCredentialsWithPartialMatchesFor(eTLDplus1: String) throws -> [SecureVaultModels.WebsiteCredentials] func websiteCredentialsFor(accountId: Int64) throws -> SecureVaultModels.WebsiteCredentials? @discardableResult func storeWebsiteCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) throws -> Int64 @@ -309,6 +311,34 @@ public class DefaultAutofillSecureVault: AutofillSe // MARK: - Credentials + public func websiteCredentialsFor(domain: String) throws -> [SecureVaultModels.WebsiteCredentials] { + lock.lock() + defer { + lock.unlock() + } + + do { + let credentials = try self.providers.database.websiteCredentialsForDomain(domain) + return try credentials.map(decryptCredentials(_:)) + } catch { + let error = error as? SecureStorageError ?? SecureStorageError.databaseError(cause: error) + throw error + } + } + + public func websiteCredentialsWithPartialMatchesFor(eTLDplus1: String) throws -> [SecureVaultModels.WebsiteCredentials] { + lock.lock() + defer { + lock.unlock() + } + do { + let credentials = try self.providers.database.websiteCredentialsForTopLevelDomain(eTLDplus1) + return try credentials.map(decryptCredentials(_:)) + } catch { + throw SecureStorageError.databaseError(cause: error) + } + } + public func websiteCredentialsFor(accountId: Int64) throws -> SecureVaultModels.WebsiteCredentials? { lock.lock() defer { @@ -318,14 +348,8 @@ public class DefaultAutofillSecureVault: AutofillSe do { var decryptedCredentials: SecureVaultModels.WebsiteCredentials? if let credentials = try self.providers.database.websiteCredentialsForAccountId(accountId) { - if let password = credentials.password { - decryptedCredentials = .init(account: credentials.account, - password: try self.l2Decrypt(data: password)) - } else { - decryptedCredentials = credentials - } + decryptedCredentials = try decryptCredentials(credentials) } - return decryptedCredentials } catch { let error = error as? SecureStorageError ?? SecureStorageError.databaseError(cause: error) @@ -632,6 +656,16 @@ public class DefaultAutofillSecureVault: AutofillSe // MARK: - Private + private func decryptCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) throws -> SecureVaultModels.WebsiteCredentials { + if let password = credentials.password { + let decryptedPassword = try self.l2Decrypt(data: password) + return .init(account: credentials.account, + password: decryptedPassword) + } else { + return credentials + } + } + private func executeThrowingDatabaseOperation(_ operation: () throws -> DatabaseResult) throws -> DatabaseResult { lock.lock() defer { diff --git a/Sources/BrowserServicesKit/SecureVault/SecureVaultManager.swift b/Sources/BrowserServicesKit/SecureVault/SecureVaultManager.swift index 3a51925f3..95953f2ea 100644 --- a/Sources/BrowserServicesKit/SecureVault/SecureVaultManager.swift +++ b/Sources/BrowserServicesKit/SecureVault/SecureVaultManager.swift @@ -144,7 +144,7 @@ extension SecureVaultManager: AutofillSecureVaultDelegate { public func autofillUserScript(_: AutofillUserScript, didRequestAutoFillInitDataForDomain domain: String, - completionHandler: @escaping ([SecureVaultModels.WebsiteAccount], + completionHandler: @escaping ([SecureVaultModels.WebsiteCredentials], [SecureVaultModels.Identity], [SecureVaultModels.CreditCard], SecureVaultModels.CredentialsProvider) -> Void) { @@ -167,16 +167,13 @@ extension SecureVaultManager: AutofillSecureVaultDelegate { } if delegate.secureVaultManagerIsEnabledStatus(self, forType: .password) { - getAccounts(for: domain, - from: vault, - or: passwordManager, - withPartialMatches: includePartialAccountMatches) { [weak self] accounts, error in + getCredentials(forDomain: domain, from: vault, or: passwordManager, withPartialMatches: includePartialAccountMatches) { [weak self] credentials, error in guard let self = self else { return } if let error = error { os_log(.error, "Error requesting autofill init data: %{public}@", error.localizedDescription) completionHandler([], [], [], self.credentialsProvider) } else { - completionHandler(accounts, identities, cards, self.credentialsProvider) + completionHandler(credentials, identities, cards, self.credentialsProvider) } } } else { @@ -746,23 +743,12 @@ extension SecureVaultManager: AutofillSecureVaultDelegate { } else { do { if withPartialMatches { - guard var currentUrlComponents = AutofillDomainNameUrlMatcher().normalizeSchemeForAutofill(domain) else { + guard let eTLDplus1 = eTLDplus1(for: domain) else { completion([], nil) return } - - if currentUrlComponents.host == .localhost { - let accounts = try vault.accountsWithPartialMatchesFor(eTLDplus1: domain) - completion(accounts, nil) - } else { - guard let tld = tld, let eTLDplus1 = currentUrlComponents.eTLDplus1WithPort(tld: tld) else { - completion([], nil) - return - } - - let accounts = try vault.accountsWithPartialMatchesFor(eTLDplus1: eTLDplus1) - completion(accounts, nil) - } + let accounts = try vault.accountsWithPartialMatchesFor(eTLDplus1: eTLDplus1) + completion(accounts, nil) } else { let accounts = try vault.accountsFor(domain: domain) completion(accounts, nil) @@ -790,6 +776,49 @@ extension SecureVaultManager: AutofillSecureVaultDelegate { } } + private func getCredentials(forDomain domain: String, + from vault: any AutofillSecureVault, + or passwordManager: PasswordManager?, + withPartialMatches: Bool, + completion: @escaping ([SecureVaultModels.WebsiteCredentials], Error?) -> Void) { + if let passwordManager = passwordManager, + passwordManager.isEnabled { + passwordManager.websiteCredentialsFor(domain: domain, completion: completion) + } else { + do { + if withPartialMatches { + guard let eTLDplus1 = eTLDplus1(for: domain) else { + completion([], nil) + return + } + let accounts = try vault.websiteCredentialsWithPartialMatchesFor(eTLDplus1: eTLDplus1) + completion(accounts, nil) + } else { + let credentials = try vault.websiteCredentialsFor(domain: domain) + completion(credentials, nil) + } + } catch { + completion([], error) + } + } + } + + private func eTLDplus1(for domain: String) -> String? { + guard var currentUrlComponents = AutofillDomainNameUrlMatcher().normalizeSchemeForAutofill(domain) else { + return nil + } + + if currentUrlComponents.host == .localhost { + return domain + } else { + guard let tld = tld, let eTLDplus1 = currentUrlComponents.eTLDplus1WithPort(tld: tld) else { + return nil + } + + return eTLDplus1 + } + } + private func existingCredentialsInPasswordManager(with autofillData: AutofillUserScript.DetectedAutofillData, domain: String, vault: any AutofillSecureVault) -> SecureVaultModels.WebsiteCredentials? { diff --git a/Tests/BrowserServicesKitTests/Autofill/AutofillVaultUserScriptTests.swift b/Tests/BrowserServicesKitTests/Autofill/AutofillVaultUserScriptTests.swift index fb76212b6..09c5c0d6f 100644 --- a/Tests/BrowserServicesKitTests/Autofill/AutofillVaultUserScriptTests.swift +++ b/Tests/BrowserServicesKitTests/Autofill/AutofillVaultUserScriptTests.swift @@ -562,7 +562,6 @@ class AutofillVaultUserScriptTests: XCTestCase { } class MockSecureVaultDelegate: AutofillSecureVaultDelegate { - enum CallbackType { case didRequestCreditCardsManagerForDomain case didRequestIdentitiesManagerForDomain @@ -623,7 +622,8 @@ class MockSecureVaultDelegate: AutofillSecureVaultDelegate { func autofillUserScript(_: BrowserServicesKit.AutofillUserScript, didRequestAutoFillInitDataForDomain domain: String, - completionHandler: @escaping ([BrowserServicesKit.SecureVaultModels.WebsiteAccount], [BrowserServicesKit.SecureVaultModels.Identity], [BrowserServicesKit.SecureVaultModels.CreditCard], BrowserServicesKit.SecureVaultModels.CredentialsProvider) -> Void) { + completionHandler: @escaping ([BrowserServicesKit.SecureVaultModels.WebsiteCredentials], [BrowserServicesKit.SecureVaultModels.Identity], [BrowserServicesKit.SecureVaultModels.CreditCard], BrowserServicesKit.SecureVaultModels.CredentialsProvider) -> Void) { + } func autofillUserScript(_: AutofillUserScript, diff --git a/Tests/BrowserServicesKitTests/SecureVault/AutofillUserScriptTests.swift b/Tests/BrowserServicesKitTests/SecureVault/AutofillUserScriptTests.swift index 1e326e878..a29c6a6ae 100644 --- a/Tests/BrowserServicesKitTests/SecureVault/AutofillUserScriptTests.swift +++ b/Tests/BrowserServicesKitTests/SecureVault/AutofillUserScriptTests.swift @@ -61,4 +61,49 @@ class AutofillUserScriptTests: XCTestCase { XCTAssertEqual(responseFromAccounts.success.credentials.username, true) } + func testWhenPasswordsAreNil_ThenAvailableInputTypesPasswordIsFalse() { + let credentialsList = createListOfCredentials(withPassword: nil) + let credentialsProvider = SecureVaultModels.CredentialsProvider(name: SecureVaultModels.CredentialsProvider.Name.duckduckgo, locked: false) + let responseFromCredentials = AutofillUserScript.RequestAvailableInputTypesResponse(credentials: credentialsList, + identities: [], + cards: [], + email: false, + credentialsProvider: credentialsProvider) + XCTAssertEqual(responseFromCredentials.success.credentials.password, false) + } + + func testWhenAllPasswordsAreEmpty_ThenAvailableInputTypesPasswordIsFalse() { + let credentialsList = createListOfCredentials(withPassword: "".data(using: .utf8)!) + let credentialsProvider = SecureVaultModels.CredentialsProvider(name: SecureVaultModels.CredentialsProvider.Name.duckduckgo, locked: false) + let responseFromCredentials = AutofillUserScript.RequestAvailableInputTypesResponse(credentials: credentialsList, + identities: [], + cards: [], + email: false, + credentialsProvider: credentialsProvider) + XCTAssertEqual(responseFromCredentials.success.credentials.password, false) + } + + func testWhenAtLeastOnePasswordIsNonNilOrEmpty_ThenAvailableInputTypesPasswordIsTrue() { + var credentialsList = createListOfCredentials(withPassword: nil) + let account = credentialsList.first?.account + let credentialsWithPassword = SecureVaultModels.WebsiteCredentials(account: account!, password: "password".data(using: .utf8)!) + credentialsList.append(credentialsWithPassword) + let credentialsProvider = SecureVaultModels.CredentialsProvider(name: SecureVaultModels.CredentialsProvider.Name.duckduckgo, locked: false) + let responseFromCredentials = AutofillUserScript.RequestAvailableInputTypesResponse(credentials: credentialsList, + identities: [], + cards: [], + email: false, + credentialsProvider: credentialsProvider) + XCTAssertEqual(responseFromCredentials.success.credentials.password, true) + } + + private func createListOfCredentials(withPassword password: Data?) -> [SecureVaultModels.WebsiteCredentials] { + var credentialsList = [SecureVaultModels.WebsiteCredentials]() + for i in 0...10 { + let account = SecureVaultModels.WebsiteAccount(id: "id\(i)", username: "username\(i)", domain: "domain.com", created: Date(), lastUpdated: Date()) + let credentials = SecureVaultModels.WebsiteCredentials(account: account, password: password) + credentialsList.append(credentials) + } + return credentialsList + } } diff --git a/Tests/BrowserServicesKitTests/SecureVault/MockAutofillDatabaseProvider.swift b/Tests/BrowserServicesKitTests/SecureVault/MockAutofillDatabaseProvider.swift index e45bb6a4f..d116f3f94 100644 --- a/Tests/BrowserServicesKitTests/SecureVault/MockAutofillDatabaseProvider.swift +++ b/Tests/BrowserServicesKitTests/SecureVault/MockAutofillDatabaseProvider.swift @@ -35,6 +35,7 @@ internal class MockAutofillDatabaseProvider: AutofillDatabaseProvider { var _creditCards = [Int64: SecureVaultModels.CreditCard]() var _forDomain = [String]() var _credentialsDict = [Int64: SecureVaultModels.WebsiteCredentials]() + var _credentialsForDomainDict = [String: [SecureVaultModels.WebsiteCredentials]]() var _note: SecureVaultModels.Note? var db: DatabaseWriter @@ -66,6 +67,14 @@ internal class MockAutofillDatabaseProvider: AutofillDatabaseProvider { return _credentialsDict[accountId] } + func websiteCredentialsForDomain(_ domain: String) throws -> [BrowserServicesKit.SecureVaultModels.WebsiteCredentials] { + return _credentialsForDomainDict[domain] ?? [] + } + + func websiteCredentialsForTopLevelDomain(_ eTLDplus1: String) throws -> [BrowserServicesKit.SecureVaultModels.WebsiteCredentials] { + return _credentialsForDomainDict[eTLDplus1] ?? [] + } + func websiteAccountsForDomain(_ domain: String) throws -> [SecureVaultModels.WebsiteAccount] { self._forDomain.append(domain) return _accounts diff --git a/Tests/BrowserServicesKitTests/SecureVault/SecureVaultManagerTests.swift b/Tests/BrowserServicesKitTests/SecureVault/SecureVaultManagerTests.swift index 6273a75f6..9558d670d 100644 --- a/Tests/BrowserServicesKitTests/SecureVault/SecureVaultManagerTests.swift +++ b/Tests/BrowserServicesKitTests/SecureVault/SecureVaultManagerTests.swift @@ -286,9 +286,17 @@ class SecureVaultManagerTests: XCTestCase { waitForExpectations(timeout: 0.1) } - func testWhenRequestingAutofillInitDataWithDomainAndPort_ThenDataIsReturned() throws { + func testWhenRequestingAutofillInitDataWithDomainAndPort_withPartialMatches_ThenDataIsReturned() throws { + self.manager = SecureVaultManager(vault: self.testVault, includePartialAccountMatches: true, tld: TLD()) + try assertWhenRequestingAutofillInitDataWithDomainAndPort_ThenDataIsReturned() + } - // Given + func testWhenRequestingAutofillInitDataWithDomainAndPort_withoutPartialMatches_ThenDataIsReturned() throws { + self.manager = SecureVaultManager(vault: self.testVault, includePartialAccountMatches: false, tld: TLD()) + try assertWhenRequestingAutofillInitDataWithDomainAndPort_ThenDataIsReturned() + } + + func assertWhenRequestingAutofillInitDataWithDomainAndPort_ThenDataIsReturned(file: StaticString = #file, line: UInt = #line) throws { class SecureVaultDelegate: MockSecureVaultManagerDelegate { override func secureVaultManager(_ manager: SecureVaultManager, promptUserToAutofillCredentialsForDomain domain: String, @@ -301,26 +309,24 @@ class SecureVaultManagerTests: XCTestCase { } } - self.manager = SecureVaultManager(vault: self.testVault, includePartialAccountMatches: true, tld: TLD()) self.secureVaultManagerDelegate = SecureVaultDelegate() self.manager.delegate = self.secureVaultManagerDelegate let domain = "domain.com:1234" let username = "dax" let account = SecureVaultModels.WebsiteAccount(id: "1", title: nil, username: username, domain: domain, created: Date(), lastUpdated: Date()) - self.mockDatabaseProvider._accounts = [account] - - let credentials = SecureVaultModels.WebsiteCredentials(account: account, password: "password".data(using: .utf8)!) - try self.testVault.storeWebsiteCredentials(credentials) + let storedCredentials = SecureVaultModels.WebsiteCredentials(account: account, password: "password".data(using: .utf8)!) + self.mockDatabaseProvider._credentialsForDomainDict[domain] = [storedCredentials] let expect = expectation(description: #function) // When - manager.autofillUserScript(mockAutofillUserScript, didRequestAutoFillInitDataForDomain: domain) { accounts, _, _, _ in + manager.autofillUserScript(mockAutofillUserScript, didRequestAutoFillInitDataForDomain: domain) { credentials, _, _, _ in // Then - XCTAssertTrue(accounts.count == 1) - XCTAssertEqual(accounts.first!, account) + XCTAssertEqual(credentials.count, 1, file: file, line: line) + XCTAssertEqual(credentials.first?.account.id, storedCredentials.account.id, file: file, line: line) + XCTAssertEqual(credentials.first?.password, storedCredentials.password, file: file, line: line) expect.fulfill() } waitForExpectations(timeout: 0.1)