blob: 9877967789d3f6ac8853594a7bb80982a661cb6e [file] [log] [blame]
// Copyright 2014 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_store.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/ranges/algorithm.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/autofill/core/common/form_data.h"
#include "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/field_info_table.h"
#include "components/password_manager/core/browser/get_logins_with_affiliations_request_handler.h"
#include "components/password_manager/core/browser/insecure_credentials_consumer.h"
#include "components/password_manager/core/browser/insecure_credentials_table.h"
#include "components/password_manager/core/browser/password_form.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_reuse_manager_impl.h"
#include "components/password_manager/core/browser/password_store_consumer.h"
#include "components/password_manager/core/browser/password_store_signin_notifier.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/browser/sync/password_sync_bridge.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_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/sync/model/client_tag_based_model_type_processor.h"
#include "components/sync/model/proxy_model_type_controller_delegate.h"
namespace password_manager {
namespace {
void CloseTraceAndCallBack(
const char* trace_name,
PasswordStoreConsumer* consumer,
base::OnceCallback<void(std::vector<std::unique_ptr<PasswordForm>>)>
callback,
std::vector<std::unique_ptr<PasswordForm>> results) {
TRACE_EVENT_NESTABLE_ASYNC_END0("passwords", trace_name, consumer);
std::move(callback).Run(std::move(results));
}
std::vector<PasswordFormDigest> ConvertToForms(
const std::vector<std::string>& realms) {
std::vector<PasswordFormDigest> forms;
for (const auto& realm : realms)
forms.emplace_back(PasswordForm::Scheme::kHtml, realm, GURL(realm));
return forms;
}
} // namespace
PasswordStore::PasswordStore()
: observers_(new base::ObserverListThreadSafe<Observer>()) {}
bool PasswordStore::Init(PrefService* prefs,
base::RepeatingClosure sync_enabled_or_disabled_cb) {
main_task_runner_ = base::SequencedTaskRunnerHandle::Get();
DCHECK(main_task_runner_);
background_task_runner_ = CreateBackgroundTaskRunner();
DCHECK(background_task_runner_);
sync_enabled_or_disabled_cb_ = std::move(sync_enabled_or_disabled_cb);
prefs_ = prefs;
if (background_task_runner_) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
"passwords", "PasswordStore::InitOnBackgroundSequence", this);
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&PasswordStore::InitOnBackgroundSequence, this),
base::BindOnce(&PasswordStore::OnInitCompleted, this));
}
return true;
}
void PasswordStore::SetAffiliatedMatchHelper(
std::unique_ptr<AffiliatedMatchHelper> helper) {
affiliated_match_helper_ = std::move(helper);
}
void PasswordStore::AddLogin(const PasswordForm& form) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::AddLoginInternal, this, form));
}
void PasswordStore::UpdateLogin(const PasswordForm& form) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::UpdateLoginInternal, this, form));
}
void PasswordStore::UpdateLoginWithPrimaryKey(
const PasswordForm& new_form,
const PasswordForm& old_primary_key) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::UpdateLoginWithPrimaryKeyInternal,
this, new_form, old_primary_key));
}
void PasswordStore::RemoveLogin(const PasswordForm& form) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::RemoveLoginInternal, this, form));
}
void PasswordStore::RemoveLoginsByURLAndTime(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion,
base::OnceCallback<void(bool)> sync_completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::RemoveLoginsByURLAndTimeInternal,
this, url_filter, delete_begin, delete_end,
std::move(completion),
std::move(sync_completion)));
}
void PasswordStore::RemoveLoginsCreatedBetween(base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(
base::BindOnce(&PasswordStore::RemoveLoginsCreatedBetweenInternal, this,
delete_begin, delete_end, std::move(completion)));
}
void PasswordStore::DisableAutoSignInForOrigins(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(
base::BindOnce(&PasswordStore::DisableAutoSignInForOriginsInternal, this,
base::RepeatingCallback<bool(const GURL&)>(origin_filter),
std::move(completion)));
}
void PasswordStore::Unblocklist(const PasswordFormDigest& form_digest,
base::OnceClosure completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::UnblocklistInternal, this,
form_digest, std::move(completion)));
}
void PasswordStore::GetLogins(const PasswordFormDigest& form,
PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("passwords", "PasswordStore::GetLogins",
consumer);
scoped_refptr<GetLoginsWithAffiliationsRequestHandler> request_handler =
new GetLoginsWithAffiliationsRequestHandler(consumer->GetWeakPtr(), this);
if (affiliated_match_helper_) {
// The backend *is* the password_store and can therefore be passed with
// base::Unretained.
affiliated_match_helper_->GetAffiliatedAndroidAndWebRealms(
form,
base::BindOnce(ConvertToForms)
.Then(base::BindOnce(&PasswordStoreBackend::FillMatchingLoginsAsync,
base::Unretained(backend_),
request_handler->AffiliatedLoginsClosure())));
} else {
request_handler->AffiliatedLoginsClosure().Run({});
}
backend_->FillMatchingLoginsAsync(request_handler->LoginsForFormClosure(),
{form});
}
void PasswordStore::GetLoginsByPassword(
const std::u16string& plain_text_password,
PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
PostLoginsTaskAndReplyToConsumerWithResult(
consumer, base::BindOnce(&PasswordStore::GetLoginsByPasswordImpl, this,
plain_text_password));
}
void PasswordStore::GetAutofillableLogins(PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
backend_->GetAutofillableLoginsAsync(
base::BindOnce(&PasswordStoreConsumer::OnGetPasswordStoreResultsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this)));
}
void PasswordStore::GetAllLogins(PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
backend_->GetAllLoginsAsync(
base::BindOnce(&PasswordStoreConsumer::OnGetPasswordStoreResultsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this)));
}
void PasswordStore::GetAllLoginsWithAffiliationAndBrandingInformation(
PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
auto consumer_reply =
base::BindOnce(&PasswordStoreConsumer::OnGetPasswordStoreResultsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this));
auto affiliation_injection =
base::BindOnce(&PasswordStore::InjectAffiliationAndBrandingInformation,
this, std::move(consumer_reply));
backend_->GetAllLoginsAsync(std::move(affiliation_injection));
}
SmartBubbleStatsStore* PasswordStore::GetSmartBubbleStatsStore() {
return this;
}
void PasswordStore::AddSiteStats(const InteractionsStats& stats) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::AddSiteStatsImpl, this, stats));
}
void PasswordStore::RemoveSiteStats(const GURL& origin_domain) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(
base::BindOnce(&PasswordStore::RemoveSiteStatsImpl, this, origin_domain));
}
void PasswordStore::GetSiteStats(const GURL& origin_domain,
PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
PostStatsTaskAndReplyToConsumerWithResult(
consumer,
base::BindOnce(&PasswordStore::GetSiteStatsImpl, this, origin_domain));
}
void PasswordStore::RemoveStatisticsByOriginAndTime(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(
&PasswordStore::RemoveStatisticsByOriginAndTimeInternal, this,
origin_filter, delete_begin, delete_end, std::move(completion)));
}
void PasswordStore::ReportMetrics(const std::string& sync_username,
bool custom_passphrase_sync_enabled,
bool is_under_advanced_protection) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
if (background_task_runner_) {
base::OnceClosure task = base::BindOnce(
&PasswordStore::ReportMetricsImpl, this, sync_username,
custom_passphrase_sync_enabled,
BulkCheckDone(prefs_ && prefs_->HasPrefPath(
prefs::kLastTimePasswordCheckCompleted)));
background_task_runner_->PostDelayedTask(FROM_HERE, std::move(task),
base::TimeDelta::FromSeconds(30));
}
}
void PasswordStore::AddInsecureCredential(
const InsecureCredential& insecure_credential) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
auto callback = base::BindOnce(&PasswordStore::AddInsecureCredentialImpl,
this, insecure_credential);
ScheduleTask(base::BindOnce(
&PasswordStore::InvokeAndNotifyAboutInsecureCredentialsChange, this,
std::move(callback)));
}
void PasswordStore::RemoveInsecureCredentials(
const std::string& signon_realm,
const std::u16string& username,
RemoveInsecureCredentialsReason reason) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
auto callback = base::BindOnce(&PasswordStore::RemoveInsecureCredentialsImpl,
this, signon_realm, username, reason);
ScheduleTask(base::BindOnce(
&PasswordStore::InvokeAndNotifyAboutInsecureCredentialsChange, this,
std::move(callback)));
}
void PasswordStore::GetAllInsecureCredentials(
InsecureCredentialsConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
PostInsecureCredentialsTaskAndReplyToConsumerWithResult(
consumer,
base::BindOnce(&PasswordStore::GetAllInsecureCredentialsImpl, this));
}
void PasswordStore::GetMatchingInsecureCredentials(
const std::string& signon_realm,
InsecureCredentialsConsumer* consumer) {
if (affiliated_match_helper_) {
PasswordFormDigest form(PasswordForm::Scheme::kHtml, signon_realm,
GURL(signon_realm));
affiliated_match_helper_->GetAffiliatedAndroidAndWebRealms(
form,
base::BindOnce(
&PasswordStore::ScheduleGetInsecureCredentialsWithAffiliations,
this, consumer->GetWeakPtr(), signon_realm));
} else {
PostInsecureCredentialsTaskAndReplyToConsumerWithResult(
consumer,
base::BindOnce(&PasswordStore::GetMatchingInsecureCredentialsImpl, this,
signon_realm));
}
}
void PasswordStore::AddFieldInfo(const FieldInfo& field_info) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(
base::BindOnce(&PasswordStore::AddFieldInfoImpl, this, field_info));
}
void PasswordStore::GetAllFieldInfo(PasswordStoreConsumer* consumer) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
auto get_all_field_info_task =
base::BindOnce(&PasswordStore::GetAllFieldInfoImpl, this);
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE,
std::move(get_all_field_info_task),
base::BindOnce(&PasswordStoreConsumer::OnGetAllFieldInfo,
consumer->GetWeakPtr()));
}
void PasswordStore::RemoveFieldInfoByTime(base::Time remove_begin,
base::Time remove_end,
base::OnceClosure completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::RemoveFieldInfoByTimeInternal,
this, remove_begin, remove_end,
std::move(completion)));
}
void PasswordStore::ClearStore(base::OnceCallback<void(bool)> completion) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
ScheduleTask(base::BindOnce(&PasswordStore::ClearStoreInternal, this,
std::move(completion)));
}
void PasswordStore::AddObserver(Observer* observer) {
observers_->AddObserver(observer);
}
void PasswordStore::RemoveObserver(Observer* observer) {
observers_->RemoveObserver(observer);
}
bool PasswordStore::ScheduleTask(base::OnceClosure task) {
return background_task_runner_ &&
background_task_runner_->PostTask(FROM_HERE, std::move(task));
}
bool PasswordStore::IsAbleToSavePasswords() const {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
return init_status_ == InitStatus::kSuccess;
}
void PasswordStore::ShutdownOnUIThread() {
ScheduleTask(
base::BindOnce(&PasswordStore::DestroyOnBackgroundSequence, this));
// The AffiliationService must be destroyed from the main sequence.
affiliated_match_helper_.reset();
shutdown_called_ = true;
}
std::unique_ptr<syncer::ProxyModelTypeControllerDelegate>
PasswordStore::CreateSyncControllerDelegate() {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
// Note that a callback is bound for
// GetSyncControllerDelegateOnBackgroundSequence() because this getter itself
// must also run in the backend sequence, and the proxy object below will take
// care of that.
return std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
background_task_runner_,
base::BindRepeating(
&PasswordStore::GetSyncControllerDelegateOnBackgroundSequence,
base::Unretained(this)));
}
void PasswordStore::SetUnsyncedCredentialsDeletionNotifier(
std::unique_ptr<PasswordStore::UnsyncedCredentialsDeletionNotifier>
notifier) {
DCHECK(!deletion_notifier_);
DCHECK(notifier);
deletion_notifier_ = std::move(notifier);
}
void PasswordStore::SetSyncTaskTimeoutForTest(base::TimeDelta timeout) {
sync_task_timeout_ = timeout;
}
PasswordStore::~PasswordStore() {
DCHECK(shutdown_called_);
}
scoped_refptr<base::SequencedTaskRunner>
PasswordStore::CreateBackgroundTaskRunner() const {
return base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
}
bool PasswordStore::InitOnBackgroundSequence() {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
sync_bridge_ = base::WrapUnique(new PasswordSyncBridge(
std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
syncer::PASSWORDS, base::DoNothing()),
/*password_store_sync=*/this, sync_enabled_or_disabled_cb_));
return true;
}
void PasswordStore::NotifyLoginsChanged(
const PasswordStoreChangeList& changes) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
if (!changes.empty()) {
observers_->Notify(FROM_HERE, &Observer::OnLoginsChanged,
base::RetainedRef(this), changes);
if (sync_bridge_)
sync_bridge_->ActOnPasswordStoreChanges(changes);
}
}
void PasswordStore::NotifyDeletionsHaveSynced(bool success) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
// Either all deletions have been committed to the Sync server, or Sync is
// telling us that it won't commit them (because Sync was turned off
// permanently). In either case, run the corresponding callbacks now (on the
// main task runner).
DCHECK(!success || !GetMetadataStore()->HasUnsyncedDeletions());
if (!deletions_have_synced_callbacks_.empty()) {
base::UmaHistogramBoolean(
"PasswordManager.PasswordStoreDeletionsHaveSynced", success);
}
for (auto& callback : deletions_have_synced_callbacks_) {
main_task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), success));
}
deletions_have_synced_timeout_.Cancel();
deletions_have_synced_callbacks_.clear();
}
void PasswordStore::InvokeAndNotifyAboutInsecureCredentialsChange(
base::OnceCallback<PasswordStoreChangeList()> callback) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
NotifyLoginsChanged(std::move(callback).Run());
}
void PasswordStore::NotifyUnsyncedCredentialsWillBeDeleted(
std::vector<PasswordForm> unsynced_credentials) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
DCHECK(IsAccountStore());
// |deletion_notifier_| only gets set for desktop.
if (deletion_notifier_) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&PasswordStore::UnsyncedCredentialsDeletionNotifier::Notify,
deletion_notifier_->GetWeakPtr(), std::move(unsynced_credentials)));
}
}
void PasswordStore::OnInitCompleted(bool success) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
init_status_ = success ? InitStatus::kSuccess : InitStatus::kFailure;
UMA_HISTOGRAM_BOOLEAN("PasswordManager.PasswordStoreInitResult", success);
TRACE_EVENT_NESTABLE_ASYNC_END0(
"passwords", "PasswordStore::InitOnBackgroundSequence", this);
}
void PasswordStore::PostLoginsTaskAndReplyToConsumerWithResult(
PasswordStoreConsumer* consumer,
LoginsTask task) {
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE, std::move(task),
base::BindOnce(&PasswordStoreConsumer::OnGetPasswordStoreResultsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this)));
}
void PasswordStore::PostLoginsTaskAndReplyToConsumerWithProcessedResult(
const char* trace_name,
PasswordStoreConsumer* consumer,
LoginsTask task,
LoginsResultProcessor processor) {
auto call_consumer = base::BindOnce(
CloseTraceAndCallBack, trace_name, consumer,
base::BindOnce(&PasswordStoreConsumer::OnGetPasswordStoreResultsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this)));
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE, std::move(task),
base::BindOnce(std::move(processor), std::move(call_consumer)));
}
void PasswordStore::PostStatsTaskAndReplyToConsumerWithResult(
PasswordStoreConsumer* consumer,
StatsTask task) {
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE, std::move(task),
base::BindOnce(&PasswordStoreConsumer::OnGetSiteStatistics,
consumer->GetWeakPtr()));
}
void PasswordStore::PostInsecureCredentialsTaskAndReplyToConsumerWithResult(
InsecureCredentialsConsumer* consumer,
InsecureCredentialsTask task) {
consumer->cancelable_task_tracker()->PostTaskAndReplyWithResult(
background_task_runner_.get(), FROM_HERE, std::move(task),
base::BindOnce(&InsecureCredentialsConsumer::OnGetInsecureCredentialsFrom,
consumer->GetWeakPtr(), base::RetainedRef(this)));
}
void PasswordStore::AddLoginInternal(const PasswordForm& form) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::AddLoginInternal");
BeginTransaction();
PasswordStoreChangeList changes = AddLoginImpl(form);
NotifyLoginsChanged(changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
}
void PasswordStore::UpdateLoginInternal(const PasswordForm& form) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::UpdateLoginInternal");
BeginTransaction();
PasswordStoreChangeList changes = UpdateLoginImpl(form);
NotifyLoginsChanged(changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
}
void PasswordStore::RemoveLoginInternal(const PasswordForm& form) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::RemoveLoginInternal");
BeginTransaction();
PasswordStoreChangeList changes = RemoveLoginImpl(form);
NotifyLoginsChanged(changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
}
void PasswordStore::UpdateLoginWithPrimaryKeyInternal(
const PasswordForm& new_form,
const PasswordForm& old_primary_key) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::UpdateLoginWithPrimaryKeyInternal");
BeginTransaction();
PasswordStoreChangeList all_changes = RemoveLoginImpl(old_primary_key);
PasswordStoreChangeList changes = AddLoginImpl(new_form);
all_changes.insert(all_changes.end(), changes.begin(), changes.end());
NotifyLoginsChanged(all_changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
}
void PasswordStore::RemoveLoginsByURLAndTimeInternal(
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion,
base::OnceCallback<void(bool)> sync_completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::RemoveLoginsByURLAndTimeInternal");
BeginTransaction();
PasswordStoreChangeList changes =
RemoveLoginsByURLAndTimeImpl(url_filter, delete_begin, delete_end);
NotifyLoginsChanged(changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
if (sync_completion) {
deletions_have_synced_callbacks_.push_back(std::move(sync_completion));
// Start a timeout for sync, or restart it if it was already running.
deletions_have_synced_timeout_.Reset(base::BindOnce(
&PasswordStore::NotifyDeletionsHaveSynced, this, /*success=*/false));
background_task_runner_->PostDelayedTask(
FROM_HERE, deletions_have_synced_timeout_.callback(),
sync_task_timeout_);
// Do an immediate check for the case where there are already no unsynced
// deletions.
if (!GetMetadataStore()->HasUnsyncedDeletions())
NotifyDeletionsHaveSynced(/*success=*/true);
}
}
void PasswordStore::RemoveLoginsCreatedBetweenInternal(
base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords",
"PasswordStore::RemoveLoginsCreatedBetweenInternal");
BeginTransaction();
PasswordStoreChangeList changes =
RemoveLoginsCreatedBetweenImpl(delete_begin, delete_end);
NotifyLoginsChanged(changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
}
void PasswordStore::RemoveStatisticsByOriginAndTimeInternal(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceClosure completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords",
"PasswordStore::RemoveStatisticsByOriginAndTimeInternal");
RemoveStatisticsByOriginAndTimeImpl(origin_filter, delete_begin, delete_end);
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
}
void PasswordStore::DisableAutoSignInForOriginsInternal(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords",
"PasswordStore::DisableAutoSignInForOriginsInternal");
DisableAutoSignInForOriginsImpl(origin_filter);
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
}
void PasswordStore::UnblocklistInternal(const PasswordFormDigest& form_digest,
base::OnceClosure completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::UnblocklistInternal");
std::vector<std::unique_ptr<PasswordForm>> all_matches =
GetLoginsImpl(form_digest);
for (auto& form : all_matches) {
// Ignore PSL matches for blocked entries.
if (form->blocked_by_user && !form->is_public_suffix_match)
RemoveLoginInternal(*form);
}
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
}
void PasswordStore::RemoveFieldInfoByTimeInternal(
base::Time remove_begin,
base::Time remove_end,
base::OnceClosure completion) {
RemoveFieldInfoByTimeImpl(remove_begin, remove_end);
if (completion)
main_task_runner_->PostTask(FROM_HERE, std::move(completion));
}
void PasswordStore::ClearStoreInternal(
base::OnceCallback<void(bool)> completion) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
bool should_clear = !IsEmpty();
if (should_clear)
DeleteAndRecreateDatabaseFile();
if (completion) {
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(completion), should_clear));
}
}
std::vector<std::unique_ptr<PasswordForm>> PasswordStore::GetLoginsImpl(
const PasswordFormDigest& form) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
return FillMatchingLogins(form);
}
std::vector<std::unique_ptr<PasswordForm>>
PasswordStore::GetLoginsByPasswordImpl(
const std::u16string& plain_text_password) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
TRACE_EVENT0("passwords", "PasswordStore::GetLoginsByPasswordImpl");
return FillMatchingLoginsByPassword(plain_text_password);
}
std::vector<InsecureCredential>
PasswordStore::GetInsecureCredentialsWithAffiliationsImpl(
const std::string& signon_realm,
const std::vector<std::string>& additional_affiliated_realms) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
std::vector<InsecureCredential> results(
GetMatchingInsecureCredentialsImpl(signon_realm));
for (const std::string& realm : additional_affiliated_realms) {
std::vector<InsecureCredential> more_results(
GetMatchingInsecureCredentialsImpl(realm));
results.insert(results.end(), std::make_move_iterator(more_results.begin()),
std::make_move_iterator(more_results.end()));
}
return results;
}
void PasswordStore::InjectAffiliationAndBrandingInformation(
LoginsReply callback,
LoginsResult forms) {
if (affiliated_match_helper_) {
affiliated_match_helper_->InjectAffiliationAndBrandingInformation(
std::move(forms), AndroidAffiliationService::StrategyOnCacheMiss::FAIL,
std::move(callback));
} else {
std::move(callback).Run(std::move(forms));
}
}
void PasswordStore::ScheduleGetInsecureCredentialsWithAffiliations(
base::WeakPtr<InsecureCredentialsConsumer> consumer,
const std::string& signon_realm,
const std::vector<std::string>& additional_affiliated_realms) {
if (consumer) {
PostInsecureCredentialsTaskAndReplyToConsumerWithResult(
consumer.get(),
base::BindOnce(
&PasswordStore::GetInsecureCredentialsWithAffiliationsImpl, this,
signon_realm, additional_affiliated_realms));
}
}
std::unique_ptr<PasswordForm> PasswordStore::GetLoginImpl(
const PasswordForm& primary_key) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
std::vector<std::unique_ptr<PasswordForm>> candidates(
FillMatchingLogins(PasswordFormDigest(primary_key)));
for (auto& candidate : candidates) {
if (ArePasswordFormUniqueKeysEqual(*candidate, primary_key) &&
!candidate->is_public_suffix_match) {
return std::move(candidate);
}
}
return nullptr;
}
void PasswordStore::FindAndUpdateAffiliatedWebLogins(
const PasswordForm& added_or_updated_android_form) {
if (!affiliated_match_helper_)
return;
affiliated_match_helper_->GetAffiliatedWebRealms(
PasswordFormDigest(added_or_updated_android_form),
base::BindOnce(&PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl,
this, added_or_updated_android_form));
}
void PasswordStore::ScheduleFindAndUpdateAffiliatedWebLogins(
const PasswordForm& added_or_updated_android_form) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PasswordStore::FindAndUpdateAffiliatedWebLogins, this,
added_or_updated_android_form));
}
void PasswordStore::UpdateAffiliatedWebLoginsImpl(
const PasswordForm& updated_android_form,
const std::vector<std::string>& affiliated_web_realms) {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
BeginTransaction();
PasswordStoreChangeList all_changes;
for (const std::string& affiliated_web_realm : affiliated_web_realms) {
std::vector<std::unique_ptr<PasswordForm>> web_logins(FillMatchingLogins(
{PasswordForm::Scheme::kHtml, affiliated_web_realm, GURL()}));
for (auto& web_login : web_logins) {
// Do not update HTTP logins, logins saved under insecure conditions, and
// non-HTML login forms; PSL matches; logins with a different username;
// and logins with the same password (to avoid generating no-op updates).
if (!AffiliatedMatchHelper::IsValidWebCredential(
PasswordFormDigest(*web_login)) ||
web_login->is_public_suffix_match ||
web_login->username_value != updated_android_form.username_value ||
web_login->password_value == updated_android_form.password_value)
continue;
// If the |web_login| was updated in the same or a later chunk of Sync
// changes, assume that it is more recent and do not update it. Note that
// this check is far from perfect conflict resolution and mostly prevents
// long-dormant Sync clients doing damage when they wake up in the face
// of the following list of changes:
//
// Time Source Change
// ==== ====== ======
// #1 Android android_login.password_value = "A"
// #2 Client A web_login.password_value = "A" (propagation)
// #3 Client A web_login.password_value = "B" (manual overwrite)
//
// When long-dormant Sync client B wakes up, it will only get a distilled
// subset of not-yet-obsoleted changes {1, 3}. In this case, client B must
// not propagate password "A" to |web_login|. This is prevented as change
// #3 will arrive either in the same/later chunk of sync changes, so the
// |date_synced| of |web_login| value will be greater or equal.
//
// Note that this solution has several shortcomings:
//
// (1) It will not prevent local changes to |web_login| from being
// overwritten if they were made shortly after start-up, before
// Sync changes are applied. This should be tolerable.
//
// (2) It assumes that all Sync clients are fully capable of propagating
// changes to web credentials on their own. If client C runs an
// older version of Chrome and updates the password for |web_login|
// around the time when the |android_login| is updated, the updated
// password will not be propagated by client B to |web_login| when
// it wakes up, regardless of the temporal order of the original
// changes, as client B will see both credentials having the same
// |data_synced|.
//
// (2a) Above could be mitigated by looking not only at |data_synced|,
// but also at the actual order of Sync changes.
//
// (2b) However, (2a) is still not workable, as a Sync change is made
// when any attribute of the credential is updated, not only the
// password. Hence it is not possible for client B to distinguish
// between two following two event orders:
//
// #1 Android android_login.password_value = "A"
// #2 Client C web_login.password_value = "B" (manual overwrite)
// #3 Android android_login.random_attribute = "..."
//
// #1 Client C web_login.password_value = "B" (manual overwrite)
// #2 Android android_login.password_value = "A"
//
// And so it must assume that it is unsafe to update |web_login|.
if (web_login->date_synced >= updated_android_form.date_synced)
continue;
web_login->password_value = updated_android_form.password_value;
PasswordStoreChangeList changes = UpdateLoginImpl(*web_login);
all_changes.insert(all_changes.end(), changes.begin(), changes.end());
}
}
NotifyLoginsChanged(all_changes);
// Sync metadata get updated in NotifyLoginsChanged(). Therefore,
// CommitTransaction() must be called after NotifyLoginsChanged(), because
// sync codebase needs to update metadata atomically together with the login
// data.
CommitTransaction();
}
void PasswordStore::ScheduleUpdateAffiliatedWebLoginsImpl(
const PasswordForm& updated_android_form,
const std::vector<std::string>& affiliated_web_realms) {
ScheduleTask(base::BindOnce(&PasswordStore::UpdateAffiliatedWebLoginsImpl,
this, updated_android_form,
affiliated_web_realms));
}
base::WeakPtr<syncer::ModelTypeControllerDelegate>
PasswordStore::GetSyncControllerDelegateOnBackgroundSequence() {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
DCHECK(sync_bridge_);
return sync_bridge_->change_processor()->GetControllerDelegate();
}
void PasswordStore::DestroyOnBackgroundSequence() {
DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
sync_bridge_.reset();
}
} // namespace password_manager