blob: ddb4f9e3da4adef2e62d06596d31842445a4aac1 [file] [log] [blame]
// Copyright 2017 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/signin/core/browser/dice_account_reconcilor_delegate.h"
#include <vector>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_client.h"
#include "components/signin/core/browser/signin_pref_names.h"
namespace signin {
DiceAccountReconcilorDelegate::DiceAccountReconcilorDelegate(
SigninClient* signin_client,
AccountConsistencyMethod account_consistency)
: signin_client_(signin_client), account_consistency_(account_consistency) {
DCHECK(signin_client_);
DCHECK(DiceMethodGreaterOrEqual(account_consistency_,
AccountConsistencyMethod::kDiceMigration));
}
bool DiceAccountReconcilorDelegate::IsReconcileEnabled() const {
return true;
}
bool DiceAccountReconcilorDelegate::IsAccountConsistencyEnforced() const {
return account_consistency_ == AccountConsistencyMethod::kDice;
}
gaia::GaiaSource DiceAccountReconcilorDelegate::GetGaiaApiSource() const {
return gaia::GaiaSource::kAccountReconcilorDice;
}
// - On first execution, the candidates are examined in this order:
// 1. The primary account
// 2. The current first Gaia account
// 3. The last known first Gaia account
// 4. The first account in the token service
// - On subsequent executions, the order is:
// 1. The current first Gaia account
// 2. The primary account
// 3. The last known first Gaia account
// 4. The first account in the token service
std::string DiceAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile(
const std::vector<std::string>& chrome_accounts,
const std::vector<gaia::ListedAccount>& gaia_accounts,
const std::string& primary_account,
bool first_execution,
bool will_logout) const {
bool primary_account_has_token =
!primary_account.empty() &&
base::ContainsValue(chrome_accounts, primary_account);
if (gaia_accounts.empty()) {
if (primary_account_has_token)
return primary_account;
// Try the last known account. This happens when the cookies are cleared
// while Sync is disabled.
if (base::ContainsValue(chrome_accounts, last_known_first_account_))
return last_known_first_account_;
// As a last resort, use the first Chrome account.
return chrome_accounts.empty() ? std::string() : chrome_accounts[0];
}
const std::string& first_gaia_account = gaia_accounts[0].id;
bool first_gaia_account_has_token =
base::ContainsValue(chrome_accounts, first_gaia_account);
if (!first_gaia_account_has_token &&
(primary_account == first_gaia_account) && gaia_accounts[0].valid) {
// The primary account is also the first Gaia account, and has no token.
// Logout everything.
return std::string();
}
// If the primary Chrome account and the default Gaia account are both in
// error, then the first gaia account can be kept, to avoid logging the user
// out of their other accounts.
// It's only possible when the reconcilor will not perform a logout, because
// that account cannot be rebuilt.
if (!first_gaia_account_has_token && !gaia_accounts[0].valid && !will_logout)
return first_gaia_account;
if (first_execution) {
// On first execution, try the primary account, and then the first Gaia
// account.
if (primary_account_has_token)
return primary_account;
if (first_gaia_account_has_token)
return first_gaia_account;
// As a last resort, use the first Chrome account.
return chrome_accounts.empty() ? std::string() : chrome_accounts[0];
}
// While Chrome is running, try the first Gaia account, and then the
// primary account.
if (first_gaia_account_has_token)
return first_gaia_account;
if (primary_account_has_token)
return primary_account;
// Changing the first Gaia account while Chrome is running would be
// confusing for the user. Logout everything.
return std::string();
}
gaia::MultiloginMode DiceAccountReconcilorDelegate::CalculateModeForReconcile(
const std::vector<gaia::ListedAccount>& gaia_accounts,
const std::string primary_account,
bool first_execution,
bool primary_has_error) const {
const bool sync_enabled = !primary_account.empty();
const bool first_gaia_is_primary =
!gaia_accounts.empty() && (gaia_accounts[0].id == primary_account);
return sync_enabled && first_execution && !primary_has_error &&
!first_gaia_is_primary
? gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER
: gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER;
}
std::vector<std::string>
DiceAccountReconcilorDelegate::GetChromeAccountsForReconcile(
const std::vector<std::string>& chrome_accounts,
const std::string& primary_account,
const std::vector<gaia::ListedAccount>& gaia_accounts,
const gaia::MultiloginMode mode) const {
if (mode == gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER) {
return ReorderChromeAccountsForReconcile(chrome_accounts, primary_account,
gaia_accounts);
}
if (gaia_accounts.empty() &&
base::ContainsValue(chrome_accounts, last_known_first_account_)) {
// In PRESERVE mode in case accounts in cookies are accidentally lost we
// should put cached first account first since Gaia has no information about
// it.
return ReorderChromeAccountsForReconcile(
chrome_accounts, last_known_first_account_, gaia_accounts);
}
return chrome_accounts;
}
AccountReconcilorDelegate::RevokeTokenOption
DiceAccountReconcilorDelegate::ShouldRevokeSecondaryTokensBeforeReconcile(
const std::vector<gaia::ListedAccount>& gaia_accounts) {
// During the Dice migration step, before Dice is actually enabled, chrome
// tokens must be cleared when the cookies are cleared.
if ((account_consistency_ == AccountConsistencyMethod::kDiceMigration) &&
gaia_accounts.empty()) {
return RevokeTokenOption::kRevoke;
}
return (account_consistency_ == AccountConsistencyMethod::kDice)
? RevokeTokenOption::kRevokeIfInError
: RevokeTokenOption::kDoNotRevoke;
}
bool DiceAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
return account_consistency_ == AccountConsistencyMethod::kDice;
}
void DiceAccountReconcilorDelegate::OnReconcileFinished(
const std::string& first_account,
bool reconcile_is_noop) {
last_known_first_account_ = first_account;
// Migration happens on startup if the last reconcile was a no-op and the
// refresh tokens are Dice-compatible.
signin_client_->SetReadyForDiceMigration(
reconcile_is_noop && signin_client_->GetPrefs()->GetBoolean(
prefs::kTokenServiceDiceCompatible));
}
} // namespace signin