blob: cf3ff871d30178b9af68e16f7dff59d595bd9e81 [file] [log] [blame]
// Copyright 2018 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/sync/password_model_type_controller.h"
#include <utility>
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/password_manager/core/browser/password_manager_features_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/sync/base/model_type.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h"
#include "components/sync/model/model_type_controller_delegate.h"
namespace password_manager {
namespace {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ClearedOnStartup {
kOptedInSoNoNeedToClear = 0,
kNotOptedInAndWasAlreadyEmpty = 1,
kNotOptedInAndHadToClear = 2,
kMaxValue = kNotOptedInAndHadToClear
};
void RecordClearedOnStartup(ClearedOnStartup state) {
base::UmaHistogramEnumeration(
"PasswordManager.AccountStorage.ClearedOnStartup", state);
}
void PasswordStoreClearDone(bool cleared) {
RecordClearedOnStartup(cleared
? ClearedOnStartup::kNotOptedInAndHadToClear
: ClearedOnStartup::kNotOptedInAndWasAlreadyEmpty);
}
} // namespace
PasswordModelTypeController::PasswordModelTypeController(
std::unique_ptr<syncer::ModelTypeControllerDelegate>
delegate_for_full_sync_mode,
std::unique_ptr<syncer::ModelTypeControllerDelegate>
delegate_for_transport_mode,
scoped_refptr<PasswordStore> account_password_store_for_cleanup,
PrefService* pref_service,
signin::IdentityManager* identity_manager,
syncer::SyncService* sync_service,
const base::RepeatingClosure& state_changed_callback)
: ModelTypeController(syncer::PASSWORDS,
std::move(delegate_for_full_sync_mode),
std::move(delegate_for_transport_mode)),
pref_service_(pref_service),
identity_manager_(identity_manager),
sync_service_(sync_service),
state_changed_callback_(state_changed_callback),
account_storage_settings_watcher_(
pref_service_,
sync_service_,
base::BindRepeating(
&PasswordModelTypeController::OnOptInStateMaybeChanged,
base::Unretained(this))) {
identity_manager_->AddObserver(this);
DCHECK_EQ(
!!base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage),
!!account_password_store_for_cleanup);
if (base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage)) {
// Note: Right now, we're still in the middle of SyncService initialization,
// so we can't check IsOptedInForAccountStorage() yet (SyncService might not
// have determined the syncing account yet). Post a task do to it after the
// initialization is complete.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&PasswordModelTypeController::MaybeClearStore,
weak_ptr_factory_.GetWeakPtr(),
account_password_store_for_cleanup));
} else {
// If the feature flag is disabled, clear any related prefs that might still
// be around.
features_util::ClearAccountStorageSettingsForAllUsers(pref_service_);
}
}
PasswordModelTypeController::~PasswordModelTypeController() {
identity_manager_->RemoveObserver(this);
}
void PasswordModelTypeController::LoadModels(
const syncer::ConfigureContext& configure_context,
const ModelLoadCallback& model_load_callback) {
DCHECK(CalledOnValidThread());
sync_service_->AddObserver(this);
sync_mode_ = configure_context.sync_mode;
ModelTypeController::LoadModels(configure_context, model_load_callback);
state_changed_callback_.Run();
}
void PasswordModelTypeController::Stop(syncer::ShutdownReason shutdown_reason,
StopCallback callback) {
DCHECK(CalledOnValidThread());
sync_service_->RemoveObserver(this);
// In transport-only mode, our storage is scoped to the Gaia account. That
// means it should be cleared if Sync is stopped for any reason (other than
// just browser shutdown). E.g. when switching to full-Sync mode, we don't
// want to end up with two copies of the passwords (one in the profile DB, one
// in the account DB).
if (sync_mode_ == syncer::SyncMode::kTransportOnly) {
switch (shutdown_reason) {
case syncer::STOP_SYNC:
shutdown_reason = syncer::DISABLE_SYNC;
break;
case syncer::DISABLE_SYNC:
case syncer::BROWSER_SHUTDOWN:
break;
}
}
ModelTypeController::Stop(shutdown_reason, std::move(callback));
state_changed_callback_.Run();
}
syncer::DataTypeController::PreconditionState
PasswordModelTypeController::GetPreconditionState() const {
// If Sync-the-feature is enabled, then the user has opted in to that, and no
// additional opt-in is required here.
if (sync_service_->IsSyncFeatureEnabled() ||
sync_service_->IsLocalSyncEnabled()) {
return PreconditionState::kPreconditionsMet;
}
// If Sync-the-feature is *not* enabled, then password sync should only be
// turned on if the user has opted in to the account-scoped storage.
return features_util::IsOptedInForAccountStorage(pref_service_, sync_service_)
? PreconditionState::kPreconditionsMet
: PreconditionState::kMustStopAndClearData;
}
bool PasswordModelTypeController::ShouldRunInTransportOnlyMode() const {
if (!base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage)) {
return false;
}
if (sync_service_->GetUserSettings()->IsUsingSecondaryPassphrase()) {
return false;
}
return true;
}
void PasswordModelTypeController::OnStateChanged(syncer::SyncService* sync) {
DCHECK(CalledOnValidThread());
sync_service_->DataTypePreconditionChanged(syncer::PASSWORDS);
state_changed_callback_.Run();
}
void PasswordModelTypeController::OnAccountsInCookieUpdated(
const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
const GoogleServiceAuthError& error) {
// If the account information is stale, do nothing for now - wait until there
// is fresh information.
if (!accounts_in_cookie_jar_info.accounts_are_fresh) {
return;
}
// Collect all the known accounts (signed-in or signed-out).
std::vector<std::string> gaia_ids;
for (const gaia::ListedAccount& account :
accounts_in_cookie_jar_info.signed_in_accounts) {
gaia_ids.push_back(account.gaia_id);
}
for (const gaia::ListedAccount& account :
accounts_in_cookie_jar_info.signed_out_accounts) {
gaia_ids.push_back(account.gaia_id);
}
// Keep any account-storage settings only for known accounts.
features_util::KeepAccountStorageSettingsOnlyForUsers(pref_service_,
gaia_ids);
}
void PasswordModelTypeController::OnAccountsCookieDeletedByUserAction() {
features_util::ClearAccountStorageSettingsForAllUsers(pref_service_);
}
void PasswordModelTypeController::OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event) {
if (event.GetEventTypeFor(signin::ConsentLevel::kSync) ==
signin::PrimaryAccountChangeEvent::Type::kCleared) {
// Note: kCleared event for ConsentLevel::kSync basically means that the
// consent for Sync-the-feature was revoked. In this case, also clear any
// possible matching opt-in for the account-scoped storage, since it'd
// probably be surprising to the user if their account passwords still
// remained after disabling Sync.
features_util::OptOutOfAccountStorageAndClearSettingsForAccount(
pref_service_, event.GetPreviousState().primary_account.gaia);
}
}
void PasswordModelTypeController::OnOptInStateMaybeChanged() {
// Note: This method gets called in many other situations as well, not just
// when the opt-in state changes, but DataTypePreconditionChanged() is cheap
// if nothing actually changed, so some spurious calls don't hurt.
sync_service_->DataTypePreconditionChanged(syncer::PASSWORDS);
}
void PasswordModelTypeController::MaybeClearStore(
scoped_refptr<PasswordStore> account_password_store_for_cleanup) {
DCHECK(account_password_store_for_cleanup);
if (features_util::IsOptedInForAccountStorage(pref_service_, sync_service_)) {
RecordClearedOnStartup(ClearedOnStartup::kOptedInSoNoNeedToClear);
} else {
account_password_store_for_cleanup->ClearStore(
base::BindOnce(&PasswordStoreClearDone));
}
}
} // namespace password_manager