blob: 243dd38ea74ae6fabbc573097228dbce80e3b196 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/password_manager/password_store_x.h"
#include <stddef.h>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_store_origin_unittest.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::PasswordForm;
using password_manager::metrics_util::LinuxBackendMigrationStatus;
using password_manager::PasswordStoreChange;
using password_manager::PasswordStoreChangeList;
using password_manager::UnorderedPasswordFormElementsAre;
using testing::ElementsAreArray;
using testing::IsEmpty;
using testing::Pointee;
using testing::UnorderedElementsAre;
namespace {
class MockPasswordStoreConsumer
: public password_manager::PasswordStoreConsumer {
public:
MOCK_METHOD1(OnGetPasswordStoreResultsConstRef,
void(const std::vector<std::unique_ptr<PasswordForm>>&));
// GMock cannot mock methods with move-only args.
void OnGetPasswordStoreResults(
std::vector<std::unique_ptr<PasswordForm>> results) override {
OnGetPasswordStoreResultsConstRef(results);
}
};
class FailingBackend : public PasswordStoreX::NativeBackend {
public:
bool Init() override { return true; }
PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
return PasswordStoreChangeList();
}
bool UpdateLogin(const PasswordForm& form,
PasswordStoreChangeList* changes) override {
return false;
}
bool RemoveLogin(const PasswordForm& form,
PasswordStoreChangeList* changes) override {
return false;
}
bool RemoveLoginsCreatedBetween(
base::Time delete_begin,
base::Time delete_end,
password_manager::PasswordStoreChangeList* changes) override {
return false;
}
bool RemoveLoginsSyncedBetween(
base::Time delete_begin,
base::Time delete_end,
password_manager::PasswordStoreChangeList* changes) override {
return false;
}
bool DisableAutoSignInForOrigins(
const base::Callback<bool(const GURL&)>& origin_filter,
password_manager::PasswordStoreChangeList* changes) override {
return false;
}
// Use this as a landmine to check whether results of failed Get*Logins calls
// get ignored.
static std::vector<std::unique_ptr<PasswordForm>> CreateTrashForms() {
std::vector<std::unique_ptr<PasswordForm>> forms;
PasswordForm trash;
trash.username_element = base::ASCIIToUTF16("trash u. element");
trash.username_value = base::ASCIIToUTF16("trash u. value");
trash.password_element = base::ASCIIToUTF16("trash p. element");
trash.password_value = base::ASCIIToUTF16("trash p. value");
for (size_t i = 0; i < 3; ++i) {
trash.origin = GURL(base::StringPrintf("http://trash%zu.com", i));
forms.push_back(std::make_unique<PasswordForm>(trash));
}
return forms;
}
bool GetLogins(const PasswordStore::FormDigest& form,
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
*forms = CreateTrashForms();
return false;
}
bool GetAutofillableLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
*forms = CreateTrashForms();
return false;
}
bool GetBlacklistLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
*forms = CreateTrashForms();
return false;
}
bool GetAllLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
*forms = CreateTrashForms();
return false;
}
scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override {
return nullptr;
}
};
class MockBackend : public PasswordStoreX::NativeBackend {
public:
~MockBackend() override {
if (save_on_destruct_) {
*save_on_destruct_ = std::move(all_forms_);
}
}
bool Init() override { return true; }
PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
all_forms_.push_back(form);
PasswordStoreChange change(PasswordStoreChange::ADD, form);
return PasswordStoreChangeList(1, change);
}
bool UpdateLogin(const PasswordForm& form,
PasswordStoreChangeList* changes) override {
for (size_t i = 0; i < all_forms_.size(); ++i) {
if (ArePasswordFormUniqueKeyEqual(all_forms_[i], form)) {
all_forms_[i] = form;
changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
form));
}
}
return true;
}
bool RemoveLogin(const PasswordForm& form,
PasswordStoreChangeList* changes) override {
for (size_t i = 0; i < all_forms_.size(); ++i) {
if (ArePasswordFormUniqueKeyEqual(all_forms_[i], form)) {
changes->push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
form));
erase(i--);
}
}
return true;
}
bool RemoveLoginsCreatedBetween(
base::Time delete_begin,
base::Time delete_end,
password_manager::PasswordStoreChangeList* changes) override {
for (size_t i = 0; i < all_forms_.size(); ++i) {
if (delete_begin <= all_forms_[i].date_created &&
(delete_end.is_null() || all_forms_[i].date_created < delete_end))
erase(i--);
}
return true;
}
bool RemoveLoginsSyncedBetween(
base::Time delete_begin,
base::Time delete_end,
password_manager::PasswordStoreChangeList* changes) override {
DCHECK(changes);
for (size_t i = 0; i < all_forms_.size(); ++i) {
if (delete_begin <= all_forms_[i].date_synced &&
(delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
changes->push_back(password_manager::PasswordStoreChange(
password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
erase(i--);
}
}
return true;
}
bool DisableAutoSignInForOrigins(
const base::Callback<bool(const GURL&)>& origin_filter,
password_manager::PasswordStoreChangeList* changes) override {
return true;
}
bool GetLogins(const PasswordStore::FormDigest& form,
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
for (size_t i = 0; i < all_forms_.size(); ++i)
if (all_forms_[i].signon_realm == form.signon_realm)
forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
return true;
}
bool GetAutofillableLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
for (size_t i = 0; i < all_forms_.size(); ++i)
if (!all_forms_[i].blacklisted_by_user)
forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
return true;
}
bool GetBlacklistLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
for (size_t i = 0; i < all_forms_.size(); ++i)
if (all_forms_[i].blacklisted_by_user)
forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
return true;
}
bool GetAllLogins(
std::vector<std::unique_ptr<PasswordForm>>* forms) override {
for (size_t i = 0; i < all_forms_.size(); ++i)
forms->push_back(std::make_unique<PasswordForm>(all_forms_[i]));
return true;
}
scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() override {
return nullptr;
}
void SaveFormsOnDestruct(std::vector<PasswordForm>* forms) {
save_on_destruct_ = forms;
}
private:
void erase(size_t index) {
if (index < all_forms_.size() - 1)
all_forms_[index] = all_forms_.back();
all_forms_.pop_back();
}
std::vector<PasswordForm>* save_on_destruct_ = nullptr;
std::vector<PasswordForm> all_forms_;
};
class MockLoginDatabaseReturn {
public:
MOCK_METHOD1(OnLoginDatabaseQueryDone,
void(const std::vector<std::unique_ptr<PasswordForm>>&));
};
void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
bool autofillable,
MockLoginDatabaseReturn* mock_return) {
std::vector<std::unique_ptr<PasswordForm>> forms;
if (autofillable)
EXPECT_TRUE(login_db->GetAutofillableLogins(&forms));
else
EXPECT_TRUE(login_db->GetBlacklistLogins(&forms));
mock_return->OnLoginDatabaseQueryDone(forms);
}
// Generate |count| expected logins, either auto-fillable or blacklisted.
void InitExpectedForms(bool autofillable,
size_t count,
std::vector<std::unique_ptr<PasswordForm>>* forms) {
const char* domain = autofillable ? "example" : "blacklisted";
for (size_t i = 0; i < count; ++i) {
std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
i, domain);
std::string action = base::StringPrintf("http://%zu.%s.com/action",
i, domain);
password_manager::PasswordFormData data = {
PasswordForm::SCHEME_HTML,
realm.c_str(),
origin.c_str(),
action.c_str(),
L"submit_element",
L"username_element",
L"password_element",
autofillable ? L"username_value" : nullptr,
autofillable ? L"password_value" : nullptr,
autofillable,
static_cast<double>(i + 1)};
forms->push_back(FillPasswordFormWithData(data));
}
}
PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
return PasswordStoreChangeList(
1, PasswordStoreChange(PasswordStoreChange::ADD, form));
}
enum BackendType {
NO_BACKEND,
FAILING_BACKEND,
WORKING_BACKEND
};
std::unique_ptr<PasswordStoreX::NativeBackend> GetBackend(
BackendType backend_type) {
switch (backend_type) {
case FAILING_BACKEND:
return std::make_unique<FailingBackend>();
case WORKING_BACKEND:
return std::make_unique<MockBackend>();
default:
return std::unique_ptr<PasswordStoreX::NativeBackend>();
}
}
class PasswordStoreXTestDelegate {
public:
PasswordStoreX* store() { return store_.get(); }
void FinishAsyncProcessing();
protected:
explicit PasswordStoreXTestDelegate(BackendType backend_type);
~PasswordStoreXTestDelegate();
private:
void SetupTempDir();
base::FilePath test_login_db_file_path() const;
base::FilePath test_encrypted_login_db_file_path() const;
base::test::ScopedTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
BackendType backend_type_;
scoped_refptr<PasswordStoreX> store_;
TestingPrefServiceSimple fake_pref_service;
DISALLOW_COPY_AND_ASSIGN(PasswordStoreXTestDelegate);
};
PasswordStoreXTestDelegate::PasswordStoreXTestDelegate(BackendType backend_type)
: backend_type_(backend_type) {
SetupTempDir();
auto login_db = std::make_unique<password_manager::LoginDatabase>(
test_login_db_file_path());
fake_pref_service.registry()->RegisterIntegerPref(
password_manager::prefs::kMigrationToLoginDBStep,
PasswordStoreX::NOT_ATTEMPTED);
store_ = new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
test_encrypted_login_db_file_path(),
GetBackend(backend_type_), &fake_pref_service);
store_->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
}
PasswordStoreXTestDelegate::~PasswordStoreXTestDelegate() {
store_->ShutdownOnUIThread();
}
void PasswordStoreXTestDelegate::FinishAsyncProcessing() {
task_environment_.RunUntilIdle();
}
void PasswordStoreXTestDelegate::SetupTempDir() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
base::FilePath PasswordStoreXTestDelegate::test_login_db_file_path() const {
return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("login_test"));
}
base::FilePath PasswordStoreXTestDelegate::test_encrypted_login_db_file_path()
const {
return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("encrypted_login_test"));
}
class PasswordStoreXNoBackendTestDelegate : public PasswordStoreXTestDelegate {
public:
PasswordStoreXNoBackendTestDelegate()
: PasswordStoreXTestDelegate(NO_BACKEND) {}
};
class PasswordStoreXWorkingBackendTestDelegate
: public PasswordStoreXTestDelegate {
public:
PasswordStoreXWorkingBackendTestDelegate()
: PasswordStoreXTestDelegate(WORKING_BACKEND) {}
};
std::vector<std::unique_ptr<PasswordForm>> ReadLoginDB(
const base::FilePath& path,
bool encrypted) {
password_manager::LoginDatabase login_db(path);
if (!encrypted)
login_db.disable_encryption();
EXPECT_TRUE(login_db.Init());
std::vector<std::unique_ptr<PasswordForm>> stored_forms;
EXPECT_TRUE(login_db.GetAutofillableLogins(&stored_forms));
return stored_forms;
}
} // namespace
namespace password_manager {
INSTANTIATE_TYPED_TEST_CASE_P(XNoBackend,
PasswordStoreOriginTest,
PasswordStoreXNoBackendTestDelegate);
INSTANTIATE_TYPED_TEST_CASE_P(XWorkingBackend,
PasswordStoreOriginTest,
PasswordStoreXWorkingBackendTestDelegate);
}
class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
protected:
PasswordStoreXTest() = default;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
fake_pref_service_.registry()->RegisterIntegerPref(
password_manager::prefs::kMigrationToLoginDBStep,
PasswordStoreX::NOT_ATTEMPTED);
OSCryptMocker::SetUp();
}
void TearDown() override { OSCryptMocker::TearDown(); }
base::FilePath test_login_db_file_path() const {
return temp_dir_.GetPath().Append(FILE_PATH_LITERAL("login_test"));
}
base::FilePath test_encrypted_login_db_file_path() const {
return temp_dir_.GetPath().Append(
FILE_PATH_LITERAL("encrypted_login_test"));
}
void WaitForPasswordStore() { task_environment_.RunUntilIdle(); }
protected:
TestingPrefServiceSimple fake_pref_service_;
base::HistogramTester histogram_tester_;
private:
base::test::ScopedTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
DISALLOW_COPY_AND_ASSIGN(PasswordStoreXTest);
};
TEST_P(PasswordStoreXTest, Notifications) {
std::unique_ptr<password_manager::LoginDatabase> login_db(
new password_manager::LoginDatabase(test_login_db_file_path()));
scoped_refptr<PasswordStoreX> store(
new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
test_encrypted_login_db_file_path(),
GetBackend(GetParam()), &fake_pref_service_));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
password_manager::PasswordFormData form_data = {
PasswordForm::SCHEME_HTML,
"http://bar.example.com",
"http://bar.example.com/origin",
"http://bar.example.com/action",
L"submit_element",
L"username_element",
L"password_element",
L"username_value",
L"password_value",
true,
1};
std::unique_ptr<PasswordForm> form = FillPasswordFormWithData(form_data);
password_manager::MockPasswordStoreObserver observer;
store->AddObserver(&observer);
const PasswordStoreChange expected_add_changes[] = {
PasswordStoreChange(PasswordStoreChange::ADD, *form),
};
EXPECT_CALL(
observer,
OnLoginsChanged(ElementsAreArray(expected_add_changes)));
// Adding a login should trigger a notification.
store->AddLogin(*form);
WaitForPasswordStore();
// Change the password.
form->password_value = base::ASCIIToUTF16("a different password");
const PasswordStoreChange expected_update_changes[] = {
PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
};
EXPECT_CALL(
observer,
OnLoginsChanged(ElementsAreArray(expected_update_changes)));
// Updating the login with the new password should trigger a notification.
store->UpdateLogin(*form);
WaitForPasswordStore();
const PasswordStoreChange expected_delete_changes[] = {
PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
};
EXPECT_CALL(
observer,
OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
// Deleting the login should trigger a notification.
store->RemoveLogin(*form);
WaitForPasswordStore();
store->RemoveObserver(&observer);
store->ShutdownOnUIThread();
}
TEST_P(PasswordStoreXTest, NativeMigration) {
std::vector<std::unique_ptr<PasswordForm>> expected_autofillable;
InitExpectedForms(true, 5, &expected_autofillable);
std::vector<std::unique_ptr<PasswordForm>> expected_blacklisted;
InitExpectedForms(false, 5, &expected_blacklisted);
const base::FilePath login_db_file = test_login_db_file_path();
std::unique_ptr<password_manager::LoginDatabase> login_db(
new password_manager::LoginDatabase(login_db_file));
login_db->disable_encryption();
ASSERT_TRUE(login_db->Init());
// Get the initial size of the login DB file, before we populate it.
// This will be used later to make sure it gets back to this size.
base::File::Info db_file_start_info;
ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
// Populate the login DB with logins that should be migrated.
for (const auto& form : expected_autofillable) {
EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
}
for (const auto& form : expected_blacklisted) {
EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
}
// Get the new size of the login DB file. We expect it to be larger.
base::File::Info db_file_full_info;
ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
// Initializing the PasswordStore shouldn't trigger a native migration (yet).
login_db.reset(new password_manager::LoginDatabase(login_db_file));
scoped_refptr<PasswordStoreX> store(
new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
test_encrypted_login_db_file_path(),
GetBackend(GetParam()), &fake_pref_service_));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
MockPasswordStoreConsumer consumer;
// The autofillable forms should have been migrated to the native backend.
EXPECT_CALL(consumer,
OnGetPasswordStoreResultsConstRef(
UnorderedPasswordFormElementsAre(&expected_autofillable)));
store->GetAutofillableLogins(&consumer);
WaitForPasswordStore();
// The blacklisted forms should have been migrated to the native backend.
EXPECT_CALL(consumer,
OnGetPasswordStoreResultsConstRef(
UnorderedPasswordFormElementsAre(&expected_blacklisted)));
store->GetBlacklistLogins(&consumer);
WaitForPasswordStore();
MockLoginDatabaseReturn ld_return;
if (GetParam() == WORKING_BACKEND) {
// No autofillable logins should be left in the login DB.
EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
} else {
// The autofillable logins should still be in the login DB.
EXPECT_CALL(ld_return,
OnLoginDatabaseQueryDone(
UnorderedPasswordFormElementsAre(&expected_autofillable)));
}
LoginDatabaseQueryCallback(store->login_db(), true, &ld_return);
WaitForPasswordStore();
if (GetParam() == WORKING_BACKEND) {
// Likewise, no blacklisted logins should be left in the login DB.
EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
} else {
// The blacklisted logins should still be in the login DB.
EXPECT_CALL(ld_return,
OnLoginDatabaseQueryDone(
UnorderedPasswordFormElementsAre(&expected_blacklisted)));
}
LoginDatabaseQueryCallback(store->login_db(), false, &ld_return);
WaitForPasswordStore();
if (GetParam() == WORKING_BACKEND) {
// If the migration succeeded, then not only should there be no logins left
// in the login DB, but also the file should have been deleted and then
// recreated. We approximate checking for this by checking that the file
// size is equal to the size before we populated it, even though it was
// larger after populating it.
base::File::Info db_file_end_info;
ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
}
store->ShutdownOnUIThread();
}
TEST_P(PasswordStoreXTest, MigrationToEncryption) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
password_manager::features::kMigrateLinuxToLoginDB);
IntegerPrefMember migration_step_pref_;
migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
&fake_pref_service_);
EXPECT_EQ(PasswordStoreX::NOT_ATTEMPTED, migration_step_pref_.GetValue());
// Add existing credentials into the backend.
std::vector<std::unique_ptr<PasswordForm>> old_credentials;
InitExpectedForms(true, 6, &old_credentials);
std::unique_ptr<PasswordStoreX::NativeBackend> backend =
GetBackend(GetParam());
std::vector<PasswordForm> native_backend_last_state;
if (GetParam() == WORKING_BACKEND) {
static_cast<MockBackend*>(backend.get())
->SaveFormsOnDestruct(&native_backend_last_state);
}
if (GetParam() != NO_BACKEND) {
for (int i = 0; i < 3; i++)
backend->AddLogin(*old_credentials[i]);
}
// Add existing credentials into the unencrypted loginDB.
auto login_db = std::make_unique<password_manager::LoginDatabase>(
test_login_db_file_path());
login_db->disable_encryption();
ASSERT_TRUE(login_db->Init());
for (int i = 3; i < 6; i++)
ignore_result(login_db->AddLogin(*old_credentials[i]));
login_db = std::make_unique<password_manager::LoginDatabase>(
test_login_db_file_path());
scoped_refptr<PasswordStoreX> store(
new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
test_encrypted_login_db_file_path(),
std::move(backend), &fake_pref_service_));
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
// Make modifications, so that we trigger the migration and so that we can
// verify where the store serves from. The migration is triggered
// opportunistically during access to the store.
const auto new_form = password_manager::FillPasswordFormWithData(
{PasswordForm::SCHEME_HTML, "https://www.fakebook.com",
"https://www.fakebook.com/li", "https://www.fakebook.com/a",
L"submit_element", L"username_element", L"password_element",
L"username_value", L"password_value", true, 1.0});
store->RemoveLogin(*old_credentials[0]);
store->AddLogin(*new_form);
MockPasswordStoreConsumer consumer;
if (GetParam() == WORKING_BACKEND) {
// The store has the native backend data, the initial unencrypted loginDB
// data and modifications.
EXPECT_CALL(
consumer,
OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
Pointee(*new_form), Pointee(*old_credentials[1]),
Pointee(*old_credentials[2]), Pointee(*old_credentials[3]),
Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
} else {
// The has the initial unencrypted loginDB data and modifications.
EXPECT_CALL(
consumer,
OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
Pointee(*new_form), Pointee(*old_credentials[3]),
Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
}
store->GetAutofillableLogins(&consumer);
WaitForPasswordStore();
store->ShutdownOnUIThread();
store.reset();
WaitForPasswordStore();
// This will report that it was migrated on the next run.
histogram_tester_.ExpectBucketCount(
"PasswordManager.LinuxBackendMigration.Adoption",
LinuxBackendMigrationStatus::kNotAttempted, 1);
if (GetParam() == WORKING_BACKEND) {
// Verify that the login database contains all the values, now encrypted.
std::vector<std::unique_ptr<PasswordForm>> stored_forms =
ReadLoginDB(test_login_db_file_path(), true);
EXPECT_EQ(6u, stored_forms.size());
EXPECT_THAT(
stored_forms,
UnorderedElementsAre(
Pointee(*new_form), Pointee(*old_credentials[1]),
Pointee(*old_credentials[2]), Pointee(*old_credentials[3]),
Pointee(*old_credentials[4]), Pointee(*old_credentials[5])));
EXPECT_TRUE(native_backend_last_state.empty());
stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), false);
EXPECT_TRUE(stored_forms.empty());
EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED,
migration_step_pref_.GetValue());
histogram_tester_.ExpectBucketCount(
"PasswordManager.LinuxBackendMigration.AttemptResult",
LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
} else if (GetParam() == FAILING_BACKEND) {
// No values should be written if we can't read the backend.
auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
EXPECT_TRUE(stored_forms.empty());
EXPECT_THAT(migration_step_pref_.GetValue(), PasswordStoreX::POSTPONED);
histogram_tester_.ExpectBucketCount(
"PasswordManager.LinuxBackendMigration.AttemptResult",
LinuxBackendMigrationStatus::kPostponed, 1);
} else { // NO_BACKEND
// No values should be moved without a working backend.
auto stored_forms = ReadLoginDB(test_encrypted_login_db_file_path(), true);
EXPECT_TRUE(stored_forms.empty());
EXPECT_THAT(migration_step_pref_.GetValue(), PasswordStoreX::NOT_ATTEMPTED);
}
}
// Once the migration is performed, don't port anything else into the new
// location.
TEST_P(PasswordStoreXTest, MigrationToEncryption_OnlyOnce) {
if (GetParam() != WORKING_BACKEND)
return;
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
password_manager::features::kMigrateLinuxToLoginDB);
IntegerPrefMember migration_step_pref_;
migration_step_pref_.Init(password_manager::prefs::kMigrationToLoginDBStep,
&fake_pref_service_);
// Signal that the migration has been completed.
migration_step_pref_.SetValue(PasswordStoreX::LOGIN_DB_REPLACED);
// We add new credentials into a backend. They should be completely ignored by
// the store.
std::vector<std::unique_ptr<PasswordForm>> old_credentials;
InitExpectedForms(true, 6, &old_credentials);
auto backend = GetBackend(GetParam());
for (int i = 0; i < 3; i++)
backend->AddLogin(*old_credentials[i]);
// Add existing credentials into loginDB. They should be the only thing that's
// available in the store.
auto login_db = std::make_unique<password_manager::LoginDatabase>(
test_login_db_file_path());
ASSERT_TRUE(login_db->Init());
for (int i = 3; i < 6; i++)
ignore_result(login_db->AddLogin(*old_credentials[i]));
login_db.reset();
// Create the store.
login_db = std::make_unique<password_manager::LoginDatabase>(
test_login_db_file_path());
scoped_refptr<PasswordStoreX> store =
new PasswordStoreX(std::move(login_db), test_login_db_file_path(),
test_encrypted_login_db_file_path(),
std::move(backend), &fake_pref_service_);
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
// Make modifications and check the contents.
MockPasswordStoreConsumer consumer;
EXPECT_CALL(consumer,
OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
Pointee(*old_credentials[3]), Pointee(*old_credentials[4]),
Pointee(*old_credentials[5]))));
store->GetAutofillableLogins(&consumer);
store->RemoveLogin(*old_credentials[3]);
EXPECT_CALL(consumer,
OnGetPasswordStoreResultsConstRef(UnorderedElementsAre(
Pointee(*old_credentials[4]), Pointee(*old_credentials[5]))));
store->GetAutofillableLogins(&consumer);
WaitForPasswordStore();
store->ShutdownOnUIThread();
store.reset();
WaitForPasswordStore();
// The previous results were served from an encrypted login database.
std::vector<std::unique_ptr<PasswordForm>> stored_forms =
ReadLoginDB(test_login_db_file_path(), true);
EXPECT_EQ(2u, stored_forms.size());
EXPECT_EQ(PasswordStoreX::LOGIN_DB_REPLACED, migration_step_pref_.GetValue());
EXPECT_THAT(stored_forms, UnorderedElementsAre(Pointee(*old_credentials[4]),
Pointee(*old_credentials[5])));
histogram_tester_.ExpectBucketCount(
"PasswordManager.LinuxBackendMigration.Adoption",
LinuxBackendMigrationStatus::kLoginDBReplaced, 1);
histogram_tester_.ExpectTotalCount(
"PasswordManager.LinuxBackendMigration.AttemptResult", 0);
}
INSTANTIATE_TEST_CASE_P(NoBackend,
PasswordStoreXTest,
testing::Values(NO_BACKEND));
INSTANTIATE_TEST_CASE_P(FailingBackend,
PasswordStoreXTest,
testing::Values(FAILING_BACKEND));
INSTANTIATE_TEST_CASE_P(WorkingBackend,
PasswordStoreXTest,
testing::Values(WORKING_BACKEND));