Commit 3843c441 authored by Rafał Godlewski's avatar Rafał Godlewski Committed by Commit Bot

Add API for leaked credentials table in Password Store

Adds functions for creating, removing and getting all records from
leaked credentials table in Login Database.
Adds initialization of leaked credentials table in LoginDatabase.

Bug: 1005746
Change-Id: I5b97e9ed2ed309f6d7babcb905559fa72ab6263a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1816544
Commit-Queue: Rafał Godlewski <rgod@google.com>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: default avatarVadym Doroshenko <dvadym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#699398}
parent 585a0a3d
......@@ -133,6 +133,8 @@ jumbo_static_library("browser") {
"password_generation_frame_helper.h",
"password_generation_state.cc",
"password_generation_state.h",
"password_leak_history_consumer.cc",
"password_leak_history_consumer.h",
"password_list_sorter.cc",
"password_list_sorter.h",
"password_manager.cc",
......
......@@ -59,7 +59,9 @@ bool operator==(const LeakedCredentials& lhs, const LeakedCredentials& rhs) {
lhs.create_time == rhs.create_time;
}
LeakedCredentialsTable::LeakedCredentialsTable(sql::Database* db) : db_(db) {}
void LeakedCredentialsTable::Init(sql::Database* db) {
db_ = db;
}
bool LeakedCredentialsTable::CreateTableIfNecessary() {
if (!db_->DoesTableExist(kLeakedCredentialsTableName)) {
......
......@@ -32,9 +32,12 @@ bool operator==(const LeakedCredentials& lhs, const LeakedCredentials& rhs);
// Represents the 'leaked credentials' table in the Login Database.
class LeakedCredentialsTable {
public:
explicit LeakedCredentialsTable(sql::Database* db);
LeakedCredentialsTable() = default;
~LeakedCredentialsTable() = default;
// Initializes |db_|.
void Init(sql::Database* db);
// Creates the leaked credentials table if it doesn't exist.
bool CreateTableIfNecessary();
......@@ -50,7 +53,7 @@ class LeakedCredentialsTable {
std::vector<LeakedCredentials> GetAllRows();
private:
sql::Database* db_;
sql::Database* db_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(LeakedCredentialsTable);
};
......
......@@ -34,10 +34,11 @@ class LeakedCredentialsTableTest : public testing::Test {
void ReloadDatabase() {
base::FilePath file = temp_dir_.GetPath().AppendASCII("TestDatabase");
db_.reset(new LeakedCredentialsTable);
connection_.reset(new sql::Database);
connection_->set_exclusive_locking();
ASSERT_TRUE(connection_->Open(file));
db_.reset(new LeakedCredentialsTable(connection_.get()));
db_->Init(connection_.get());
ASSERT_TRUE(db_->CreateTableIfNecessary());
}
......
......@@ -142,6 +142,7 @@ enum DatabaseInitError {
INIT_STATS_ERROR,
MIGRATION_ERROR,
COMMIT_TRANSACTION_ERROR,
INIT_LEAKED_CREDENTIALS_ERROR,
DATABASE_INIT_ERROR_COUNT,
};
......@@ -734,6 +735,7 @@ bool LoginDatabase::Init() {
}
stats_table_.Init(&db_);
leaked_credentials_table_.Init(&db_);
int current_version = meta_table_.GetVersionNumber();
bool migration_success = FixVersionIfNeeded(&db_, &current_version);
......@@ -772,6 +774,14 @@ bool LoginDatabase::Init() {
return false;
}
if (!leaked_credentials_table_.CreateTableIfNecessary()) {
LogDatabaseInitError(INIT_LEAKED_CREDENTIALS_ERROR);
LOG(ERROR) << "Unable to create the leaked credentials table.";
transaction.Rollback();
db_.Close();
return false;
}
if (!transaction.Commit()) {
LogDatabaseInitError(COMMIT_TRANSACTION_ERROR);
LOG(ERROR) << "Unable to commit a transaction.";
......
......@@ -16,6 +16,7 @@
#include "base/strings/string16.h"
#include "base/util/type_safety/strong_alias.h"
#include "build/build_config.h"
#include "components/password_manager/core/browser/leaked_credentials_table.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/password_store_change.h"
#include "components/password_manager/core/browser/password_store_sync.h"
......@@ -204,6 +205,9 @@ class LoginDatabase : public PasswordStoreSync::MetadataStore {
bool CommitTransaction();
StatisticsTable& stats_table() { return stats_table_; }
LeakedCredentialsTable& leaked_credentials_table() {
return leaked_credentials_table_;
}
#if defined(OS_POSIX) && !defined(OS_MACOSX)
void enable_encryption() { use_encryption_ = true; }
......@@ -318,6 +322,7 @@ class LoginDatabase : public PasswordStoreSync::MetadataStore {
mutable sql::Database db_;
sql::MetaTable meta_table_;
StatisticsTable stats_table_;
LeakedCredentialsTable leaked_credentials_table_;
// These cached strings are used to build SQL statements.
std::string add_statement_;
......
......@@ -10,6 +10,7 @@
#include <vector>
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/leaked_credentials_table.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -71,6 +72,10 @@ class MockPasswordStore : public PasswordStore {
std::vector<InteractionsStats>(const GURL& origin_domain));
MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
MOCK_METHOD1(AddLeakedCredentialsImpl, void(const LeakedCredentials&));
MOCK_METHOD2(RemoveLeakedCredentialsImpl,
void(const GURL&, const base::string16&));
MOCK_METHOD0(GetAllLeakedCredentialsImpl, std::vector<LeakedCredentials>());
MOCK_CONST_METHOD0(IsAbleToSavePasswords, bool());
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
......
// Copyright 2019 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 "components/password_manager/core/browser/password_leak_history_consumer.h"
#include "components/password_manager/core/browser/leaked_credentials_table.h"
namespace password_manager {
PasswordLeakHistoryConsumer::PasswordLeakHistoryConsumer() = default;
PasswordLeakHistoryConsumer::~PasswordLeakHistoryConsumer() = default;
} // namespace password_manager
// Copyright 2019 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
#include <vector>
#include "base/task/cancelable_task_tracker.h"
namespace password_manager {
struct LeakedCredentials;
// Reads information associated with leaked credentials history from the
// PasswordStore. Reads are done asynchronously on a separate thread. It
// provides the virtual callback method, which is guaranteed to be executed on
// this (the UI) thread. It also provides the base::CancelableTaskTracker
// member, which cancels any outstanding tasks upon destruction.
class PasswordLeakHistoryConsumer {
public:
PasswordLeakHistoryConsumer();
// Called when the GetAllLeakedCredentials() request is finished, with the
// associated |leaked_credentials|.
virtual void OnGetLeakedCredentials(
std::vector<LeakedCredentials> leaked_credentials) = 0;
// The base::CancelableTaskTracker can be used for cancelling the tasks
// associated with the consumer.
base::CancelableTaskTracker* cancelable_task_tracker() {
return &cancelable_task_tracker_;
}
base::WeakPtr<PasswordLeakHistoryConsumer> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
protected:
virtual ~PasswordLeakHistoryConsumer();
private:
base::CancelableTaskTracker cancelable_task_tracker_;
base::WeakPtrFactory<PasswordLeakHistoryConsumer> weak_ptr_factory_{this};
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_LEAK_HISTORY_CONSUMER_H_
......@@ -24,6 +24,8 @@
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/leaked_credentials_table.h"
#include "components/password_manager/core/browser/password_leak_history_consumer.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
......@@ -340,6 +342,28 @@ void PasswordStore::GetSiteStats(const GURL& origin_domain,
base::BindOnce(&PasswordStore::GetSiteStatsImpl, this, origin_domain));
}
void PasswordStore::AddLeakedCredentials(
const LeakedCredentials& leaked_credentials) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::AddLeakedCredentialsImpl, this,
leaked_credentials));
}
void PasswordStore::RemoveLeakedCredentials(const GURL& url,
const base::string16& username) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::RemoveLeakedCredentialsImpl, this,
url, username));
}
void PasswordStore::GetAllLeakedCredentials(
PasswordLeakHistoryConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
consumer,
base::BindOnce(&PasswordStore::GetAllLeakedCredentialsImpl, this));
}
void PasswordStore::AddObserver(Observer* observer) {
observers_->AddObserver(observer);
}
......@@ -700,6 +724,15 @@ void PasswordStore::PostStatsTaskAndReplyToConsumerWithResult(
consumer->GetWeakPtr()));
}
void PasswordStore::PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
PasswordLeakHistoryConsumer* consumer,
LeakedCredentialsTask task) {
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE, std::move(task),
base::BindOnce(&PasswordLeakHistoryConsumer::OnGetLeakedCredentials,
consumer->GetWeakPtr()));
}
void PasswordStore::AddLoginInternal(const PasswordForm& form) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
SCOPED_UMA_HISTOGRAM_TIMER("PasswordManager.StorePerformance.AddLogin");
......
......@@ -54,10 +54,12 @@ using metrics_util::GaiaPasswordHashChange;
class AffiliatedMatchHelper;
class PasswordStoreConsumer;
class PasswordLeakHistoryConsumer;
class PasswordStoreSigninNotifier;
class PasswordSyncableService;
class PasswordSyncBridge;
struct InteractionsStats;
struct LeakedCredentials;
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
using PasswordHashDataList = base::Optional<std::vector<PasswordHashData>>;
......@@ -242,6 +244,19 @@ class PasswordStore : protected PasswordStoreSync,
// completion. The request will be cancelled if the consumer is destroyed.
void GetSiteStats(const GURL& origin_domain, PasswordStoreConsumer* consumer);
// Adds information about credentials leaked on |leaked_credentials.url| for
// |leaked_credentials.username|. The first |leaked_credentials.create_time|
// is kept, so if the record for given url and username already exists,
// the new one will be ignored.
void AddLeakedCredentials(const LeakedCredentials& leaked_credentials);
// Removes information about credentials leaked on |url| for |username|.
void RemoveLeakedCredentials(const GURL& url, const base::string16& username);
// Retrieves all leaked credentials and notifies |consumer| on completion. The
// request will be cancelled if the consumer is destroyed.
void GetAllLeakedCredentials(PasswordLeakHistoryConsumer* consumer);
// Adds an observer to be notified when the password store data changes.
void AddObserver(Observer* observer);
......@@ -437,6 +452,14 @@ class PasswordStore : protected PasswordStoreSync,
virtual std::vector<InteractionsStats> GetSiteStatsImpl(
const GURL& origin_domain) = 0;
// Synchronous implementation for manipulating with information about leaked
// credentials.
virtual void AddLeakedCredentialsImpl(
const LeakedCredentials& leaked_credentials) = 0;
virtual void RemoveLeakedCredentialsImpl(const GURL& url,
const base::string16& username) = 0;
virtual std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() = 0;
// PasswordStoreSync:
PasswordStoreChangeList AddLoginSync(const autofill::PasswordForm& form,
AddLoginError* error) override;
......@@ -511,6 +534,9 @@ class PasswordStore : protected PasswordStoreSync,
using StatsResult = std::vector<InteractionsStats>;
using StatsTask = base::OnceCallback<StatsResult()>;
using LeakedCredentialsResult = std::vector<LeakedCredentials>;
using LeakedCredentialsTask = base::OnceCallback<LeakedCredentialsResult()>;
// Called on the main thread after initialization is completed.
// |success| is true if initialization was successful. Sets the
// |init_status_|.
......@@ -538,6 +564,13 @@ class PasswordStore : protected PasswordStoreSync,
PasswordStoreConsumer* consumer,
StatsTask task);
// Schedules the given |task| to be run on the PasswordStore's TaskRunner.
// Invokes |consumer|->OnGetLeakedCredentials() on the caller's thread with
// the result.
void PostLeakedCredentialsTaskAndReplyToConsumerWithResult(
PasswordLeakHistoryConsumer* consumer,
LeakedCredentialsTask task);
// The following methods notify observers that the password store may have
// been modified via NotifyLoginsChanged(). Note that there is no guarantee
// that the called method will actually modify the password store data.
......
......@@ -222,6 +222,28 @@ std::vector<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl(
: std::vector<InteractionsStats>();
}
void PasswordStoreDefault::AddLeakedCredentialsImpl(
const LeakedCredentials& leaked_credentials) {
DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
if (login_db_)
login_db_->leaked_credentials_table().AddRow(leaked_credentials);
}
void PasswordStoreDefault::RemoveLeakedCredentialsImpl(
const GURL& url,
const base::string16& username) {
DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
if (login_db_)
login_db_->leaked_credentials_table().RemoveRow(url, username);
}
std::vector<LeakedCredentials>
PasswordStoreDefault::GetAllLeakedCredentialsImpl() {
DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
return login_db_ ? login_db_->leaked_credentials_table().GetAllRows()
: std::vector<LeakedCredentials>();
}
bool PasswordStoreDefault::BeginTransaction() {
if (login_db_)
return login_db_->BeginTransaction();
......
......@@ -81,6 +81,11 @@ class PasswordStoreDefault : public PasswordStore {
std::vector<InteractionsStats> GetAllSiteStatsImpl() override;
std::vector<InteractionsStats> GetSiteStatsImpl(
const GURL& origin_domain) override;
void AddLeakedCredentialsImpl(
const LeakedCredentials& leaked_credentials) override;
void RemoveLeakedCredentialsImpl(const GURL& url,
const base::string16& username) override;
std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
// Implements PasswordStoreSync interface.
bool BeginTransaction() override;
......
......@@ -21,6 +21,7 @@
#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_service.h"
#include "components/password_manager/core/browser/android_affiliation/mock_affiliated_match_helper.h"
#include "components/password_manager/core/browser/password_leak_history_consumer.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
......@@ -37,6 +38,7 @@ using autofill::PasswordForm;
using base::WaitableEvent;
using testing::_;
using testing::DoAll;
using testing::UnorderedElementsAre;
using testing::WithArg;
namespace password_manager {
......@@ -74,6 +76,16 @@ constexpr const char kTestAndroidIconURL1[] = "https://example.com/icon_1.png";
constexpr const char kTestAndroidName2[] = "Example Android App 2";
constexpr const char kTestAndroidIconURL2[] = "https://example.com/icon_2.png";
class MockPasswordLeakHistoryConsumer : public PasswordLeakHistoryConsumer {
public:
MockPasswordLeakHistoryConsumer() = default;
MOCK_METHOD1(OnGetLeakedCredentials, void(std::vector<LeakedCredentials>));
private:
DISALLOW_COPY_AND_ASSIGN(MockPasswordLeakHistoryConsumer);
};
class MockPasswordStoreConsumer : public PasswordStoreConsumer {
public:
MockPasswordStoreConsumer() = default;
......@@ -1235,6 +1247,39 @@ TEST_F(PasswordStoreTest, ReportMetricsForNonSyncPassword) {
store->ShutdownOnUIThread();
}
TEST_F(PasswordStoreTest, GetAllLeakedCredentials) {
LeakedCredentials leaked_credentials;
leaked_credentials.url = GURL("https://example.com");
leaked_credentials.username = base::ASCIIToUTF16("username");
leaked_credentials.create_time = base::Time::FromTimeT(1);
LeakedCredentials leaked_credentials2;
leaked_credentials2.url = GURL("https://example2.com");
leaked_credentials2.username = base::ASCIIToUTF16("username2");
leaked_credentials2.create_time = base::Time::FromTimeT(2);
scoped_refptr<PasswordStoreDefault> store = CreatePasswordStore();
store->Init(syncer::SyncableService::StartSyncFlare(), nullptr);
store->AddLeakedCredentials(leaked_credentials);
store->AddLeakedCredentials(leaked_credentials2);
MockPasswordLeakHistoryConsumer consumer;
EXPECT_CALL(consumer, OnGetLeakedCredentials(UnorderedElementsAre(
leaked_credentials, leaked_credentials2)));
store->GetAllLeakedCredentials(&consumer);
WaitForPasswordStore();
testing::Mock::VerifyAndClearExpectations(&consumer);
store->RemoveLeakedCredentials(leaked_credentials.url,
leaked_credentials.username);
EXPECT_CALL(consumer, OnGetLeakedCredentials(
UnorderedElementsAre(leaked_credentials2)));
store->GetAllLeakedCredentials(&consumer);
WaitForPasswordStore();
store->ShutdownOnUIThread();
}
#endif
} // namespace password_manager
......@@ -225,6 +225,23 @@ std::vector<InteractionsStats> TestPasswordStore::GetAllSiteStatsImpl() {
return std::vector<InteractionsStats>();
}
void TestPasswordStore::AddLeakedCredentialsImpl(
const LeakedCredentials& stats) {
NOTIMPLEMENTED();
}
void TestPasswordStore::RemoveLeakedCredentialsImpl(
const GURL& url,
const base::string16& username) {
NOTIMPLEMENTED();
}
std::vector<LeakedCredentials>
TestPasswordStore::GetAllLeakedCredentialsImpl() {
NOTIMPLEMENTED();
return std::vector<LeakedCredentials>();
}
bool TestPasswordStore::BeginTransaction() {
return true;
}
......
......@@ -85,6 +85,11 @@ class TestPasswordStore : public PasswordStore {
void AddSiteStatsImpl(const InteractionsStats& stats) override;
void RemoveSiteStatsImpl(const GURL& origin_domain) override;
std::vector<InteractionsStats> GetAllSiteStatsImpl() override;
void AddLeakedCredentialsImpl(
const LeakedCredentials& leaked_credentials) override;
void RemoveLeakedCredentialsImpl(const GURL& url,
const base::string16& username) override;
std::vector<LeakedCredentials> GetAllLeakedCredentialsImpl() override;
// PasswordStoreSync interface.
bool BeginTransaction() override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment