| // 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 |