diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-03 13:32:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 14:31:55 +0200 |
commit | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (patch) | |
tree | 91be119f694044dfc1ff9fdc054459e925de9df0 /chromium/components/signin | |
parent | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (diff) | |
download | qtwebengine-chromium-21ba0c5d4bf8fba15dddd97cd693bad2358b77fd.tar.gz |
BASELINE: Update Chromium to 92.0.4515.166
Change-Id: I42a050486714e9e54fc271f2a8939223a02ae364
Diffstat (limited to 'chromium/components/signin')
106 files changed, 2012 insertions, 1759 deletions
diff --git a/chromium/components/signin/OWNERS b/chromium/components/signin/OWNERS index e2126a9765d..9dfe6d9bb76 100644 --- a/chromium/components/signin/OWNERS +++ b/chromium/components/signin/OWNERS @@ -1,3 +1,4 @@ +alexilin@chromium.org bsazonov@chromium.org droger@chromium.org msalama@chromium.org diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index ed063470251..13e7ce4a768 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -20,6 +20,8 @@ static_library("browser") { "account_reconcilor.h", "account_reconcilor_delegate.cc", "account_reconcilor_delegate.h", + "account_reconcilor_throttler.cc", + "account_reconcilor_throttler.h", "chrome_connected_header_helper.cc", "chrome_connected_header_helper.h", "cookie_reminter.cc", @@ -84,13 +86,23 @@ static_library("browser") { "active_directory_account_reconcilor_delegate.cc", "active_directory_account_reconcilor_delegate.h", ] - deps += [ "//chromeos/tpm:tpm" ] + deps += [ + "//chromeos/crosapi/mojom", + "//chromeos/tpm:tpm", + ] sources -= [ "signin_status_metrics_provider.cc", "signin_status_metrics_provider_delegate.cc", ] } + if (is_chromeos_lacros) { + deps += [ + "//chromeos/crosapi/mojom", + "//chromeos/lacros", + ] + } + if (!enable_dice_support) { sources -= [ "dice_account_reconcilor_delegate.cc", @@ -138,13 +150,24 @@ source_set("unit_tests") { ] if (is_chromeos_ash) { - deps += [ "//chromeos/tpm:test_support" ] + deps += [ + "//chromeos/crosapi/mojom", + "//chromeos/tpm:test_support", + ] sources -= [ "account_investigator_unittest.cc", "signin_status_metrics_provider_unittest.cc", ] } + if (is_chromeos_lacros) { + deps += [ + "//chromeos/crosapi/mojom", + "//chromeos/lacros", + "//chromeos/lacros:test_support", + ] + } + if (!enable_dice_support) { sources -= [ "dice_account_reconcilor_delegate_unittest.cc" ] } diff --git a/chromium/components/signin/core/browser/DEPS b/chromium/components/signin/core/browser/DEPS index 080a1d60ee4..9712cbfae5a 100644 --- a/chromium/components/signin/core/browser/DEPS +++ b/chromium/components/signin/core/browser/DEPS @@ -11,5 +11,15 @@ specific_include_rules = { "account_reconcilor_unittest.cc": [ "+chromeos/tpm/install_attributes.h", "+chromeos/tpm/stub_install_attributes.h" + ], + # TODO(crbug.com/1198528): remove Lacros deps after the rollout. + "chrome_connected_header_helper.cc": [ + "+chromeos/lacros/lacros_chrome_service_impl.h", + "+chromeos/crosapi/mojom/crosapi.mojom.h" + ], + "signin_header_helper_unittest.cc": [ + "+chromeos/lacros/lacros_chrome_service_delegate.h", + "+chromeos/lacros/lacros_chrome_service_impl.h", + "+chromeos/lacros/lacros_test_helper.h" ] } diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc index 95f1a3ce24e..ef2abc091b7 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.cc +++ b/chromium/components/signin/core/browser/about_signin_internals.cc @@ -203,6 +203,17 @@ std::string GetAccountConsistencyDescription( return ""; } +std::string GetSigninStatusDescription( + signin::IdentityManager* identity_manager) { + if (!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) { + return "Not Signed In"; + } else if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) { + return "Signed In, Consented for Sync"; + } else { + return "Signed In, Not Consented for Sync"; + } +} + } // anonymous namespace AboutSigninInternals::AboutSigninInternals( @@ -615,11 +626,8 @@ AboutSigninInternals::SigninStatus::ToValue( AddSection(signin_info.get(), "Basic Information"); AddSectionEntry(basic_info, "Account Consistency", GetAccountConsistencyDescription(account_consistency)); - AddSectionEntry( - basic_info, "Signin Status", - identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync) - ? "Signed In" - : "Not Signed In"); + AddSectionEntry(basic_info, "Signin Status", + GetSigninStatusDescription(identity_manager)); signin::LoadCredentialsState load_tokens_state = identity_manager->GetDiagnosticsProvider() ->GetDetailedStateOfLoadingOfRefreshTokens(); @@ -629,9 +637,9 @@ AboutSigninInternals::SigninStatus::ToValue( basic_info, "Gaia cookies state", GetGaiaCookiesStateAsString(GetGaiaCookiesState(signin_client))); - if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) { + if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) { CoreAccountInfo account_info = - identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); AddSectionEntry(basic_info, SigninStatusFieldToLabel(signin_internals_util::ACCOUNT_ID), account_info.account_id.ToString()); @@ -644,7 +652,7 @@ AboutSigninInternals::SigninStatus::ToValue( if (signin_error_controller->HasError()) { const CoreAccountId error_account_id = signin_error_controller->error_account_id(); - const base::Optional<AccountInfo> error_account_info = + const absl::optional<AccountInfo> error_account_info = identity_manager ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( error_account_id); diff --git a/chromium/components/signin/core/browser/account_investigator.cc b/chromium/components/signin/core/browser/account_investigator.cc index 4924dd4b2ca..870e30210eb 100644 --- a/chromium/components/signin/core/browser/account_investigator.cc +++ b/chromium/components/signin/core/browser/account_investigator.cc @@ -37,12 +37,12 @@ bool AreSame(const CoreAccountInfo& info, const ListedAccount& account) { // Returns the extended info for the primary account (no consent required) if // available. -base::Optional<AccountInfo> GetExtendedAccountInfo( +absl::optional<AccountInfo> GetExtendedAccountInfo( signin::IdentityManager* identity_manager) { CoreAccountId account_id = identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin); if (account_id.empty()) - return base::nullopt; + return absl::nullopt; return identity_manager ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( account_id); @@ -244,7 +244,7 @@ void AccountInvestigator::DoPeriodicReport( if (identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) { const bool is_syncing = identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync); - base::Optional<AccountInfo> info = + absl::optional<AccountInfo> info = GetExtendedAccountInfo(identity_manager_); signin_metrics::LogSignedInCookiesCountsPerPrimaryAccountType( signed_in_accounts.size(), is_syncing, info->IsManaged()); diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc index 35deb4520ad..57bb3635d06 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.cc +++ b/chromium/components/signin/core/browser/account_reconcilor.cc @@ -17,6 +17,8 @@ #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" @@ -175,6 +177,26 @@ bool HasUnknownInvalidAccountInCookie( } // namespace +// static +const char AccountReconcilor::kOperationHistogramName[] = + "Signin.Reconciler.Operation"; + +// static +const char AccountReconcilor::kTriggerLogoutHistogramName[] = + "Signin.Reconciler.Trigger.Logout"; + +// static +const char AccountReconcilor::kTriggerMultiloginHistogramName[] = + "Signin.Reconciler.Trigger.Multilogin"; + +// static +const char AccountReconcilor::kTriggerNoopHistogramName[] = + "Signin.Reconciler.Trigger.Noop"; + +// static +const char AccountReconcilor::kTriggerThrottledHistogramName[] = + "Signin.Reconciler.Trigger.Throttled"; + AccountReconcilor::Lock::Lock(AccountReconcilor* reconcilor) : reconcilor_(reconcilor->weak_factory_.GetWeakPtr()) { DCHECK(reconcilor_); @@ -208,20 +230,7 @@ AccountReconcilor::AccountReconcilor( std::unique_ptr<signin::AccountReconcilorDelegate> delegate) : delegate_(std::move(delegate)), identity_manager_(identity_manager), - client_(client), - registered_with_identity_manager_(false), - registered_with_content_settings_(false), - is_reconcile_started_(false), - first_execution_(true), - error_during_last_reconcile_(GoogleServiceAuthError::AuthErrorNone()), - reconcile_is_noop_(true), - set_accounts_in_progress_(false), - log_out_in_progress_(false), - chrome_accounts_changed_(false), - account_reconcilor_lock_count_(0), - reconcile_on_unblock_(false), - timer_(new base::OneShotTimer), - state_(signin_metrics::ACCOUNT_RECONCILOR_OK) { + client_(client) { VLOG(1) << "AccountReconcilor::AccountReconcilor"; DCHECK(delegate_); delegate_->set_reconcilor(this); @@ -253,14 +262,14 @@ void AccountReconcilor::Initialize(bool start_reconcile_if_tokens_available) { // Start a reconcile if the tokens are already loaded. if (start_reconcile_if_tokens_available && IsIdentityManagerReady()) - StartReconcile(); + StartReconcile(Trigger::kInitialized); } } void AccountReconcilor::EnableReconcile() { RegisterWithAllDependencies(); if (IsIdentityManagerReady()) - StartReconcile(); + StartReconcile(Trigger::kEnableReconcile); else SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED); } @@ -360,7 +369,7 @@ void AccountReconcilor::OnContentSettingChanged( } VLOG(1) << "AccountReconcilor::OnContentSettingChanged"; - StartReconcile(); + StartReconcile(Trigger::kCookieSettingChange); } void AccountReconcilor::OnEndBatchOfRefreshTokenStateChanges() { @@ -368,11 +377,11 @@ void AccountReconcilor::OnEndBatchOfRefreshTokenStateChanges() { << "Reconcilor state: " << is_reconcile_started_; // Remember that accounts have changed if a reconcile is already started. chrome_accounts_changed_ = is_reconcile_started_; - StartReconcile(); + StartReconcile(Trigger::kTokenChange); } void AccountReconcilor::OnRefreshTokensLoaded() { - StartReconcile(); + StartReconcile(Trigger::kTokensLoaded); } void AccountReconcilor::OnErrorStateOfRefreshTokenUpdatedForAccount( @@ -389,19 +398,8 @@ void AccountReconcilor::OnErrorStateOfRefreshTokenUpdatedForAccount( identity_manager_->GetAccountsCookieMutator()->TriggerCookieJarUpdate(); } -void AccountReconcilor::PerformMergeAction(const CoreAccountId& account_id) { - DCHECK(!IsMultiloginEndpointEnabled()); - reconcile_is_noop_ = false; - VLOG(1) << "AccountReconcilor::PerformMergeAction: " << account_id; - identity_manager_->GetAccountsCookieMutator()->AddAccountToCookie( - account_id, delegate_->GetGaiaApiSource(), - base::BindOnce(&AccountReconcilor::OnAddAccountToCookieCompleted, - weak_factory_.GetWeakPtr())); -} - void AccountReconcilor::PerformSetCookiesAction( const signin::MultiloginParameters& parameters) { - DCHECK(IsMultiloginEndpointEnabled()); reconcile_is_noop_ = false; VLOG(1) << "AccountReconcilor::PerformSetCookiesAction: " << base::JoinString(ToStringList(parameters.accounts_to_send), " "); @@ -424,7 +422,7 @@ void AccountReconcilor::PerformLogoutAllAccountsAction() { weak_factory_.GetWeakPtr())); } -void AccountReconcilor::StartReconcile() { +void AccountReconcilor::StartReconcile(Trigger trigger) { if (WasShutDown()) return; @@ -459,11 +457,11 @@ void AccountReconcilor::StartReconcile() { // Begin reconciliation. Reset initial states. SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING); - add_to_cookie_.clear(); reconcile_start_time_ = base::Time::Now(); is_reconcile_started_ = true; error_during_last_reconcile_ = GoogleServiceAuthError::AuthErrorNone(); reconcile_is_noop_ = true; + trigger_ = trigger; if (!timeout_.is_max()) { timer_->Start(FROM_HERE, timeout_, @@ -499,7 +497,6 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( const CoreAccountId& primary_account, const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts) { - DCHECK(IsMultiloginEndpointEnabled()); DCHECK(!set_accounts_in_progress_); DCHECK(!log_out_in_progress_); DCHECK_EQ(AccountReconcilorState::ACCOUNT_RECONCILOR_RUNNING, state_); @@ -533,21 +530,37 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( primary_has_error); } if (CookieNeedsUpdate(parameters_for_multilogin, gaia_accounts)) { - if (parameters_for_multilogin == kLogoutParameters) { - // UPDATE mode does not support empty list of accounts, call logout - // instead. - log_out_in_progress_ = true; - PerformLogoutAllAccountsAction(); + // Verify the account reconcilor is not trapped into a loop of repeating the + // same request with the same params. + if (throttler_.TryMultiloginOperation(parameters_for_multilogin)) { + if (parameters_for_multilogin == kLogoutParameters) { + RecordReconcileOperation(trigger_, Operation::kLogout); + // UPDATE mode does not support empty list of accounts, call logout + // instead. + log_out_in_progress_ = true; + PerformLogoutAllAccountsAction(); + } else { + // Reconcilor has to do some calls to gaia. is_reconcile_started_ is + // true and any StartReconcile() calls that are made in the meantime + // will be aborted until OnSetAccountsInCookieCompleted is called and + // is_reconcile_started_ is set to false. + RecordReconcileOperation(trigger_, Operation::kMultilogin); + set_accounts_in_progress_ = true; + PerformSetCookiesAction(parameters_for_multilogin); + } } else { - // Reconcilor has to do some calls to gaia. is_reconcile_started_ is true - // and any StartReconcile() calls that are made in the meantime will be - // aborted until OnSetAccountsInCookieCompleted is called and - // is_reconcile_started_ is set to false. - set_accounts_in_progress_ = true; - PerformSetCookiesAction(parameters_for_multilogin); + // Too many requests with the same parameters led to a backoff time + // required between successive identical requests that has not yet passed. + error_during_last_reconcile_ = + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); + CalculateIfMultiloginReconcileIsDone(); + ScheduleStartReconcileIfChromeAccountsChanged(); + RecordReconcileOperation(trigger_, Operation::kThrottled); } } else { // Nothing to do, accounts already match. + RecordReconcileOperation(trigger_, Operation::kNoop); + throttler_.Reset(); error_during_last_reconcile_ = GoogleServiceAuthError::AuthErrorNone(); CalculateIfMultiloginReconcileIsDone(); @@ -585,16 +598,13 @@ void AccountReconcilor::OnAccountsInCookieUpdated( // let it complete. Adding accounts to the cookie will trigger new // notifications anyway, and these will be handled in a new reconciliation // cycle. See https://crbug.com/923716 - if (IsMultiloginEndpointEnabled()) { - if (set_accounts_in_progress_) - return; - } else { - if (!add_to_cookie_.empty()) - return; - } + // + // TODO(droger): Should we also check if |logout_in_progress_|? + if (set_accounts_in_progress_) + return; if (!is_reconcile_started_) { - StartReconcile(); + StartReconcile(Trigger::kCookieChange); return; } @@ -652,13 +662,8 @@ void AccountReconcilor::OnAccountsInCookieUpdated( return; } - if (IsMultiloginEndpointEnabled()) { - FinishReconcileWithMultiloginEndpoint(primary_account, chrome_accounts, - std::move(verified_gaia_accounts)); - } else { - FinishReconcile(primary_account, chrome_accounts, - std::move(verified_gaia_accounts)); - } + FinishReconcileWithMultiloginEndpoint(primary_account, chrome_accounts, + std::move(verified_gaia_accounts)); } void AccountReconcilor::OnAccountsCookieDeletedByUserAction() { @@ -726,149 +731,15 @@ void AccountReconcilor::OnReceivedManageAccountsResponse( } } -void AccountReconcilor::FinishReconcile( - const CoreAccountId& primary_account, - const std::vector<CoreAccountId>& chrome_accounts, - std::vector<gaia::ListedAccount>&& gaia_accounts) { - DCHECK(!IsMultiloginEndpointEnabled()); - VLOG(1) << "AccountReconcilor::FinishReconcile"; - DCHECK(add_to_cookie_.empty()); - DCHECK(delegate_->IsUnknownInvalidAccountInCookieAllowed()) - << "Only supported in UPDATE mode"; - - size_t number_gaia_accounts = gaia_accounts.size(); - // If there are any accounts in the gaia cookie but not in chrome, then - // those accounts need to be removed from the cookie. This means we need - // to blow the cookie away. - int removed_from_cookie = 0; - for (size_t i = 0; i < number_gaia_accounts; ++i) { - if (gaia_accounts[i].valid && - !base::Contains(chrome_accounts, gaia_accounts[i].id)) { - ++removed_from_cookie; - } - } - - CoreAccountId first_account = delegate_->GetFirstGaiaAccountForReconcile( - chrome_accounts, gaia_accounts, primary_account, first_execution_, - removed_from_cookie > 0); - bool first_account_mismatch = - (number_gaia_accounts > 0) && (first_account != gaia_accounts[0].id); - - bool rebuild_cookie = first_account_mismatch || (removed_from_cookie > 0); - std::vector<gaia::ListedAccount> original_gaia_accounts = gaia_accounts; - if (rebuild_cookie) { - VLOG(1) << "AccountReconcilor::FinishReconcile: rebuild cookie"; - // Really messed up state. Blow away the gaia cookie completely and - // rebuild it, making sure the primary account as specified by the - // IdentityManager is the first session in the gaia cookie. - log_out_in_progress_ = true; - PerformLogoutAllAccountsAction(); - gaia_accounts.clear(); - } - - if (first_account.empty()) { - DCHECK(!delegate_->ShouldAbortReconcileIfPrimaryHasError()); - auto revoke_option = - delegate_->ShouldRevokeTokensIfNoPrimaryAccount() - ? AccountReconcilorDelegate::RevokeTokenOption::kRevoke - : AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke; - reconcile_is_noop_ = !RevokeAllSecondaryTokens( - identity_manager_, revoke_option, primary_account, - signin_metrics::SourceForRefreshTokenOperation:: - kAccountReconcilor_Reconcile); - } else { - // Create a list of accounts that need to be added to the Gaia cookie. - if (base::Contains(chrome_accounts, first_account)) { - add_to_cookie_.push_back(first_account); - } else { - // If the first account is not empty and not in chrome_accounts, it is - // impossible to rebuild it. It must be already the current default - // account, and no logout can happen. - DCHECK_EQ(gaia_accounts[0].id, first_account); - DCHECK(!rebuild_cookie); - } - for (size_t i = 0; i < chrome_accounts.size(); ++i) { - if (chrome_accounts[i] != first_account) - add_to_cookie_.push_back(chrome_accounts[i]); - } - } - - // For each account known to chrome, PerformMergeAction() if the account is - // not already in the cookie jar or its state is invalid, or signal merge - // completed otherwise. Make a copy of |add_to_cookie_| since calls - // to OnAddAccountToCookieCompleted() will change the array. - std::vector<CoreAccountId> add_to_cookie_copy = add_to_cookie_; - int added_to_cookie = 0; - for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) { - if (ContainsGaiaAccount(gaia_accounts, add_to_cookie_copy[i])) { - OnAddAccountToCookieCompleted(add_to_cookie_copy[i], - GoogleServiceAuthError::AuthErrorNone()); - } else { - PerformMergeAction(add_to_cookie_copy[i]); - if (!ContainsGaiaAccount(original_gaia_accounts, add_to_cookie_copy[i])) { - added_to_cookie++; - } - } - } - - signin_metrics::LogSigninAccountReconciliation( - chrome_accounts.size(), added_to_cookie, removed_from_cookie, - !first_account_mismatch, first_execution_, number_gaia_accounts); - first_execution_ = false; - CalculateIfMergeSessionReconcileIsDone(); - if (!is_reconcile_started_) - delegate_->OnReconcileFinished(first_account); - ScheduleStartReconcileIfChromeAccountsChanged(); -} - void AccountReconcilor::AbortReconcile() { VLOG(1) << "AccountReconcilor::AbortReconcile: try again later"; log_out_in_progress_ = false; - if (IsMultiloginEndpointEnabled()) { - set_accounts_in_progress_ = false; - CalculateIfMultiloginReconcileIsDone(); - } else { - add_to_cookie_.clear(); - CalculateIfMergeSessionReconcileIsDone(); - } - + set_accounts_in_progress_ = false; + CalculateIfMultiloginReconcileIsDone(); DCHECK(!is_reconcile_started_); DCHECK(!timer_->IsRunning()); } -void AccountReconcilor::CalculateIfMergeSessionReconcileIsDone() { - DCHECK(!IsMultiloginEndpointEnabled()); - base::TimeDelta duration = base::Time::Now() - reconcile_start_time_; - // Record the duration if reconciliation was underway and now it is over. - if (is_reconcile_started_ && add_to_cookie_.empty() && - !log_out_in_progress_) { - bool was_last_reconcile_successful = - (error_during_last_reconcile_.state() == - GoogleServiceAuthError::State::NONE); - signin_metrics::LogSigninAccountReconciliationDuration( - duration, was_last_reconcile_successful); - - // Reconciliation has actually finished (and hence stop the timer), but it - // may have ended in some failures. Pass this information to the - // |delegate_|. - timer_->Stop(); - if (!was_last_reconcile_successful) { - // Note: This is the only call to |OnReconcileError| in this file. We MUST - // make sure that we do not call |OnReconcileError| multiple times in the - // same reconciliation batch. - // The enclosing if-condition |is_reconcile_started_ && - // add_to_cookie_.empty()| represents the halting condition for one batch - // of reconciliation. - delegate_->OnReconcileError(error_during_last_reconcile_); - } - } - - is_reconcile_started_ = !add_to_cookie_.empty() || log_out_in_progress_; - if (!is_reconcile_started_) - VLOG(1) - << "AccountReconcilor::CalculateIfMergeSessionReconcileIsDone: done"; -} - void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { if (is_reconcile_started_) return; @@ -883,7 +754,8 @@ void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_SCHEDULED); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&AccountReconcilor::StartReconcile, - base::Unretained(this))); + base::Unretained(this), + Trigger::kTokenChangeDuringReconcile)); } else if (error_during_last_reconcile_.state() == GoogleServiceAuthError::NONE) { SetState(AccountReconcilorState::ACCOUNT_RECONCILOR_OK); @@ -892,25 +764,12 @@ void AccountReconcilor::ScheduleStartReconcileIfChromeAccountsChanged() { } } -// Remove the account from the list that is being merged. -bool AccountReconcilor::MarkAccountAsAddedToCookie( - const CoreAccountId& account_id) { - for (auto i = add_to_cookie_.begin(); i != add_to_cookie_.end(); ++i) { - if (account_id == *i) { - add_to_cookie_.erase(i); - return true; - } - } - return false; -} - bool AccountReconcilor::IsIdentityManagerReady() { return identity_manager_->AreRefreshTokensLoaded(); } void AccountReconcilor::OnSetAccountsInCookieCompleted( signin::SetAccountsInCookieResult result) { - DCHECK(IsMultiloginEndpointEnabled()); VLOG(1) << "AccountReconcilor::OnSetAccountsInCookieCompleted: " << "Error was " << static_cast<int>(result); @@ -936,7 +795,6 @@ void AccountReconcilor::OnSetAccountsInCookieCompleted( } void AccountReconcilor::CalculateIfMultiloginReconcileIsDone() { - DCHECK(IsMultiloginEndpointEnabled()); DCHECK(!set_accounts_in_progress_); DCHECK(!log_out_in_progress_); VLOG(1) << "AccountReconcilor::CalculateIfMultiloginReconcileIsDone: " @@ -958,27 +816,6 @@ void AccountReconcilor::CalculateIfMultiloginReconcileIsDone() { duration, was_last_reconcile_successful); } -void AccountReconcilor::OnAddAccountToCookieCompleted( - const CoreAccountId& account_id, - const GoogleServiceAuthError& error) { - DCHECK(!IsMultiloginEndpointEnabled()); - VLOG(1) << "AccountReconcilor::OnAddAccountToCookieCompleted: " - << "Account added: " << account_id << ", " - << "Error was " << error.ToString(); - // Always listens to GaiaCookieManagerService. Only proceed if reconciling. - if (is_reconcile_started_ && MarkAccountAsAddedToCookie(account_id)) { - // We may have seen a series of errors during reconciliation. Delegates may - // rely on the severity of the last seen error (see |OnReconcileError|) and - // hence do not override a persistent error, if we have seen one. - if (error.state() != GoogleServiceAuthError::State::NONE && - !error_during_last_reconcile_.IsPersistentError()) { - error_during_last_reconcile_ = error; - } - CalculateIfMergeSessionReconcileIsDone(); - ScheduleStartReconcileIfChromeAccountsChanged(); - } -} - void AccountReconcilor::OnLogOutFromCookieCompleted( const GoogleServiceAuthError& error) { VLOG(1) << "AccountReconcilor::OnLogOutFromCookieCompleted: " @@ -995,13 +832,8 @@ void AccountReconcilor::OnLogOutFromCookieCompleted( !error_during_last_reconcile_.IsPersistentError()) { error_during_last_reconcile_ = error; } - if (IsMultiloginEndpointEnabled()) { - CalculateIfMultiloginReconcileIsDone(); - ScheduleStartReconcileIfChromeAccountsChanged(); - } else { - CalculateIfMergeSessionReconcileIsDone(); - ScheduleStartReconcileIfChromeAccountsChanged(); - } + CalculateIfMultiloginReconcileIsDone(); + ScheduleStartReconcileIfChromeAccountsChanged(); } } @@ -1043,7 +875,7 @@ void AccountReconcilor::UnblockReconcile() { observer.OnUnblockReconcile(); if (reconcile_on_unblock_) { reconcile_on_unblock_ = false; - StartReconcile(); + StartReconcile(Trigger::kUnblockReconcile); } } @@ -1064,15 +896,11 @@ void AccountReconcilor::HandleReconcileTimeout() { // Will stop reconciliation and inform |delegate_| about // |error_during_last_reconcile_|, through - // |CalculateIfMergeSessionReconcileIsDone|. + // |CalculateIfReconcileIsDone|. AbortReconcile(); DCHECK(!timer_->IsRunning()); } -bool AccountReconcilor::IsMultiloginEndpointEnabled() const { - return delegate_->IsMultiloginEndpointEnabled(); -} - bool AccountReconcilor::CookieNeedsUpdate( const signin::MultiloginParameters& parameters, const std::vector<gaia::ListedAccount>& existing_accounts) { @@ -1118,3 +946,24 @@ void AccountReconcilor::SetState(AccountReconcilorState state) { bool AccountReconcilor::WasShutDown() const { return was_shut_down_; } + +// static +void AccountReconcilor::RecordReconcileOperation(Trigger trigger, + Operation operation) { + // Using the histogram macro for histogram that may be recorded in a loop. + UMA_HISTOGRAM_ENUMERATION(kOperationHistogramName, operation); + switch (operation) { + case Operation::kNoop: + base::UmaHistogramEnumeration(kTriggerNoopHistogramName, trigger); + break; + case Operation::kLogout: + base::UmaHistogramEnumeration(kTriggerLogoutHistogramName, trigger); + break; + case Operation::kMultilogin: + base::UmaHistogramEnumeration(kTriggerMultiloginHistogramName, trigger); + break; + case Operation::kThrottled: + UMA_HISTOGRAM_ENUMERATION(kTriggerThrottledHistogramName, trigger); + break; + } +} diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h index f31ab8d62db..fe573eeb0bd 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.h +++ b/chromium/components/signin/core/browser/account_reconcilor.h @@ -5,10 +5,8 @@ #define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_H_ #include <memory> -#include <string> #include <vector> -#include "base/callback_forward.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/macros.h" @@ -22,6 +20,7 @@ #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" +#include "components/signin/core/browser/account_reconcilor_throttler.h" #include "components/signin/core/browser/signin_header_helper.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/base/signin_metrics.h" @@ -127,8 +126,6 @@ class AccountReconcilor : public KeyedService, bool IsReconcileBlocked() const; protected: - void OnAddAccountToCookieCompleted(const CoreAccountId& account_id, - const GoogleServiceAuthError& error); void OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult result); void OnLogOutFromCookieCompleted(const GoogleServiceAuthError& error); @@ -136,6 +133,8 @@ class AccountReconcilor : public KeyedService, friend class AccountReconcilorTest; friend class DiceBrowserTest; friend class BaseAccountReconcilorTestTable; + friend class AccountReconcilorThrottlerTest; + friend class AccountReconcilorTestForceDiceMigration; FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestForceDiceMigration, TableRowTestCheckNoOp); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, @@ -151,21 +150,18 @@ class AccountReconcilor : public KeyedService, FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestMiceMultilogin, TableRowTest); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMiceTest, AccountReconcilorStateScheduled); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DiceTokenServiceRegistration); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DiceReconcileWithoutSignin); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, - DiceReconcileNoop); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DiceReconcileNoop); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DiceLastKnownFirstAccount); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, - UnverifiedAccountNoop); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, - UnverifiedAccountMerge); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, UnverifiedAccountNoop); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, UnverifiedAccountMerge); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, HandleSigninDuringReconcile); - FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest, + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceTest, DiceReconcileReuseGaiaFirstAccount); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceDeleteCookie); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, TokensNotLoaded); @@ -229,6 +225,39 @@ class AccountReconcilor : public KeyedService, TableRowTestMultilogin); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, ReconcileAfterShutdown); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, UnlockAfterShutdown); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorThrottlerTest, RefillOneRequest); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorThrottlerTest, RefillFiveRequests); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorThrottlerTest, + NewRequestParamsPasses); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorThrottlerTest, BlockFiveRequests); + + // Operation executed by the reconcilor. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class Operation { + kNoop = 0, + kLogout = 1, + kMultilogin = 2, + kThrottled = 3, + + kMaxValue = kThrottled + }; + + // Event triggering a call to StartReconcile(). + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class Trigger { + kInitialized = 0, + kTokensLoaded = 1, + kEnableReconcile = 2, + kUnblockReconcile = 3, + kTokenChange = 4, + kTokenChangeDuringReconcile = 5, + kCookieChange = 6, + kCookieSettingChange = 7, + + kMaxValue = kCookieSettingChange + }; void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer); @@ -246,27 +275,22 @@ class AccountReconcilor : public KeyedService, // All actions with side effects, only doing meaningful work if account // consistency is enabled. Virtual so that they can be overridden in tests. - virtual void PerformMergeAction(const CoreAccountId& account_id); virtual void PerformLogoutAllAccountsAction(); virtual void PerformSetCookiesAction( const signin::MultiloginParameters& parameters); // Used during periodic reconciliation. - void StartReconcile(); + void StartReconcile(Trigger trigger); // |gaia_accounts| are the accounts in the Gaia cookie. void FinishReconcile(const CoreAccountId& primary_account, const std::vector<CoreAccountId>& chrome_accounts, std::vector<gaia::ListedAccount>&& gaia_accounts); void AbortReconcile(); - void CalculateIfMergeSessionReconcileIsDone(); void ScheduleStartReconcileIfChromeAccountsChanged(); // Returns the list of valid accounts from the TokenService. std::vector<CoreAccountId> LoadValidAccountsFromTokenService() const; - // Note internally that this |account_id| is added to the cookie jar. - bool MarkAccountAsAddedToCookie(const CoreAccountId& account_id); - // The reconcilor only starts when the token service is ready. bool IsIdentityManagerReady(); @@ -300,9 +324,6 @@ class AccountReconcilor : public KeyedService, void HandleReconcileTimeout(); - // Returns true is multilogin endpoint can be enabled. - bool IsMultiloginEndpointEnabled() const; - // Returns true if current array of existing accounts in cookie is different // from the desired one. If this returns false, the multilogin call would be a // no-op. @@ -316,7 +337,17 @@ class AccountReconcilor : public KeyedService, // Returns whether Shutdown() was called. bool WasShutDown() const; + static void RecordReconcileOperation(Trigger trigger, Operation operation); + + // Histogram names. + static const char kOperationHistogramName[]; + static const char kTriggerLogoutHistogramName[]; + static const char kTriggerMultiloginHistogramName[]; + static const char kTriggerNoopHistogramName[]; + static const char kTriggerThrottledHistogramName[]; + std::unique_ptr<signin::AccountReconcilorDelegate> delegate_; + AccountReconcilorThrottler throttler_; // The IdentityManager associated with this reconcilor. signin::IdentityManager* identity_manager_; @@ -324,16 +355,17 @@ class AccountReconcilor : public KeyedService, // The SigninClient associated with this reconcilor. SigninClient* client_; - bool registered_with_identity_manager_; - bool registered_with_content_settings_; + bool registered_with_identity_manager_ = false; + bool registered_with_content_settings_ = false; // True while the reconcilor is busy checking or managing the accounts in // this profile. - bool is_reconcile_started_; + bool is_reconcile_started_ = false; base::Time reconcile_start_time_; + Trigger trigger_ = Trigger::kInitialized; // True iff this is the first time the reconcilor is executing. - bool first_execution_; + bool first_execution_ = true; // 'Most severe' error encountered during the last attempt to reconcile. If // the last reconciliation attempt was successful, this will be @@ -343,23 +375,23 @@ class AccountReconcilor : public KeyedService, // error is considered more severe than all non-persistent errors, but // persistent (or non-persistent) errors do not have an internal severity // ordering among themselves. - GoogleServiceAuthError error_during_last_reconcile_; + GoogleServiceAuthError error_during_last_reconcile_ = + GoogleServiceAuthError::AuthErrorNone(); // Used for Dice migration: migration can happen if the accounts are // consistent, which is indicated by reconcile being a no-op. - bool reconcile_is_noop_; + bool reconcile_is_noop_ = true; // Used during reconcile action. - std::vector<CoreAccountId> add_to_cookie_; // Progress of AddAccount calls. - bool set_accounts_in_progress_; // Progress of SetAccounts calls. - bool log_out_in_progress_; // Progress of LogOut calls. - bool chrome_accounts_changed_; + bool set_accounts_in_progress_ = false; // Progress of SetAccounts calls. + bool log_out_in_progress_ = false; // Progress of LogOut calls. + bool chrome_accounts_changed_ = false; // Used for the Lock. // StartReconcile() is blocked while this is > 0. - int account_reconcilor_lock_count_; + int account_reconcilor_lock_count_ = 0; // StartReconcile() should be started when the reconcilor is unblocked. - bool reconcile_on_unblock_; + bool reconcile_on_unblock_ = false; base::ObserverList<Observer, true>::Unchecked observer_list_; @@ -371,14 +403,16 @@ class AccountReconcilor : public KeyedService, // of reconciliation completing within a finite time. It is technically // possible for account reconciliation to be running/waiting forever in cases // such as a network connection not being present. - std::unique_ptr<base::OneShotTimer> timer_; + std::unique_ptr<base::OneShotTimer> timer_ = + std::make_unique<base::OneShotTimer>(); base::TimeDelta timeout_; // Greater than 0 when synced data is being deleted, and it is important to // not invalidate the primary token while this is happening. int synced_data_deletion_in_progress_count_ = 0; - signin_metrics::AccountReconcilorState state_; + signin_metrics::AccountReconcilorState state_ = + signin_metrics::ACCOUNT_RECONCILOR_OK; // Set to true when Shutdown() is called. bool was_shut_down_ = false; diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc index a16384a0d34..4c64dbd9875 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.cc @@ -17,10 +17,6 @@ bool AccountReconcilorDelegate::IsReconcileEnabled() const { return false; } -bool AccountReconcilorDelegate::IsMultiloginEndpointEnabled() const { - return true; -} - gaia::GaiaSource AccountReconcilorDelegate::GetGaiaApiSource() const { NOTREACHED() << "Reconcile is not enabled, no Gaia API calls should be made."; return gaia::GaiaSource::kChrome; diff --git a/chromium/components/signin/core/browser/account_reconcilor_delegate.h b/chromium/components/signin/core/browser/account_reconcilor_delegate.h index 7a6ecd71d66..7e6e287af9e 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/account_reconcilor_delegate.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_DELEGATE_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_DELEGATE_H_ -#include <string> #include <vector> #include "base/time/time.h" @@ -40,11 +39,6 @@ class AccountReconcilorDelegate { // false. virtual bool IsReconcileEnabled() const; - // Returns whether the OAuth multilogin endpoint can be used to build the Gaia - // cookies. - // Default implementation returns true. - virtual bool IsMultiloginEndpointEnabled() const; - // Returns the value to set in the "source" parameter for Gaia API calls. virtual gaia::GaiaSource GetGaiaApiSource() const; diff --git a/chromium/components/signin/core/browser/account_reconcilor_throttler.cc b/chromium/components/signin/core/browser/account_reconcilor_throttler.cc new file mode 100644 index 00000000000..1742ff043c7 --- /dev/null +++ b/chromium/components/signin/core/browser/account_reconcilor_throttler.cc @@ -0,0 +1,113 @@ +// Copyright 2021 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/account_reconcilor_throttler.h" + +#include "base/metrics/histogram_functions.h" + +namespace { +MultiloginRequestType GetMultiloginRequestType( + const signin::MultiloginParameters& params) { + if (params.mode == + gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER) { + return MultiloginRequestType::kPreserveCookieAccountsOrder; + } + + // Update mode. + DCHECK_EQ(params.mode, + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER); + if (params.accounts_to_send.size()) + return MultiloginRequestType::kUpdateCookieAccountsOrder; + + // Accounts to send is empty. + return MultiloginRequestType::kLogoutAllAccounts; +} +} // namespace + +// static +constexpr float AccountReconcilorThrottler::kMaxAllowedRequestsPerBucket; +// static +constexpr float AccountReconcilorThrottler::kRefillRequestsBucketRatePerMinute; + +AccountReconcilorThrottler::AccountReconcilorThrottler() { + Reset(); +} + +AccountReconcilorThrottler::~AccountReconcilorThrottler() { + RecordAndResetNumberOfRejectedRequests(); +} + +void AccountReconcilorThrottler::Reset() { + // Needed for the case when the reconcilor is a no op and calls reset. + RecordAndResetNumberOfRejectedRequests(); + last_request_params_ = absl::nullopt; + available_requests_bucket_ = kMaxAllowedRequestsPerBucket; + last_refill_time_stamp_ = base::TimeTicks::Now(); +} + +bool AccountReconcilorThrottler::IsDifferentRequest( + const signin::MultiloginParameters& params) const { + return !last_request_params_.has_value() || + params != last_request_params_.value(); +} + +bool AccountReconcilorThrottler::TryMultiloginOperation( + const signin::MultiloginParameters& params) { + if (IsDifferentRequest(params)) + Reset(); + + RefillAllowedRequests(); + if (available_requests_bucket_ < 1.0) { + ++consecutive_rejected_requests_; + return false; + } + + RecordMultiloginOperation(params); + RecordAndResetNumberOfRejectedRequests(); + return true; +} + +void AccountReconcilorThrottler::RecordMultiloginOperation( + const signin::MultiloginParameters& params) { + DCHECK_GE(available_requests_bucket_, 1.0f); + last_request_params_ = params; + available_requests_bucket_ -= 1.0f; +} + +void AccountReconcilorThrottler::RefillAllowedRequests() { + float refill_requests = + (base::TimeTicks::Now() - last_refill_time_stamp_).InSecondsF() / 60.0 * + kRefillRequestsBucketRatePerMinute; + + available_requests_bucket_ = + std::min(available_requests_bucket_ + refill_requests, + kMaxAllowedRequestsPerBucket); + last_refill_time_stamp_ = base::TimeTicks::Now(); +} + +void AccountReconcilorThrottler::RecordAndResetNumberOfRejectedRequests() { + if (!consecutive_rejected_requests_) + return; + + DCHECK(last_request_params_.has_value()); + switch (GetMultiloginRequestType(last_request_params_.value())) { + case MultiloginRequestType::kPreserveCookieAccountsOrder: + base::UmaHistogramCounts1000( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Preserve", + consecutive_rejected_requests_); + break; + case MultiloginRequestType::kUpdateCookieAccountsOrder: + base::UmaHistogramCounts1000( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", + consecutive_rejected_requests_); + break; + case MultiloginRequestType::kLogoutAllAccounts: + base::UmaHistogramCounts1000( + "Signin.Reconciler.RejectedRequestsDueToThrottler.LogoutAll", + consecutive_rejected_requests_); + break; + } + + consecutive_rejected_requests_ = 0; +} diff --git a/chromium/components/signin/core/browser/account_reconcilor_throttler.h b/chromium/components/signin/core/browser/account_reconcilor_throttler.h new file mode 100644 index 00000000000..e4e2e02c2e4 --- /dev/null +++ b/chromium/components/signin/core/browser/account_reconcilor_throttler.h @@ -0,0 +1,61 @@ +// Copyright 2021 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_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_THROTTLER_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_THROTTLER_H_ + +#include "base/time/time.h" +#include "components/signin/public/base/multilogin_parameters.h" + +// Used for UMA logging, do not remove or reorder values. +enum class MultiloginRequestType { + kPreserveCookieAccountsOrder = 0, + kUpdateCookieAccountsOrder = 1, + kLogoutAllAccounts = 2, + kMaxValue = kLogoutAllAccounts, +}; + +// Helper class to avoid the account reconcilor from getting into a loop, +// repeating the same request and generating a spike in the number of requests. +class AccountReconcilorThrottler { + public: + AccountReconcilorThrottler(); + ~AccountReconcilorThrottler(); + + AccountReconcilorThrottler(const AccountReconcilorThrottler&) = delete; + AccountReconcilorThrottler& operator=(const AccountReconcilorThrottler&) = + delete; + + // The multilogin operation should be sent or blocked based on the result of + // this function. It returns true if not all the |available_requests_ >= 1.0| + // has been consumed, and consumes one of the available requests. + bool TryMultiloginOperation(const signin::MultiloginParameters& params); + + // Any request passed to |IsMultiloginOperationAllowed()| after |Reset()| is + // called is considered as a new different request. + // |available_requests_bucket_| is reset to its max allowance. + void Reset(); + + // Max bucket size. The throttler tolerates up to 30 successive identical + // requests before throttling. + static constexpr float kMaxAllowedRequestsPerBucket = 30.0f; + + // Requests bucket is refilled with the rate of 0.5 per minute. If all the + // available requests have been consumed, the reconcilor will need to wait for + // 2 minutes from the last request to perform another identical request. + static constexpr float kRefillRequestsBucketRatePerMinute = 0.5f; + + private: + bool IsDifferentRequest(const signin::MultiloginParameters& params) const; + void RefillAllowedRequests(); + void RecordMultiloginOperation(const signin::MultiloginParameters& params); + void RecordAndResetNumberOfRejectedRequests(); + + // Reset for every new request with different parameters. + float available_requests_bucket_; + base::TimeTicks last_refill_time_stamp_; + absl::optional<signin::MultiloginParameters> last_request_params_; + size_t consecutive_rejected_requests_ = 0; +}; + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_THROTTLER_H_ diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc index ac9d75d907d..4933f0b9bd4 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc @@ -175,7 +175,6 @@ class MockAccountReconcilor SigninClient* client, std::unique_ptr<signin::AccountReconcilorDelegate> delegate); - MOCK_METHOD1(PerformMergeAction, void(const CoreAccountId& account_id)); MOCK_METHOD0(PerformLogoutAllAccountsAction, void()); MOCK_METHOD1(PerformSetCookiesAction, void(const signin::MultiloginParameters& parameters)); @@ -252,10 +251,6 @@ class AccountReconcilorTest : public ::testing::Test { CoreAccountId PickAccountIdForAccount(const std::string& gaia_id, const std::string& username); - void SimulateAddAccountToCookieCompleted(AccountReconcilor* reconcilor, - const CoreAccountId& account_id, - const GoogleServiceAuthError& error); - void SimulateSetAccountsInCookieCompleted( AccountReconcilor* reconcilor, signin::SetAccountsInCookieResult result); @@ -379,13 +374,6 @@ CoreAccountId AccountReconcilorTest::PickAccountIdForAccount( gaia_id, username); } -void AccountReconcilorTest::SimulateAddAccountToCookieCompleted( - AccountReconcilor* reconcilor, - const CoreAccountId& account_id, - const GoogleServiceAuthError& error) { - reconcilor->OnAddAccountToCookieCompleted(account_id, error); -} - void AccountReconcilorTest::SimulateSetAccountsInCookieCompleted( AccountReconcilor* reconcilor, signin::SetAccountsInCookieResult result) { @@ -437,9 +425,6 @@ struct AccountReconcilorTestTableParam { const char* gaia_api_calls; const char* tokens_after_reconcile; const char* cookies_after_reconcile; - const char* gaia_api_calls_multilogin; - const char* tokens_after_reconcile_multilogin; - const char* cookies_after_reconcile_multilogin; // Int represents AccountReconcilorDelegate::InconsistencyReason. const int inconsistency_reason; }; @@ -480,26 +465,7 @@ void PrintTo(const AccountReconcilorTestTableParam& param, ::std::ostream* os) { class BaseAccountReconcilorTestTable : public AccountReconcilorTest { protected: - BaseAccountReconcilorTestTable(const AccountReconcilorTestTableParam& param) - : BaseAccountReconcilorTestTable(param.tokens, - param.cookies, - param.is_first_reconcile, - param.gaia_api_calls, - param.tokens_after_reconcile, - param.cookies_after_reconcile) {} - - BaseAccountReconcilorTestTable(const char* tokens, - const char* cookies, - IsFirstReconcile is_first_reconcile, - const char* gaia_api_calls, - const char* tokens_after_reconcile, - const char* cookies_after_reconcile) - : tokens_(tokens), - cookies_(cookies), - is_first_reconcile_(is_first_reconcile), - gaia_api_calls_(gaia_api_calls), - tokens_after_reconcile_(tokens_after_reconcile), - cookies_after_reconcile_(cookies_after_reconcile) { + BaseAccountReconcilorTestTable() { accounts_['A'] = {"a@gmail.com", signin::GetTestGaiaIdForEmail("a@gmail.com")}; accounts_['B'] = {"b@gmail.com", @@ -634,111 +600,11 @@ class BaseAccountReconcilorTestTable : public AccountReconcilorTest { identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false); } -#if BUILDFLAG(ENABLE_DICE_SUPPORT) - void RunReconcile() { - // Setup cookies. - std::vector<Cookie> cookies = ParseCookieString(cookies_); - ConfigureCookieManagerService(cookies); - - // Call list accounts now so that the next call completes synchronously. - identity_test_env()->identity_manager()->GetAccountsInCookieJar(); - base::RunLoop().RunUntilIdle(); - - // Setup tokens. This triggers listing cookies so we need to setup cookies - // before that. - SetupTokens(tokens_); - - // Setup expectations. - testing::InSequence mock_sequence; - bool logout_action = false; - for (int i = 0; gaia_api_calls_[i] != '\0'; ++i) { - if (gaia_api_calls_[i] == 'X') { - logout_action = true; - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(1); - cookies.clear(); - continue; - } - std::string cookie(1, gaia_api_calls_[i]); - CoreAccountId account_id_for_cookie = PickAccountIdForAccount( - accounts_[cookie[0]].gaia_id, accounts_[cookie[0]].email); - EXPECT_CALL(*GetMockReconcilor(), - PerformMergeAction(account_id_for_cookie)) - .Times(1); - // MergeSession fixes an existing cookie or appends it at the end. - auto it = - std::find(cookies.begin(), cookies.end(), - Cookie{accounts_[cookie[0]].gaia_id, false /* is_valid */}); - if (it == cookies.end()) - cookies.push_back({accounts_[cookie[0]].gaia_id, true}); - else - it->is_valid = true; - } - if (!logout_action) { - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(0); - } - - // Check the expected cookies after reconcile. - std::vector<Cookie> expected_cookies = - ParseCookieString(cookies_after_reconcile_); - ASSERT_EQ(expected_cookies, cookies); - - // Reconcile. - AccountReconcilor* reconcilor = GetMockReconcilor(); - ASSERT_TRUE(reconcilor->first_execution_); - reconcilor->first_execution_ = - is_first_reconcile_ == IsFirstReconcile::kFirst; - reconcilor->StartReconcile(); - for (int i = 0; gaia_api_calls_[i] != '\0'; ++i) { - if (gaia_api_calls_[i] == 'X') { - SimulateLogOutFromCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); - continue; - } - CoreAccountId account_id = - PickAccountIdForAccount(accounts_[gaia_api_calls_[i]].gaia_id, - accounts_[gaia_api_calls_[i]].email); - SimulateAddAccountToCookieCompleted( - reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); - } - ASSERT_FALSE(reconcilor->is_reconcile_started_); - - if (tokens_ == tokens_after_reconcile_) { - EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); - } else { - // If the tokens were changed by the reconcile, a new reconcile should be - // scheduled. - EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, - reconcilor->GetState()); - } - - VerifyCurrentTokens(ParseTokenString(tokens_after_reconcile_)); - - testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); - - // Another reconcile is sometimes triggered if Chrome accounts have changed. - // Allow it to finish. - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)) - .WillRepeatedly(testing::Return()); - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .WillRepeatedly(testing::Return()); - ConfigureCookieManagerService({}); - base::RunLoop().RunUntilIdle(); - } -#endif // BUILDFLAG(ENABLE_DICE_SUPPORT) - std::string GaiaIdForAccountKey(char account_key) { return accounts_[account_key].gaia_id; } std::map<char, Account> accounts_; - const char* tokens_; - const char* cookies_; - IsFirstReconcile is_first_reconcile_; - const char* gaia_api_calls_; - const char* tokens_after_reconcile_; - const char* cookies_after_reconcile_; }; // Parameterized version of AccountReconcilorTest. @@ -746,35 +612,24 @@ class AccountReconcilorTestTable : public BaseAccountReconcilorTestTable, public ::testing::WithParamInterface<AccountReconcilorTestTableParam> { protected: - AccountReconcilorTestTable() : BaseAccountReconcilorTestTable(GetParam()) {} + AccountReconcilorTestTable() = default; // Checks that reconcile is idempotent. void CheckReconcileIdempotent( const std::vector<AccountReconcilorTestTableParam>& params, - const AccountReconcilorTestTableParam& param, - bool multilogin) { + const AccountReconcilorTestTableParam& param) { // Simulate another reconcile based on the results of this one: find the // corresponding row in the table and check that it does nothing. for (const AccountReconcilorTestTableParam& row : params) { if (row.is_first_reconcile == IsFirstReconcile::kFirst) continue; - if (!((strcmp(row.tokens, param.tokens_after_reconcile) == 0 && - strcmp(row.cookies, param.cookies_after_reconcile) == 0 && - !multilogin) || - (strcmp(row.tokens, param.tokens_after_reconcile_multilogin) == 0 && - strcmp(row.cookies, param.cookies_after_reconcile_multilogin) == - 0 && - multilogin))) { + if (!(strcmp(row.tokens, param.tokens_after_reconcile) == 0 && + strcmp(row.cookies, param.cookies_after_reconcile) == 0)) { continue; } - if (multilogin) { - EXPECT_STREQ(row.tokens, row.tokens_after_reconcile_multilogin); - EXPECT_STREQ(row.cookies, row.cookies_after_reconcile_multilogin); - } else { - EXPECT_STREQ(row.tokens, row.tokens_after_reconcile); - EXPECT_STREQ(row.cookies, row.cookies_after_reconcile); - } + EXPECT_STREQ(row.tokens, row.tokens_after_reconcile); + EXPECT_STREQ(row.cookies, row.cookies_after_reconcile); return; } @@ -810,7 +665,8 @@ TEST_F(AccountReconcilorMirrorTest, Reauth) { auto* account_mutator = identity_test_env()->identity_manager()->GetPrimaryAccountMutator(); DCHECK(account_mutator); - account_mutator->SetPrimaryAccount(account_info.account_id); + account_mutator->SetPrimaryAccount(account_info.account_id, + signin::ConsentLevel::kSync); ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager()); } @@ -878,248 +734,285 @@ const std::vector<AccountReconcilorTestTableParam> kDiceParams = { // x: The next cookie is marked "invalid". // - First Run: true if this is the first reconcile (i.e. Chrome startup). // ------------------------------------------------------------------------- - // Tokens|Cookies|First Run|Gaia calls|Tokens aft.|Cookies aft.|M.calls| M.Tokens aft.| M.Cookies aft.| AccountReconcilorDelegate::InconsistencyReason | + // Tokens|Cookies|First Run|Gaia calls|Tokens aft.|Cookies aft.|AccountReconcilorDelegate::InconsistencyReason | // ------------------------------------------------------------------------- // First reconcile (Chrome restart): Rebuild the Gaia cookie to match the // tokens. Make the Sync account the default account in the Gaia cookie. // Sync enabled. - { "", "A", IsFirstReconcile::kBoth, "X", "", "", "U", "", "", 3}, - { "*AB", "AB", IsFirstReconcile::kBoth, "", "*AB", "AB", "", "*AB", "AB", 0}, - { "*A", "A", IsFirstReconcile::kBoth, "", "*A", "A", "", "*A" , "A", 0}, - { "*A", "", IsFirstReconcile::kBoth, "A", "*A", "A", "PA", "*A" , "A", 1}, - { "*A", "B", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A" , "A", 1}, - { "*A", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A" , "A", 5}, - { "*AB", "BA", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB", 7}, - { "*AB", "BA", IsFirstReconcile::kNotFirst, "", "*AB", "BA", "", "*AB", "BA", 0}, + { "", "A", IsFirstReconcile::kBoth, "U", "", "", 3}, + { "*AB", "AB", IsFirstReconcile::kBoth, "", "*AB", "AB", 0}, + { "*A", "A", IsFirstReconcile::kBoth, "", "*A" , "A", 0}, + { "*A", "", IsFirstReconcile::kBoth, "PA", "*A" , "A", 1}, + { "*A", "B", IsFirstReconcile::kBoth, "UA", "*A" , "A", 1}, + { "*A", "AB", IsFirstReconcile::kBoth, "UA", "*A" , "A", 5}, + { "*AB", "BA", IsFirstReconcile::kFirst, "UAB", "*AB", "AB", 7}, + { "*AB", "BA", IsFirstReconcile::kNotFirst,"", "*AB", "BA", 0}, - { "*AB", "A", IsFirstReconcile::kBoth, "B", "*AB", "AB", "PAB", "*AB", "AB", 4}, + { "*AB", "A", IsFirstReconcile::kBoth, "PAB", "*AB", "AB", 4}, - { "*AB", "B", IsFirstReconcile::kFirst, "XAB", "*AB", "AB", "UAB", "*AB", "AB", 1}, - { "*AB", "B", IsFirstReconcile::kNotFirst, "A", "*AB", "BA", "PBA", "*AB", "BA", 1}, + { "*AB", "B", IsFirstReconcile::kFirst, "UAB", "*AB", "AB", 1}, + { "*AB", "B", IsFirstReconcile::kNotFirst,"PBA", "*AB", "BA", 1}, - { "*AB", "", IsFirstReconcile::kBoth, "AB", "*AB", "AB", "PAB", "*AB", "AB", 1}, + { "*AB", "", IsFirstReconcile::kBoth, "PAB", "*AB", "AB", 1}, // Sync enabled, token error on primary. - { "*xAB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "" , "U", "*xA", "", 2}, - { "*xAB", "BA", IsFirstReconcile::kBoth, "XB", "*xAB", "B", "UB", "*xAB", "B", 2}, - { "*xAB", "A", IsFirstReconcile::kBoth, "X", "*xA", "" , "U", "*xA", "", 2}, - { "*xAB", "B", IsFirstReconcile::kBoth, "", "*xAB", "B", "", "*xAB", "B", 0}, - { "*xAB", "", IsFirstReconcile::kBoth, "B", "*xAB", "B", "PB", "*xAB", "B", 0}, + { "*xAB", "AB", IsFirstReconcile::kBoth, "U", "*xA", "", 2}, + { "*xAB", "BA", IsFirstReconcile::kBoth, "UB", "*xAB", "B", 2}, + { "*xAB", "A", IsFirstReconcile::kBoth, "U", "*xA", "", 2}, + { "*xAB", "B", IsFirstReconcile::kBoth, "", "*xAB", "B", 0}, + { "*xAB", "", IsFirstReconcile::kBoth, "PB", "*xAB", "B", 0}, // Sync enabled, token error on secondary. - { "*AxB", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A", "A", 5}, - { "*AxB", "A", IsFirstReconcile::kBoth, "", "*A", "A", "", "*A", "A", 0}, - { "*AxB", "", IsFirstReconcile::kBoth, "A", "*A", "A", "PA", "*A", "A", 1}, + { "*AxB", "AB", IsFirstReconcile::kBoth, "UA", "*A", "A", 5}, + { "*AxB", "A", IsFirstReconcile::kBoth, "", "*A", "A", 0}, + { "*AxB", "", IsFirstReconcile::kBoth, "PA", "*A", "A", 1}, // The first account in cookies is swapped even when Chrome is running. // The swap would happen at next startup anyway and doing it earlier avoids signing the user out. - { "*AxB", "BA", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A", "A", 5}, - { "*AxB", "B", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A", "A", 1}, + { "*AxB", "BA", IsFirstReconcile::kBoth, "UA", "*A", "A", 5}, + { "*AxB", "B", IsFirstReconcile::kBoth, "UA", "*A", "A", 1}, // Sync enabled, token error on both accounts. - { "*xAxB", "AB", IsFirstReconcile::kBoth, "X", "*xA", "", "U", "*xA", "", 2}, - { "*xAxB", "BA", IsFirstReconcile::kBoth, "X", "*xA", "", "U", "*xA", "", 2}, - { "*xAxB", "A", IsFirstReconcile::kBoth, "X", "*xA", "", "U", "*xA", "", 2}, - { "*xAxB", "B", IsFirstReconcile::kBoth, "X", "*xA", "", "U", "*xA", "", 5}, - { "*xAxB", "", IsFirstReconcile::kBoth, "", "*xA", "", "", "*xA", "", 0}, + { "*xAxB", "AB", IsFirstReconcile::kBoth, "U", "*xA", "", 2}, + { "*xAxB", "BA", IsFirstReconcile::kBoth, "U", "*xA", "", 2}, + { "*xAxB", "A", IsFirstReconcile::kBoth, "U", "*xA", "", 2}, + { "*xAxB", "B", IsFirstReconcile::kBoth, "U", "*xA", "", 5}, + { "*xAxB", "", IsFirstReconcile::kBoth, "", "*xA", "", 0}, // Sync disabled. - { "AB", "AB", IsFirstReconcile::kBoth, "", "AB", "AB", "", "AB", "AB", 0}, - { "AB", "BA", IsFirstReconcile::kBoth, "", "AB", "BA", "", "AB", "BA", 0}, - { "AB", "A", IsFirstReconcile::kBoth, "B", "AB", "AB", "PAB", "AB", "AB", 4}, - { "AB", "B", IsFirstReconcile::kBoth, "A", "AB", "BA", "PBA", "AB", "BA", 4}, - { "AB", "", IsFirstReconcile::kBoth, "AB", "AB", "AB", "PAB", "AB", "AB", 0}, + { "AB", "AB", IsFirstReconcile::kBoth, "", "AB", "AB", 0}, + { "AB", "BA", IsFirstReconcile::kBoth, "", "AB", "BA", 0}, + { "AB", "A", IsFirstReconcile::kBoth, "PAB", "AB", "AB", 4}, + { "AB", "B", IsFirstReconcile::kBoth, "PBA", "AB", "BA", 4}, + { "AB", "", IsFirstReconcile::kBoth, "PAB", "AB", "AB", 0}, // Sync disabled, token error on first account. - { "xAB", "AB", IsFirstReconcile::kFirst, "XB", "B", "B", "UB", "B", "B", 3}, - { "xAB", "AB", IsFirstReconcile::kNotFirst, "X", "", "" , "U", "", "", 3}, + { "xAB", "AB", IsFirstReconcile::kFirst, "UB", "B", "B", 3}, + { "xAB", "AB", IsFirstReconcile::kNotFirst, "U", "", "", 3}, - { "xAB", "BA", IsFirstReconcile::kBoth, "XB", "B", "B", "UB", "B", "B", 5}, + { "xAB", "BA", IsFirstReconcile::kBoth, "UB", "B", "B", 5}, - { "xAB", "A", IsFirstReconcile::kFirst, "XB", "B", "B", "UB", "B", "B", 3}, - { "xAB", "A", IsFirstReconcile::kNotFirst, "X", "", "" , "U", "", "", 3}, + { "xAB", "A", IsFirstReconcile::kFirst, "UB", "B", "B", 3}, + { "xAB", "A", IsFirstReconcile::kNotFirst, "U", "", "", 3}, - { "xAB", "B", IsFirstReconcile::kBoth, "", "B", "B", "", "B", "B", 0}, + { "xAB", "B", IsFirstReconcile::kBoth, "", "B", "B", 0}, - { "xAB", "", IsFirstReconcile::kBoth, "B", "B", "B", "PB", "B", "B", 0}, - // Sync disabled, token error on second account . - { "AxB", "AB", IsFirstReconcile::kBoth, "XA", "A", "A", "UA", "A", "A", 5}, + { "xAB", "", IsFirstReconcile::kBoth, "PB", "B", "B", 0}, + // Sync disabled, token error on second account + { "AxB", "AB", IsFirstReconcile::kBoth, "UA", "A", "A", 5}, - { "AxB", "BA", IsFirstReconcile::kFirst, "XA", "A", "A", "UA", "A", "A", 3}, - { "AxB", "BA", IsFirstReconcile::kNotFirst, "X", "", "" , "U", "", "", 3}, + { "AxB", "BA", IsFirstReconcile::kFirst, "UA", "A", "A", 3}, + { "AxB", "BA", IsFirstReconcile::kNotFirst, "U", "", "", 3}, - { "AxB", "A", IsFirstReconcile::kBoth, "", "A", "A", "", "A", "A", 0}, + { "AxB", "A", IsFirstReconcile::kBoth, "", "A", "A", 0}, - { "AxB", "B", IsFirstReconcile::kFirst, "XA", "A", "A", "UA", "A", "A", 3}, - { "AxB", "B", IsFirstReconcile::kNotFirst, "X", "", "" , "U", "", "", 3}, + { "AxB", "B", IsFirstReconcile::kFirst, "UA", "A", "A", 3}, + { "AxB", "B", IsFirstReconcile::kNotFirst, "U", "", "", 3}, - { "AxB", "", IsFirstReconcile::kBoth, "A", "A", "A", "PA", "A", "A", 0}, + { "AxB", "", IsFirstReconcile::kBoth, "PA", "A", "A", 0}, // Sync disabled, token error on both accounts. - { "xAxB", "AB", IsFirstReconcile::kBoth, "X", "", "", "U", "", "", 3}, - { "xAxB", "BA", IsFirstReconcile::kBoth, "X", "", "", "U", "", "", 3}, - { "xAxB", "A", IsFirstReconcile::kBoth, "X", "", "", "U", "", "", 3}, - { "xAxB", "B", IsFirstReconcile::kBoth, "X", "", "", "U", "", "", 3}, - { "xAxB", "", IsFirstReconcile::kBoth, "", "", "", "", "", "", 0}, + { "xAxB", "AB", IsFirstReconcile::kBoth, "U", "", "", 3}, + { "xAxB", "BA", IsFirstReconcile::kBoth, "U", "", "", 3}, + { "xAxB", "A", IsFirstReconcile::kBoth, "U", "", "", 3}, + { "xAxB", "B", IsFirstReconcile::kBoth, "U", "", "", 3}, + { "xAxB", "", IsFirstReconcile::kBoth, "", "", "", 0}, // Account marked as invalid in cookies. // No difference between cookies and tokens, do not do do anything. // Do not logout. Regression tests for http://crbug.com/854799 - { "", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA", 0}, - { "", "xAxB", IsFirstReconcile::kBoth, "", "", "xAxB", "", "", "xAxB", 0}, - { "xA", "xA", IsFirstReconcile::kBoth, "", "", "xA", "", "", "xA", 0}, - { "xAB", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB", 0}, - { "AxB", "AxC", IsFirstReconcile::kBoth, "", "A", "AxC", "", "A", "AxC", 0}, - { "B", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", "", "B", "xAB", 0}, - { "*xA", "xA", IsFirstReconcile::kBoth, "", "*xA", "xA", "", "*xA", "xA", 0}, - { "*xA", "xB", IsFirstReconcile::kBoth, "", "*xA", "xB", "", "*xA", "xB", 0}, - { "*xAB", "xAB", IsFirstReconcile::kBoth, "", "*xAB", "xAB", "", "*xAB", "xAB", 0}, - { "*AxB", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA", 0}, + { "", "xA", IsFirstReconcile::kBoth, "", "", "xA", 0}, + { "", "xAxB", IsFirstReconcile::kBoth, "", "", "xAxB", 0}, + { "xA", "xA", IsFirstReconcile::kBoth, "", "", "xA", 0}, + { "xAB", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", 0}, + { "AxB", "AxC", IsFirstReconcile::kBoth, "", "A", "AxC", 0}, + { "B", "xAB", IsFirstReconcile::kBoth, "", "B", "xAB", 0}, + { "*xA", "xA", IsFirstReconcile::kBoth, "", "*xA", "xA", 0}, + { "*xA", "xB", IsFirstReconcile::kBoth, "", "*xA", "xB", 0}, + { "*xAB", "xAB", IsFirstReconcile::kBoth, "", "*xAB", "xAB", 0}, + { "*AxB", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", 0}, // Appending a new cookie after the invalid one. - { "B", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB", 4}, - { "xAB", "xA", IsFirstReconcile::kBoth, "B", "B", "xAB", "PB", "B", "xAB", 4}, + { "B", "xA", IsFirstReconcile::kBoth, "PB", "B", "xAB", 4}, + { "xAB", "xA", IsFirstReconcile::kBoth, "PB", "B", "xAB", 4}, // Refresh existing cookies. - { "AB", "xAB", IsFirstReconcile::kBoth, "A", "AB", "AB", "PAB", "AB", "AB", 4}, - { "*AB", "xBxA", IsFirstReconcile::kNotFirst, "BA", "*AB", "BA", "PBA", "*AB", "BA", 1}, + { "AB", "xAB", IsFirstReconcile::kBoth, "PAB", "AB", "AB", 4}, + { "*AB", "xBxA", IsFirstReconcile::kNotFirst, "PBA", "*AB", "BA", 1}, // Appending and invalidating cookies at the same time. - { "xAB", "xAC", IsFirstReconcile::kFirst, "XB", "B", "B", "UB", "B", "B", 6}, - { "xAB", "xAC", IsFirstReconcile::kNotFirst, "X", "", "", "U", "", "", 6}, + { "xAB", "xAC", IsFirstReconcile::kFirst, "UB", "B", "B", 6}, + { "xAB", "xAC", IsFirstReconcile::kNotFirst, "U", "", "", 6}, - { "xAB", "AxC", IsFirstReconcile::kFirst, "XB", "B", "B", "UB", "B", "B", 3}, - { "xAB", "AxC", IsFirstReconcile::kNotFirst, "X", "", "", "U", "", "", 3}, + { "xAB", "AxC", IsFirstReconcile::kFirst, "UB", "B", "B", 3}, + { "xAB", "AxC", IsFirstReconcile::kNotFirst, "U", "", "", 3}, - { "*xAB", "xABC", IsFirstReconcile::kFirst, "XB", "*xAB", "B", "UB", "*xAB", "B", 5}, - { "*xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "*xA", "", "U", "*xA", "", 5}, + { "*xAB", "xABC", IsFirstReconcile::kFirst, "UB", "*xAB", "B", 5}, + { "*xAB", "xABC", IsFirstReconcile::kNotFirst, "U", "*xA", "", 5}, - { "xAB", "xABC", IsFirstReconcile::kFirst, "XB", "B", "B", "UB", "B", "B", 5}, - { "xAB", "xABC", IsFirstReconcile::kNotFirst, "X", "", "", "U", "", "", 5}, + { "xAB", "xABC", IsFirstReconcile::kFirst, "UB", "B", "B", 5}, + { "xAB", "xABC", IsFirstReconcile::kNotFirst, "U", "", "", 5}, // Miscellaneous cases. - // Check that unknown Gaia accounts are signed out. - { "*A", "AB", IsFirstReconcile::kBoth, "XA", "*A", "A", "UA", "*A", "A", 5}, + // Check that unknown Gaia accounts are signed o. + { "*A", "AB", IsFirstReconcile::kBoth, "UA", "*A", "A", 5}, // Check that Gaia default account is kept in first position. - { "AB", "BC", IsFirstReconcile::kBoth, "XBA", "AB", "BA", "UBA", "AB", "BA", 6}, + { "AB", "BC", IsFirstReconcile::kBoth, "UBA", "AB", "BA", 6}, // Check that Gaia cookie order is preserved for B. - { "*ABC", "CB", IsFirstReconcile::kFirst, "XABC", "*ABC", "ABC", "UABC", "*ABC", "ABC", 1}, + { "*ABC", "CB", IsFirstReconcile::kFirst, "UABC", "*ABC", "ABC", 1}, // TODO(https://crbug.com/1129931): Merge session should do XCB instead. - { "xABC", "ABC", IsFirstReconcile::kFirst, "XBC", "BC", "BC", "UCB", "BC", "CB", 1}, + { "xABC", "ABC", IsFirstReconcile::kFirst, "UCB", "BC", "CB", 1}, // Check that order in the chrome_accounts is not important. - { "A*B", "", IsFirstReconcile::kBoth, "BA", "A*B", "BA", "PBA", "A*B", "BA", 7}, - { "*xBA", "BA", IsFirstReconcile::kFirst, "X", "*xB", "" , "U", "*xB", "", 2}, + { "A*B", "", IsFirstReconcile::kBoth, "PBA", "A*B", "BA", 7}, + { "*xBA", "BA", IsFirstReconcile::kFirst, "U", "*xB", "", 2}, // Required for idempotency check. - { "", "", IsFirstReconcile::kNotFirst, "", "", "", "", "", "", 0}, - { "", "xA", IsFirstReconcile::kNotFirst, "", "", "xA", "", "", "xA", 0}, - { "", "xB", IsFirstReconcile::kNotFirst, "", "", "xB", "", "", "xB", 0}, - { "", "xAxB", IsFirstReconcile::kNotFirst, "", "", "xAxB", "", "", "xAxB", 0}, - { "", "xBxA", IsFirstReconcile::kNotFirst, "", "", "xBxA", "", "", "xBxA", 0}, - { "*A", "A", IsFirstReconcile::kNotFirst, "", "*A", "A", "", "*A", "A", 0}, - { "*A", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", "", "*A", "xBA", 0}, - { "*A", "AxB", IsFirstReconcile::kNotFirst, "", "*A", "AxB", "", "*A", "AxB", 0}, - { "A", "A", IsFirstReconcile::kNotFirst, "", "A", "A", "", "A", "A", 0}, - { "A", "xBA", IsFirstReconcile::kNotFirst, "", "A", "xBA", "", "A", "xBA", 0}, - { "A", "AxB", IsFirstReconcile::kNotFirst, "", "A", "AxB", "", "A", "AxB", 0}, - { "B", "B", IsFirstReconcile::kNotFirst, "", "B", "B", "", "B", "B", 0}, - { "B", "xAB", IsFirstReconcile::kNotFirst, "", "B", "xAB", "", "B", "xAB", 0}, - { "B", "BxA", IsFirstReconcile::kNotFirst, "", "B", "BxA", "", "B", "BxA", 0}, - { "*xA", "", IsFirstReconcile::kNotFirst, "", "*xA", "", "", "*xA", "", 0}, - { "*xA", "xAxB", IsFirstReconcile::kNotFirst, "", "*xA", "xAxB", "", "*xA", "xAxB", 0}, - { "*xA", "xBxA", IsFirstReconcile::kNotFirst, "", "*xA", "xBxA", "", "*xA", "xBxA", 0}, - { "*xA", "xA", IsFirstReconcile::kNotFirst, "", "*xA", "xA", "", "*xA", "xA", 0}, - { "*xA", "xB", IsFirstReconcile::kNotFirst, "", "*xA", "xB", "", "*xA", "xB", 0}, - { "*xAB", "B", IsFirstReconcile::kNotFirst, "", "*xAB", "B", "", "*xAB", "B", 0}, - { "*xAB", "BxA", IsFirstReconcile::kNotFirst, "", "*xAB", "BxA", "", "*xAB", "BxA", 0}, - { "*xAB", "xAB", IsFirstReconcile::kNotFirst, "", "*xAB", "xAB", "", "*xAB", "xAB", 0}, - { "*xAB", "xABxC",IsFirstReconcile::kNotFirst, "", "*xAB", "xABxC", "", "*xAB", "xABxC", 0}, - { "*xB", "", IsFirstReconcile::kNotFirst, "", "*xB", "", "", "*xB", "", 0}, - { "A*B", "BA", IsFirstReconcile::kNotFirst, "", "A*B", "BA", "", "A*B", "BA", 0}, - { "A*B", "AB", IsFirstReconcile::kNotFirst, "", "A*B", "AB", "", "A*B", "AB", 0}, - { "A", "AxC", IsFirstReconcile::kNotFirst, "", "A", "AxC", "", "A", "AxC", 0}, - { "AB", "BxCA", IsFirstReconcile::kNotFirst, "", "AB", "BxCA", "", "AB", "BxCA", 0}, - { "B", "xABxC",IsFirstReconcile::kNotFirst, "", "B", "xABxC", "", "B", "xABxC", 0}, - { "B", "xAxCB",IsFirstReconcile::kNotFirst, "", "B", "xAxCB", "", "B", "xAxCB", 0}, - { "*ABC", "ACB", IsFirstReconcile::kNotFirst, "", "*ABC", "ACB", "", "*ABC", "ACB", 0}, - { "*ABC", "ABC", IsFirstReconcile::kNotFirst, "", "*ABC", "ABC", "", "*ABC", "ABC", 0}, - { "BC", "BC", IsFirstReconcile::kNotFirst, "", "BC", "BC", "", "BC", "BC", 0}, - { "BC", "CB", IsFirstReconcile::kNotFirst, "", "BC", "CB", "", "BC", "CB", 0}, + { "", "", IsFirstReconcile::kNotFirst, "", "", "", 0}, + { "", "xA", IsFirstReconcile::kNotFirst, "", "", "xA", 0}, + { "", "xB", IsFirstReconcile::kNotFirst, "", "", "xB", 0}, + { "", "xAxB", IsFirstReconcile::kNotFirst, "", "", "xAxB", 0}, + { "", "xBxA", IsFirstReconcile::kNotFirst, "", "", "xBxA", 0}, + { "*A", "A", IsFirstReconcile::kNotFirst, "", "*A", "A", 0}, + { "*A", "xBA", IsFirstReconcile::kNotFirst, "", "*A", "xBA", 0}, + { "*A", "AxB", IsFirstReconcile::kNotFirst, "", "*A", "AxB", 0}, + { "A", "A", IsFirstReconcile::kNotFirst, "", "A", "A", 0}, + { "A", "xBA", IsFirstReconcile::kNotFirst, "", "A", "xBA", 0}, + { "A", "AxB", IsFirstReconcile::kNotFirst, "", "A", "AxB", 0}, + { "B", "B", IsFirstReconcile::kNotFirst, "", "B", "B", 0}, + { "B", "xAB", IsFirstReconcile::kNotFirst, "", "B", "xAB", 0}, + { "B", "BxA", IsFirstReconcile::kNotFirst, "", "B", "BxA", 0}, + { "*xA", "", IsFirstReconcile::kNotFirst, "", "*xA", "", 0}, + { "*xA", "xAxB", IsFirstReconcile::kNotFirst, "", "*xA", "xAxB", 0}, + { "*xA", "xBxA", IsFirstReconcile::kNotFirst, "", "*xA", "xBxA", 0}, + { "*xA", "xA", IsFirstReconcile::kNotFirst, "", "*xA", "xA", 0}, + { "*xA", "xB", IsFirstReconcile::kNotFirst, "", "*xA", "xB", 0}, + { "*xAB", "B", IsFirstReconcile::kNotFirst, "", "*xAB", "B", 0}, + { "*xAB", "BxA", IsFirstReconcile::kNotFirst, "", "*xAB", "BxA", 0}, + { "*xAB", "xAB", IsFirstReconcile::kNotFirst, "", "*xAB", "xAB", 0}, + { "*xAB", "xABxC",IsFirstReconcile::kNotFirst, "", "*xAB", "xABxC", 0}, + { "*xB", "", IsFirstReconcile::kNotFirst, "", "*xB", "", 0}, + { "A*B", "BA", IsFirstReconcile::kNotFirst, "", "A*B", "BA", 0}, + { "A*B", "AB", IsFirstReconcile::kNotFirst, "", "A*B", "AB", 0}, + { "A", "AxC", IsFirstReconcile::kNotFirst, "", "A", "AxC", 0}, + { "AB", "BxCA", IsFirstReconcile::kNotFirst, "", "AB", "BxCA", 0}, + { "B", "xABxC",IsFirstReconcile::kNotFirst, "", "B", "xABxC", 0}, + { "B", "xAxCB",IsFirstReconcile::kNotFirst, "", "B", "xAxCB", 0}, + { "*ABC", "ACB", IsFirstReconcile::kNotFirst, "", "*ABC", "ACB", 0}, + { "*ABC", "ABC", IsFirstReconcile::kNotFirst, "", "*ABC", "ABC", 0}, + { "BC", "BC", IsFirstReconcile::kNotFirst, "", "BC", "BC", 0}, + { "BC", "CB", IsFirstReconcile::kNotFirst, "", "BC", "CB", 0}, }; // clang-format on -// Parameterized version of AccountReconcilorTest that tests Dice -// implementation with MergeSession endpoint. -class AccountReconcilorTestDiceMergeSession - : public AccountReconcilorTestTable { +class AccountReconcilorTestForceDiceMigration + : public BaseAccountReconcilorTestTable, + public ::testing::WithParamInterface<ForceDiceMigrationTestTableParam> { public: - AccountReconcilorTestDiceMergeSession() = default; + AccountReconcilorTestForceDiceMigration() = default; - protected: - base::test::ScopedFeatureList scoped_feature_list_; + void RunReconcile() { + // Setup cookies. + std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies); + ConfigureCookieManagerService(cookies); + std::vector<Cookie> cookies_after_reconcile = cookies; - private: - DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestDiceMergeSession); -}; + // Call list accounts now so that the next call completes synchronously. + identity_test_env()->identity_manager()->GetAccountsInCookieJar(); + base::RunLoop().RunUntilIdle(); -// Checks one row of the kDiceParams table above. -TEST_P(AccountReconcilorTestDiceMergeSession, TableRowTest) { - SetAccountConsistency(signin::AccountConsistencyMethod::kDice); - scoped_feature_list_.InitAndDisableFeature(kUseMultiloginEndpoint); + // Setup tokens. This triggers listing cookies so we need to setup cookies + // before that. + SetupTokens(GetParam().tokens); - // Enable Dice. - SetAccountConsistency(signin::AccountConsistencyMethod::kDice); + // Setup expectations. + testing::InSequence mock_sequence; + bool should_logout; + if (GetParam().gaia_api_calls[0] != '\0') { + gaia::MultiloginMode mode = + GetParam().gaia_api_calls[0] == 'U' + ? gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER + : gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER; + // Generate expected array of accounts in cookies and set fake gaia + // response. + std::vector<CoreAccountId> accounts_to_send; + for (int i = 1; GetParam().gaia_api_calls[i] != '\0'; ++i) { + accounts_to_send.push_back( + CoreAccountId(accounts_[GetParam().gaia_api_calls[i]].gaia_id)); + } + const signin::MultiloginParameters params(mode, accounts_to_send); + cookies_after_reconcile = FakeSetAccountsInCookie(params, cookies); + should_logout = + accounts_to_send.empty() && + (mode == + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER); + if (should_logout) { + EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) + .Times(1); + } else { + EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)) + .Times(1); + } + } + // Reconcile. + AccountReconcilor* reconcilor = GetMockReconcilor(); + ASSERT_TRUE(reconcilor); + ASSERT_TRUE(reconcilor->first_execution_); + reconcilor->first_execution_ = true; + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); + if (GetParam().gaia_api_calls[0] != '\0') { + if (should_logout) { + SimulateLogOutFromCookieCompleted( + reconcilor, GoogleServiceAuthError::AuthErrorNone()); + } else { + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); + } + } - // Check that reconcile is idempotent: when called twice in a row it should do - // nothing on the second call. - CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/false); - RunReconcile(); -} + ASSERT_FALSE(reconcilor->is_reconcile_started_); + if (GetParam().tokens == GetParam().tokens_after_reconcile) { + EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); + } else { + // If the tokens were changed by the reconcile, a new reconcile should be + // scheduled. + EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, + reconcilor->GetState()); + } + VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile)); -INSTANTIATE_TEST_SUITE_P( - DiceTable, - AccountReconcilorTestDiceMergeSession, - ::testing::ValuesIn(GenerateTestCasesFromParams(kDiceParams))); + std::vector<Cookie> cookies_after = + ParseCookieString(GetParam().cookies_after_reconcile); + EXPECT_EQ(cookies_after, cookies_after_reconcile); -class AccountReconcilorTestForceDiceMigration - : public BaseAccountReconcilorTestTable, - public ::testing::WithParamInterface<ForceDiceMigrationTestTableParam> { - public: - AccountReconcilorTestForceDiceMigration() - : BaseAccountReconcilorTestTable(GetParam().tokens, - GetParam().cookies, - IsFirstReconcile::kFirst, - GetParam().gaia_api_calls, - GetParam().tokens_after_reconcile, - GetParam().cookies_after_reconcile) { - // ForceDiceMigration is temporary and the migration was enabled in in - // Q1 2020. It is expected to be removed in 2021 Q2. - // Simply disable the OAuthmultilogin endpoint instead of migrating the - // tests. - scoped_feature_list_.InitAndDisableFeature(kUseMultiloginEndpoint); + testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); + + // Another reconcile is sometimes triggered if Chrome accounts have + // changed. Allow it to finish. + EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) + .WillRepeatedly(testing::Return()); + ConfigureCookieManagerService({}); + base::RunLoop().RunUntilIdle(); } private: - base::test::ScopedFeatureList scoped_feature_list_; - DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestForceDiceMigration); }; // clang-format off const std::vector<ForceDiceMigrationTestTableParam> kForceDiceParams = { - {"*A", "AB", "XA", "*A", "A" }, - {"*AxB", "AB", "XA", "*A", "A" }, - {"AxB", "AB", "XA", "A", "A" }, - {"xAxB", "AB", "X", "", "" }, + {"*A", "AB", "UA", "*A", "A" }, + {"*AxB", "AB", "UA", "*A", "A" }, + {"AxB", "AB", "UA", "A", "A" }, + {"xAxB", "AB", "U", "", "" }, {"*A", "", "", "*xA", "" }, - {"*A", "B", "X", "*xA", "" }, + {"*A", "B", "U", "*xA", "" }, {"*AB", "B", "", "*xAB", "B" }, - {"*AxB", "B", "X", "*xA", "" }, + {"*AxB", "B", "U", "*xA", "" }, {"*ABC", "CB", "", "*xABC", "CB" }, {"*AB", "A", "", "*A", "A" }, {"AB", "A", "", "A", "A" }, {"AB", "", "", "", "" }, {"xAB", "", "", "", "" }, - {"xAB", "A", "X", "", "" }, + {"xAB", "A", "U", "", "" }, {"xAB", "xA", "", "", "xA" }, {"xAB", "B", "", "B", "B" }, - {"AxB", "B", "X", "", "" }, + {"AxB", "B", "U", "", "" }, {"AxB", "", "", "", "" }, {"xAxB", "", "", "", "" }, {"B", "xA", "", "", "xA" }, {"AB", "xAB", "", "B", "xAB" }, - {"xAB", "xAC", "X", "", "" }, - {"xAB", "AxC", "X", "", "" }, - {"AB", "BC", "XB", "B", "B" }, + {"xAB", "xAC", "U", "", "" }, + {"xAB", "AxC", "U", "", "" }, + {"AB", "BC", "UB", "B", "B" }, {"*AB", "", "", "*xA", "" }, {"*xAB", "", "", "*xA", "" }, {"*AxB", "", "", "*xA", "" }, @@ -1127,7 +1020,6 @@ const std::vector<ForceDiceMigrationTestTableParam> kForceDiceParams = { }; // clang-format on -// Checks one row of the kForceDiceParams table above. TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTest) { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); EXPECT_FALSE(test_signin_client()->is_dice_migration_completed()); @@ -1143,7 +1035,8 @@ TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTest) { TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTestCheckNoOp) { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); // Setup cookies. - std::vector<Cookie> cookies = ParseCookieString(cookies_after_reconcile_); + std::vector<Cookie> cookies = + ParseCookieString(GetParam().cookies_after_reconcile); ConfigureCookieManagerService(cookies); // Call list accounts now so that the next call completes synchronously. @@ -1152,16 +1045,15 @@ TEST_P(AccountReconcilorTestForceDiceMigration, TableRowTestCheckNoOp) { // Setup tokens. This triggers listing cookies so we need to setup cookies // before that. - SetupTokens(tokens_after_reconcile_); + SetupTokens(GetParam().tokens_after_reconcile); - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) .Times(0); AccountReconcilor* reconcilor = GetMockReconcilor(); EXPECT_FALSE(reconcilor->delegate_->ShouldRevokeTokensNotInCookies()); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1177,9 +1069,6 @@ class AccountReconcilorTestDiceMultilogin : public AccountReconcilorTestTable { public: AccountReconcilorTestDiceMultilogin() = default; - protected: - base::test::ScopedFeatureList scoped_feature_list_; - private: DISALLOW_COPY_AND_ASSIGN(AccountReconcilorTestDiceMultilogin); }; @@ -1187,9 +1076,8 @@ class AccountReconcilorTestDiceMultilogin : public AccountReconcilorTestTable { // Checks one row of the kDiceParams table above. TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); - scoped_feature_list_.InitAndEnableFeature(kUseMultiloginEndpoint); - CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/true); + CheckReconcileIdempotent(kDiceParams, GetParam()); // Setup cookies. std::vector<Cookie> cookies = ParseCookieString(GetParam().cookies); @@ -1206,18 +1094,18 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { // Setup expectations. testing::InSequence mock_sequence; - bool should_logout; - if (GetParam().gaia_api_calls_multilogin[0] != '\0') { + bool should_logout = false; + if (GetParam().gaia_api_calls[0] != '\0') { gaia::MultiloginMode mode = - GetParam().gaia_api_calls_multilogin[0] == 'U' + GetParam().gaia_api_calls[0] == 'U' ? gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER : gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER; // Generate expected array of accounts in cookies and set fake gaia // response. std::vector<CoreAccountId> accounts_to_send; - for (int i = 1; GetParam().gaia_api_calls_multilogin[i] != '\0'; ++i) { - accounts_to_send.push_back(CoreAccountId( - accounts_[GetParam().gaia_api_calls_multilogin[i]].gaia_id)); + for (int i = 1; GetParam().gaia_api_calls[i] != '\0'; ++i) { + accounts_to_send.push_back( + CoreAccountId(accounts_[GetParam().gaia_api_calls[i]].gaia_id)); } const signin::MultiloginParameters params(mode, accounts_to_send); cookies_after_reconcile = FakeSetAccountsInCookie(params, cookies); @@ -1238,8 +1126,8 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { ASSERT_TRUE(reconcilor->first_execution_); reconcilor->first_execution_ = GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; - reconcilor->StartReconcile(); - if (GetParam().gaia_api_calls_multilogin[0] != '\0') { + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); + if (GetParam().gaia_api_calls[0] != '\0') { if (should_logout) { SimulateLogOutFromCookieCompleted( reconcilor, GoogleServiceAuthError::AuthErrorNone()); @@ -1250,7 +1138,7 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { } ASSERT_FALSE(reconcilor->is_reconcile_started_); - if (GetParam().tokens == GetParam().tokens_after_reconcile_multilogin) { + if (GetParam().tokens == GetParam().tokens_after_reconcile) { EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } else { // If the tokens were changed by the reconcile, a new reconcile should be @@ -1258,11 +1146,10 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, reconcilor->GetState()); } - VerifyCurrentTokens( - ParseTokenString(GetParam().tokens_after_reconcile_multilogin)); + VerifyCurrentTokens(ParseTokenString(GetParam().tokens_after_reconcile)); std::vector<Cookie> cookies_after = - ParseCookieString(GetParam().cookies_after_reconcile_multilogin); + ParseCookieString(GetParam().cookies_after_reconcile); EXPECT_EQ(cookies_after, cookies_after_reconcile); testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); @@ -1280,28 +1167,18 @@ INSTANTIATE_TEST_SUITE_P( AccountReconcilorTestDiceMultilogin, ::testing::ValuesIn(GenerateTestCasesFromParams(kDiceParams))); -class AccountReconcilorDiceEndpointParamTest - : public AccountReconcilorTest, - public ::testing::WithParamInterface<bool> { +class AccountReconcilorDiceTest : public AccountReconcilorTest { public: - AccountReconcilorDiceEndpointParamTest() { + AccountReconcilorDiceTest() { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); - if (IsMultiloginEnabled()) - scoped_feature_list_.InitAndEnableFeature(kUseMultiloginEndpoint); - else - scoped_feature_list_.InitAndDisableFeature(kUseMultiloginEndpoint); } - bool IsMultiloginEnabled() { return GetParam(); } - - protected: - base::test::ScopedFeatureList scoped_feature_list_; private: - DISALLOW_COPY_AND_ASSIGN(AccountReconcilorDiceEndpointParamTest); + DISALLOW_COPY_AND_ASSIGN(AccountReconcilorDiceTest); }; // Tests that the AccountReconcilor is always registered. -TEST_P(AccountReconcilorDiceEndpointParamTest, DiceTokenServiceRegistration) { +TEST_F(AccountReconcilorDiceTest, DiceTokenServiceRegistration) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); ASSERT_TRUE(reconcilor->IsRegisteredWithIdentityManager()); @@ -1320,7 +1197,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceTokenServiceRegistration) { } // Tests that reconcile starts even when Sync is not enabled. -TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) { +TEST_F(AccountReconcilorDiceTest, DiceReconcileWithoutSignin) { // Add a token in Chrome but do not sign in. Making account available (setting // a refresh token) triggers listing cookies so we need to setup cookies // before that. @@ -1328,44 +1205,34 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) { const CoreAccountId account_id = identity_test_env()->MakeAccountAvailable("user@gmail.com").account_id; - if (!IsMultiloginEnabled()) { - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); - } else { std::vector<CoreAccountId> accounts_to_send = {account_id}; const signin::MultiloginParameters params( gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - } AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); - if (!IsMultiloginEnabled()) { - SimulateAddAccountToCookieCompleted( - reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); - } else { - SimulateSetAccountsInCookieCompleted( - reconcilor, signin::SetAccountsInCookieResult::kSuccess); - } + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } // Checks that nothing happens when there is no Chrome account and no Gaia // cookie. -TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileNoop) { +TEST_F(AccountReconcilorDiceTest, DiceReconcileNoop) { // No Chrome account and no cookie. signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) .Times(0); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -1373,8 +1240,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileNoop) { } // Tests that the first Gaia account is re-used when possible. -TEST_P(AccountReconcilorDiceEndpointParamTest, - DiceReconcileReuseGaiaFirstAccount) { +TEST_F(AccountReconcilorDiceTest, DiceReconcileReuseGaiaFirstAccount) { // Add account "other" to the Gaia cookie. signin::SetListAccountsResponseTwoAccounts( "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), @@ -1395,13 +1261,6 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_2)); - if (!IsMultiloginEnabled()) { - testing::InSequence mock_sequence; - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()); - // Account 2 is added first. - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_2)); - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1)); - } else { std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1}; // Send accounts to Gaia in order of chrome accounts. Account 2 is added // first. @@ -1409,30 +1268,20 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - } AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); - if (!IsMultiloginEnabled()) { - SimulateLogOutFromCookieCompleted(reconcilor, - GoogleServiceAuthError::AuthErrorNone()); - SimulateAddAccountToCookieCompleted( - reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone()); - SimulateAddAccountToCookieCompleted( - reconcilor, account_id_2, GoogleServiceAuthError::AuthErrorNone()); - } else { SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); - } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } // Tests that the first account is kept in cache and reused when cookies are // lost. -TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { +TEST_F(AccountReconcilorDiceTest, DiceLastKnownFirstAccount) { // Add accounts to the token service and the Gaia cookie in a different order. // Making account available (setting a refresh token) triggers listing cookies // so we need to setup cookies before that. @@ -1459,14 +1308,13 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { // Do one reconcile. It should do nothing but to populating the last known // account. { - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) .Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) .Times(0); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -1477,17 +1325,6 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_); identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false); - if (!IsMultiloginEnabled()) { - // Reconcile again and check that account_id_2 is added first. - testing::InSequence mock_sequence; - - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_2)) - .Times(1); - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id_1)) - .Times(1); - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(0); - } else { // Since Gaia can't know about cached account, make sure that we reorder // chrome accounts accordingly even in PRESERVE mode. std::vector<CoreAccountId> accounts_to_send = {account_id_2, account_id_1}; @@ -1495,27 +1332,19 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - } AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); - if (!IsMultiloginEnabled()) { - SimulateAddAccountToCookieCompleted( - reconcilor, account_id_2, GoogleServiceAuthError::AuthErrorNone()); - SimulateAddAccountToCookieCompleted( - reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone()); - } else { SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); - } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } // Checks that the reconcilor does not log out unverified accounts. -TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountNoop) { +TEST_F(AccountReconcilorDiceTest, UnverifiedAccountNoop) { // Add a unverified account to the Gaia cookie. signin::SetListAccountsResponseOneAccountWithParams( {"user@gmail.com", "12345", true /* valid */, false /* signed_out */, @@ -1523,13 +1352,12 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountNoop) { &test_url_loader_factory_); // Check that nothing happens. - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(testing::_)).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()).Times(0); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)) .Times(0); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -1538,7 +1366,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountNoop) { // Checks that the reconcilor does not log out unverified accounts when adding // a new account to the Gaia cookie. -TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { +TEST_F(AccountReconcilorDiceTest, UnverifiedAccountMerge) { // Add a unverified account to the Gaia cookie. signin::SetListAccountsResponseOneAccountWithParams( {"user@gmail.com", "12345", true /* valid */, false /* signed_out */, @@ -1549,14 +1377,6 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { const CoreAccountId chrome_account_id = identity_test_env()->MakeAccountAvailable("other@gmail.com").account_id; - if (!IsMultiloginEnabled()) { - // Check that the Chrome account is merged and the unverified account is not - // logged out. - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(chrome_account_id)) - .Times(1); - EXPECT_CALL(*GetMockReconcilor(), PerformLogoutAllAccountsAction()) - .Times(0); - } else { // In PRESERVE mode it is up to Gaia to not delete existing accounts in // cookies and not sign out unveridied accounts. std::vector<CoreAccountId> accounts_to_send = {chrome_account_id}; @@ -1564,27 +1384,17 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER, accounts_to_send); EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); - } AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); - if (!IsMultiloginEnabled()) { - SimulateAddAccountToCookieCompleted( - reconcilor, chrome_account_id, GoogleServiceAuthError::AuthErrorNone()); - } else { SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); - } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } -INSTANTIATE_TEST_SUITE_P(TestDiceEndpoint, - AccountReconcilorDiceEndpointParamTest, - ::testing::ValuesIn({false, true})); - TEST_F(AccountReconcilorTest, DiceDeleteCookie) { SetAccountConsistency(signin::AccountConsistencyMethod::kDice); @@ -1761,7 +1571,7 @@ TEST_P(AccountReconcilorTestMirrorMultilogin, TableRowTest) { ASSERT_TRUE(reconcilor->first_execution_); reconcilor->first_execution_ = GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); @@ -1881,7 +1691,7 @@ TEST_P(AccountReconcilorTestActiveDirectory, TableRowTestMultilogin) { ASSERT_TRUE(reconcilor->first_execution_); reconcilor->first_execution_ = GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); if (GetParam().gaia_api_calls[0] != '\0') { if (should_logout) { SimulateLogOutFromCookieCompleted( @@ -1923,7 +1733,7 @@ TEST_F(AccountReconcilorMirrorTest, TokensNotLoaded) { identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState(); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); // No reconcile when tokens are not loaded. ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -1937,6 +1747,7 @@ TEST_F(AccountReconcilorMirrorTest, TokensNotLoaded) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); ASSERT_TRUE(reconcilor->is_reconcile_started_); + EXPECT_EQ(AccountReconcilor::Trigger::kTokensLoaded, reconcilor->trigger_); base::RunLoop().RunUntilIdle(); SimulateSetAccountsInCookieCompleted( @@ -1965,7 +1776,7 @@ TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieSuccess) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, reconcilor->GetState()); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); base::RunLoop().RunUntilIdle(); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); @@ -1999,7 +1810,7 @@ TEST_F(AccountReconcilorMirrorTest, EnableReconcileWhileAlreadyRunning) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, reconcilor->GetState()); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); reconcilor->EnableReconcile(); EXPECT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); @@ -2024,7 +1835,7 @@ TEST_F(AccountReconcilorMirrorTest, GetAccountsFromCookieFailure) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, reconcilor->GetState()); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); base::RunLoop().RunUntilIdle(); @@ -2061,7 +1872,7 @@ TEST_F(AccountReconcilorMirrorTest, ExtraCookieChangeNotification) { ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, reconcilor->GetState()); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_RUNNING, reconcilor->GetState()); // Add extra cookie change notification. Reconcilor should ignore it. @@ -2090,11 +1901,18 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileNoop) { signin::SetListAccountsResponseOneAccount( account_info.email, account_info.gaia, &test_url_loader_factory_); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); + + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kOperationHistogramName, + AccountReconcilor::Operation::kNoop, 1); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerNoopHistogramName, + AccountReconcilor::Trigger::kCookieChange, 1); } TEST_F(AccountReconcilorMirrorTest, StartReconcileCookiesDisabled) { @@ -2106,7 +1924,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileCookiesDisabled) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_FALSE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); @@ -2135,6 +1953,8 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileContentSettings) { SimulateCookieContentSettingsChanged(reconcilor, ContentSettingsPattern::Wildcard()); ASSERT_TRUE(reconcilor->is_reconcile_started_); + EXPECT_EQ(AccountReconcilor::Trigger::kCookieSettingChange, + reconcilor->trigger_); } TEST_F(AccountReconcilorMirrorTest, StartReconcileContentSettingsGaiaUrl) { @@ -2202,7 +2022,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopWithDots) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); } @@ -2218,7 +2038,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileNoopMultiple) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); } @@ -2240,7 +2060,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookie) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2255,6 +2075,19 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookie) { EXPECT_THAT(histogram_tester()->GetTotalCountsForPrefix( "Signin.Reconciler.Duration.UpTo3mins.Success"), testing::ContainerEq(expected_counts)); + + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kOperationHistogramName, + AccountReconcilor::Operation::kMultilogin, 1); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerMultiloginHistogramName, + AccountReconcilor::Trigger::kCookieChange, 1); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerLogoutHistogramName, 0); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerNoopHistogramName, 0); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerThrottledHistogramName, 0); } TEST_F(AccountReconcilorTest, AuthErrorTriggersListAccount) { @@ -2332,7 +2165,7 @@ TEST_F(AccountReconcilorMirrorTest, SignoutAfterErrorDoesNotRecordUma) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2366,7 +2199,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileRemoveFromCookie) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); @@ -2389,7 +2222,7 @@ TEST_F(AccountReconcilorMirrorTest, TokenErrorOnPrimary) { &test_url_loader_factory_); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2416,7 +2249,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookieTwice) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(ml_params_1)); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2443,6 +2276,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileAddToCookieTwice) { base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); + EXPECT_EQ(AccountReconcilor::Trigger::kTokenChange, reconcilor->trigger_); SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); @@ -2468,7 +2302,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileBadPrimary) { EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(params)); AccountReconcilor* reconcilor = GetMockReconcilor(); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2488,7 +2322,7 @@ TEST_F(AccountReconcilorMirrorTest, StartReconcileOnlyOnce) { ASSERT_TRUE(reconcilor); ASSERT_FALSE(reconcilor->is_reconcile_started_); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); @@ -2529,7 +2363,7 @@ TEST_F(AccountReconcilorMirrorTest, Lock) { std::unique_ptr<AccountReconcilor::Lock> lock_1 = std::make_unique<AccountReconcilor::Lock>(reconcilor); EXPECT_EQ(1, reconcilor->account_reconcilor_lock_count_); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); // lock_1 is blocking the reconcile. EXPECT_FALSE(reconcilor->is_reconcile_started_); { @@ -2551,6 +2385,8 @@ TEST_F(AccountReconcilorMirrorTest, Lock) { EXPECT_EQ(1, observer.started_count_); EXPECT_EQ(1, observer.unblocked_count_); EXPECT_EQ(1, observer.blocked_count_); + EXPECT_EQ(AccountReconcilor::Trigger::kUnblockReconcile, + reconcilor->trigger_); // Lock aborts current reconcile, and restarts it later. { @@ -2590,9 +2426,6 @@ TEST_P(AccountReconcilorMethodParamTest, AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - if (!reconcilor->IsMultiloginEndpointEnabled()) { - EXPECT_CALL(*GetMockReconcilor(), PerformMergeAction(account_id)); - } else { switch (account_consistency) { case signin::AccountConsistencyMethod::kMirror: { signin::MultiloginParameters params( @@ -2612,20 +2445,14 @@ TEST_P(AccountReconcilorMethodParamTest, NOTREACHED(); break; } - } ASSERT_FALSE(reconcilor->is_reconcile_started_); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); - if (!reconcilor->IsMultiloginEndpointEnabled()) { - SimulateAddAccountToCookieCompleted( - reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); - } else { SimulateSetAccountsInCookieCompleted( reconcilor, signin::SetAccountsInCookieResult::kSuccess); - } ASSERT_FALSE(reconcilor->is_reconcile_started_); } @@ -2648,7 +2475,7 @@ TEST_F(AccountReconcilorMirrorTest, ASSERT_TRUE(reconcilor); ASSERT_FALSE(reconcilor->is_reconcile_started_); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); @@ -2685,7 +2512,7 @@ TEST_F(AccountReconcilorMirrorTest, NoLoopWithBadPrimary) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2707,7 +2534,7 @@ TEST_F(AccountReconcilorMirrorTest, NoLoopWithBadPrimary) { identity_test_env()->identity_manager(), account_id1, error); // A second attempt to reconcile should be a noop. - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); @@ -2740,7 +2567,7 @@ TEST_F(AccountReconcilorMirrorTest, WontMergeAccountsWithError) { AccountReconcilor* reconcilor = GetMockReconcilor(); ASSERT_TRUE(reconcilor); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); @@ -2765,7 +2592,7 @@ TEST_F(AccountReconcilorTest, DelegateTimeoutIsCalled) { base::MockOneShotTimer* timer = timer0.get(); reconcilor->set_timer_for_testing(std::move(timer0)); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); ASSERT_TRUE(timer->IsRunning()); @@ -2788,7 +2615,7 @@ TEST_F(AccountReconcilorMirrorTest, DelegateTimeoutIsNotCalled) { base::MockOneShotTimer* timer = timer0.get(); reconcilor->set_timer_for_testing(std::move(timer0)); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); EXPECT_TRUE(reconcilor->is_reconcile_started_); EXPECT_FALSE(timer->IsRunning()); } @@ -2806,7 +2633,7 @@ TEST_F(AccountReconcilorTest, DelegateTimeoutIsNotCalledIfTimeoutIsNotReached) { base::MockOneShotTimer* timer = timer0.get(); reconcilor->set_timer_for_testing(std::move(timer0)); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); ASSERT_TRUE(timer->IsRunning()); @@ -2870,13 +2697,23 @@ TEST_F(AccountReconcilorTest, MultiloginLogout) { // No multilogin call. EXPECT_CALL(*reconcilor, PerformSetCookiesAction(testing::_)).Times(0); - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); ASSERT_TRUE(reconcilor->is_reconcile_started_); base::RunLoop().RunUntilIdle(); SimulateLogOutFromCookieCompleted(reconcilor, GoogleServiceAuthError::AuthErrorNone()); EXPECT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kOperationHistogramName, + AccountReconcilor::Operation::kLogout, 1); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerLogoutHistogramName, + AccountReconcilor::Trigger::kCookieChange, 1); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerThrottledHistogramName, 0); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerMultiloginHistogramName, 0); } // Reconcilor does not start after being shutdown. Regression test for @@ -2887,7 +2724,8 @@ TEST_F(AccountReconcilorTest, ReconcileAfterShutdown) { EXPECT_FALSE(reconcilor->WasShutDown()); reconcilor->Shutdown(); EXPECT_TRUE(reconcilor->WasShutDown()); - reconcilor->StartReconcile(); // This should not crash. + // This should not crash. + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); EXPECT_FALSE(reconcilor->is_reconcile_started_); } @@ -2901,10 +2739,229 @@ TEST_F(AccountReconcilorTest, UnlockAfterShutdown) { // Reconcile does not start now because of the Lock, but is scheduled to start // when the lock is released. - reconcilor->StartReconcile(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); EXPECT_FALSE(reconcilor->is_reconcile_started_); reconcilor->Shutdown(); lock.reset(); // This should not crash. EXPECT_FALSE(reconcilor->is_reconcile_started_); } + +class AccountReconcilorThrottlerTest : public AccountReconcilorTest { + public: + AccountReconcilorThrottlerTest() { +#if BUILDFLAG(ENABLE_DICE_SUPPORT) + signin::AccountConsistencyMethod account_consistency = + signin::AccountConsistencyMethod::kDice; + SetAccountConsistency(account_consistency); +#else + signin::AccountConsistencyMethod account_consistency = + signin::AccountConsistencyMethod::kMirror; + SetAccountConsistency(account_consistency); +#endif + minutes_to_refill_per_request_ = + 1 / AccountReconcilorThrottler::kRefillRequestsBucketRatePerMinute; + } + + void ConsumeRequests(size_t number_of_requests, + const signin::MultiloginParameters& expected_params) { + AccountReconcilor* reconcilor = GetMockReconcilor(); + for (size_t i = 0; i < number_of_requests; ++i) { + EXPECT_CALL(*GetMockReconcilor(), + PerformSetCookiesAction(expected_params)); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); + base::RunLoop().RunUntilIdle(); + // Reconciliation not blocked. + ASSERT_TRUE(reconcilor->is_reconcile_started_); + + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + ASSERT_EQ(GoogleServiceAuthError::State::NONE, + reconcilor->error_during_last_reconcile_.state()); + testing::Mock::VerifyAndClearExpectations(GetMockReconcilor()); + } + } + + void VerifyRequestsBlockedByThrottler() { + AccountReconcilor* reconcilor = GetMockReconcilor(); + reconcilor->StartReconcile(AccountReconcilor::Trigger::kCookieChange); + base::RunLoop().RunUntilIdle(); + // Reconciliation should fail. + ASSERT_FALSE(reconcilor->is_reconcile_started_); + ASSERT_EQ(GoogleServiceAuthError::State::REQUEST_CANCELED, + reconcilor->error_during_last_reconcile_.state()); + } + + void FastForwadTimeToRefillRequests(size_t number_of_requests) { + task_environment()->FastForwardBy(base::TimeDelta::FromMinutes( + minutes_to_refill_per_request_ * number_of_requests)); + } + + private: + size_t minutes_to_refill_per_request_; + DISALLOW_COPY_AND_ASSIGN(AccountReconcilorThrottlerTest); +}; + +TEST_F(AccountReconcilorThrottlerTest, RefillOneRequest) { + AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); + const CoreAccountId account_id = account_info.account_id; + signin::SetListAccountsResponseOneAccount( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + &test_url_loader_factory_); + + signin::MultiloginParameters params( + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id}); + + // Consume all available requests. + ConsumeRequests(AccountReconcilorThrottler::kMaxAllowedRequestsPerBucket, + params); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerMultiloginHistogramName, + AccountReconcilor::Trigger::kCookieChange, 30); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerThrottledHistogramName, 0); + + // At this point all the requests in the available request buckets should + // have been consumed. + VerifyRequestsBlockedByThrottler(); + + // Allow enough time to refill 1 request. + FastForwadTimeToRefillRequests(1); + ConsumeRequests(1, params); + + // The blocked request recorded upon allowing a new request. + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1, 1); + histogram_tester()->ExpectBucketCount( + AccountReconcilor::kOperationHistogramName, + AccountReconcilor::Operation::kThrottled, 1); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerThrottledHistogramName, + AccountReconcilor::Trigger::kCookieChange, 1); + histogram_tester()->ExpectBucketCount( + AccountReconcilor::kOperationHistogramName, + AccountReconcilor::Operation::kMultilogin, 31); + histogram_tester()->ExpectUniqueSample( + AccountReconcilor::kTriggerMultiloginHistogramName, + AccountReconcilor::Trigger::kCookieChange, 31); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerLogoutHistogramName, 0); + histogram_tester()->ExpectTotalCount( + AccountReconcilor::kTriggerNoopHistogramName, 0); + + // No Available requests. + VerifyRequestsBlockedByThrottler(); + + DeleteReconcilor(); + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1, 2); +} + +TEST_F(AccountReconcilorThrottlerTest, RefillFiveRequests) { + AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); + const CoreAccountId account_id = account_info.account_id; + signin::SetListAccountsResponseOneAccount( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + &test_url_loader_factory_); + + signin::MultiloginParameters params( + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id}); + + // Consume all available requests. + ConsumeRequests(AccountReconcilorThrottler::kMaxAllowedRequestsPerBucket, + params); + + // At this point all the requests in the available request buckets should + // have been consumed. + VerifyRequestsBlockedByThrottler(); + + // Allow enough time to refill 1 request. + FastForwadTimeToRefillRequests(5); + ConsumeRequests(5, params); + + // The blocked request recorded upon allowing a new request. + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1, 1); + + // No Available requests. + VerifyRequestsBlockedByThrottler(); + + DeleteReconcilor(); + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1, 2); +} + +TEST_F(AccountReconcilorThrottlerTest, NewRequestParamsPasses) { + AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); + const CoreAccountId account_id = account_info.account_id; + signin::SetListAccountsResponseOneAccount( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + &test_url_loader_factory_); + + signin::MultiloginParameters params( + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id}); + + // Consume all available requests. + ConsumeRequests(AccountReconcilorThrottler::kMaxAllowedRequestsPerBucket, + params); + + // Next request should fail. + VerifyRequestsBlockedByThrottler(); + + // Trigger different params. + AccountReconcilor* reconcilor = GetMockReconcilor(); + EXPECT_CALL(*GetMockReconcilor(), PerformSetCookiesAction(testing::_)); + identity_test_env()->MakeAccountAvailable("other@gmail.com"); + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(reconcilor->is_reconcile_started_); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); + ASSERT_FALSE(reconcilor->is_reconcile_started_); + + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1, 1); +} + +TEST_F(AccountReconcilorThrottlerTest, BlockFiveRequests) { + AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); + const CoreAccountId account_id = account_info.account_id; + signin::SetListAccountsResponseOneAccount( + "other@gmail.com", signin::GetTestGaiaIdForEmail("other@gmail.com"), + &test_url_loader_factory_); + + signin::MultiloginParameters params( + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, + {account_id}); + + // Consume all available requests. + ConsumeRequests(AccountReconcilorThrottler::kMaxAllowedRequestsPerBucket, + params); + + // At this point all the requests in the available request buckets should + // have been consumed. + size_t rejected_requests = 5; + for (size_t i = 0; i < rejected_requests; ++i) { + VerifyRequestsBlockedByThrottler(); + } + + // Allow enough time to refill 1 request. + FastForwadTimeToRefillRequests(1); + ConsumeRequests(1, params); + + // The blocked request recorded upon allowing a new request. + histogram_tester()->ExpectBucketCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", + rejected_requests, 1); + + // Allow a new request with no blocked requests in between. + FastForwadTimeToRefillRequests(1); + ConsumeRequests(1, params); + // The number of samples should remain 1. + histogram_tester()->ExpectTotalCount( + "Signin.Reconciler.RejectedRequestsDueToThrottler.Update", 1); +} diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc index 1a5205a5fc2..ea3ac36f547 100644 --- a/chromium/components/signin/core/browser/chrome_connected_header_helper.cc +++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.cc @@ -20,7 +20,8 @@ #include "url/gurl.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "components/signin/public/base/signin_switches.h" +#include "chromeos/crosapi/mojom/crosapi.mojom.h" +#include "chromeos/lacros/lacros_chrome_service_impl.h" #endif namespace signin { @@ -39,7 +40,7 @@ const char kProfileModeAttrName[] = "mode"; const char kServiceTypeAttrName[] = "action"; const char kSupervisedAttrName[] = "supervised"; const char kSourceAttrName[] = "source"; -#if defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) const char kEligibleForConsistency[] = "eligible_for_consistency"; const char kShowConsistencyPromo[] = "show_consistency_promo"; #endif @@ -83,7 +84,7 @@ std::string ChromeConnectedHeaderHelper::BuildRequestCookieIfPossible( // this information in the ChromeConnected cookie. return chrome_connected_helper.BuildRequestHeader( false /* is_header_request */, url, gaia_id, - base::nullopt /* is_child_account */, profile_mode_mask, "" /* source */, + absl::nullopt /* is_child_account */, profile_mode_mask, "" /* source */, false /* force_account_consistency */); } @@ -108,7 +109,7 @@ ManageAccountsParams ChromeConnectedHeaderHelper::BuildManageAccountsParams( params.continue_url = value; } else if (key_name == kIsSameTabAttrName) { params.is_same_tab = value == "true"; -#if defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) } else if (key_name == kShowConsistencyPromo) { params.show_consistency_promo = value == "true"; #endif @@ -187,7 +188,7 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader( bool is_header_request, const GURL& url, const std::string& gaia_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, int profile_mode_mask, const std::string& source, bool force_account_consistency) { @@ -203,16 +204,12 @@ std::string ChromeConnectedHeaderHelper::BuildRequestHeader( // Sessions and Active Directory logins. Guest Sessions have already been // filtered upstream and we want to enforce account consistency in Public // Sessions and Active Directory logins. -#if BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) force_account_consistency = true; -#elif BUILDFLAG(IS_CHROMEOS_LACROS) - if (base::FeatureList::IsEnabled(switches::kUseAccountManagerFacade)) { - force_account_consistency = true; - } #endif if (!force_account_consistency && gaia_id.empty()) { -#if defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) if (base::FeatureList::IsEnabled(kMobileIdentityConsistency) && gaia::IsGaiaSignonRealm(url.GetOrigin())) { parts.push_back( diff --git a/chromium/components/signin/core/browser/chrome_connected_header_helper.h b/chromium/components/signin/core/browser/chrome_connected_header_helper.h index c17dc782c61..703f5d3747b 100644 --- a/chromium/components/signin/core/browser/chrome_connected_header_helper.h +++ b/chromium/components/signin/core/browser/chrome_connected_header_helper.h @@ -7,9 +7,9 @@ #include <string> -#include "base/optional.h" #include "components/signin/core/browser/signin_header_helper.h" #include "components/signin/public/base/account_consistency_method.h" +#include "third_party/abseil-cpp/absl/types/optional.h" class GURL; @@ -47,7 +47,7 @@ class ChromeConnectedHeaderHelper : public SigninHeaderHelper { std::string BuildRequestHeader(bool is_header_request, const GURL& url, const std::string& gaia_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, int profile_mode_mask, const std::string& source, bool force_account_consistency); diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc index 08dd3c8f0e7..ee59bfb477c 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.cc @@ -14,9 +14,6 @@ #include "components/signin/public/base/signin_client.h" #include "components/signin/public/base/signin_pref_names.h" -const base::Feature kUseMultiloginEndpoint{"UseMultiloginEndpoint", - base::FEATURE_ENABLED_BY_DEFAULT}; - namespace signin { DiceAccountReconcilorDelegate::DiceAccountReconcilorDelegate( @@ -31,10 +28,6 @@ bool DiceAccountReconcilorDelegate::IsReconcileEnabled() const { return true; } -bool DiceAccountReconcilorDelegate::IsMultiloginEndpointEnabled() const { - return base::FeatureList::IsEnabled(kUseMultiloginEndpoint); -} - DiceAccountReconcilorDelegate::InconsistencyReason DiceAccountReconcilorDelegate::GetInconsistencyReason( const CoreAccountId& primary_account, diff --git a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h index 534a2be68b8..55635846dd6 100644 --- a/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/dice_account_reconcilor_delegate.h @@ -5,17 +5,12 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_DICE_ACCOUNT_RECONCILOR_DELEGATE_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_DICE_ACCOUNT_RECONCILOR_DELEGATE_H_ -#include <string> - #include "base/macros.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" #include "components/signin/public/base/account_consistency_method.h" class SigninClient; -// Enables usage of Gaia Auth Multilogin endpoint for identity consistency. -extern const base::Feature kUseMultiloginEndpoint; - namespace signin { // AccountReconcilorDelegate specialized for Dice. @@ -27,7 +22,6 @@ class DiceAccountReconcilorDelegate : public AccountReconcilorDelegate { // AccountReconcilorDelegate: bool IsReconcileEnabled() const override; - bool IsMultiloginEndpointEnabled() const override; gaia::GaiaSource GetGaiaApiSource() const override; CoreAccountId GetFirstGaiaAccountForReconcile( const std::vector<CoreAccountId>& chrome_accounts, diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc index 2accff2fc0e..c1185dcc5dd 100644 --- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc +++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.cc @@ -39,12 +39,19 @@ bool MirrorAccountReconcilorDelegate::ShouldAbortReconcileIfPrimaryHasError() ConsentLevel MirrorAccountReconcilorDelegate::GetConsentLevelForPrimaryAccount() const { -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_IOS) if (base::FeatureList::IsEnabled(kMobileIdentityConsistency)) { return ConsentLevel::kSignin; } -#endif return ConsentLevel::kSync; +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + // Whenever Mirror is enabled on a Lacros Profile, the Primary Account may or + // may not have consented to Chrome Sync. But we want to enable + // `AccountReconcilor` regardless - for minting Gaia cookies. + return ConsentLevel::kSignin; +#else + return ConsentLevel::kSync; +#endif } CoreAccountId MirrorAccountReconcilorDelegate::GetFirstGaiaAccountForReconcile( diff --git a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h index 192325ee2f5..08ff4ecd9ee 100644 --- a/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h +++ b/chromium/components/signin/core/browser/mirror_account_reconcilor_delegate.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_MIRROR_ACCOUNT_RECONCILOR_DELEGATE_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_MIRROR_ACCOUNT_RECONCILOR_DELEGATE_H_ -#include <string> #include <vector> #include "base/macros.h" diff --git a/chromium/components/signin/core/browser/signin_error_controller.h b/chromium/components/signin/core/browser/signin_error_controller.h index 8a5fcfb7dc9..3859c9bf97f 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.h +++ b/chromium/components/signin/core/browser/signin_error_controller.h @@ -5,8 +5,6 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ERROR_CONTROLLER_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_ERROR_CONTROLLER_H_ -#include <string> - #include "base/macros.h" #include "base/observer_list.h" #include "base/scoped_observation.h" diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc index d8fabb6b2bf..8188d319443 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.cc +++ b/chromium/components/signin/core/browser/signin_header_helper.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_functions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "components/google/core/common/google_util.h" #include "components/signin/core/browser/chrome_connected_header_helper.h" @@ -24,6 +25,7 @@ namespace signin { const char kChromeConnectedHeader[] = "X-Chrome-Connected"; const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts"; +const char kAutoLoginHeader[] = "X-Auto-Login"; const char kDiceRequestHeader[] = "X-Chrome-ID-Consistency-Request"; const char kDiceResponseHeader[] = "X-Chrome-ID-Consistency-Response"; @@ -148,9 +150,9 @@ SigninHeaderHelper::ParseAccountConsistencyResponseHeader( continue; } dictionary.insert( - {field.substr(0, delim).as_string(), + {std::string(field.substr(0, delim)), net::UnescapeURLComponent( - field.substr(delim + 1).as_string(), + field.substr(delim + 1), net::UnescapeRule::PATH_SEPARATORS | net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)}); } @@ -167,7 +169,7 @@ void AppendOrRemoveMirrorRequestHeader( RequestAdapter* request, const GURL& redirect_url, const std::string& gaia_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, AccountConsistencyMethod account_consistency, const content_settings::CookieSettings* cookie_settings, int profile_mode_mask, diff --git a/chromium/components/signin/core/browser/signin_header_helper.h b/chromium/components/signin/core/browser/signin_header_helper.h index bf76900e121..d87917cf169 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.h +++ b/chromium/components/signin/core/browser/signin_header_helper.h @@ -40,6 +40,13 @@ extern const char kChromeManageAccountsHeader[]; extern const char kDiceRequestHeader[]; extern const char kDiceResponseHeader[]; +// The X-Auto-Login header detects when a user is prompted to enter their +// credentials on the Gaia sign-in page. It is sent with an empty email if the +// user is on the Gaia sign-in email page or a pre-filled email if the user has +// selected an account on the AccountChooser. X-Auto-Login is not sent following +// a reauth request. +extern const char kAutoLoginHeader[]; + // The ServiceType specified by Gaia in the response header accompanying the 204 // response. This indicates the action Chrome is supposed to lead the user to // perform. @@ -197,10 +204,6 @@ class SigninHeaderHelper { const GURL& url, const content_settings::CookieSettings* cookie_settings) = 0; - protected: - SigninHeaderHelper(); - virtual ~SigninHeaderHelper(); - // Dictionary of fields in a account consistency response header. using ResponseHeaderDictionary = std::multimap<std::string, std::string>; @@ -209,6 +212,10 @@ class SigninHeaderHelper { static ResponseHeaderDictionary ParseAccountConsistencyResponseHeader( const std::string& header_value); + protected: + SigninHeaderHelper(); + virtual ~SigninHeaderHelper(); + // Returns whether the url is eligible for the request header. virtual bool IsUrlEligibleForRequestHeader(const GURL& url) = 0; @@ -238,7 +245,7 @@ void AppendOrRemoveMirrorRequestHeader( RequestAdapter* request, const GURL& redirect_url, const std::string& gaia_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, AccountConsistencyMethod account_consistency, const content_settings::CookieSettings* cookie_settings, int profile_mode_mask, diff --git a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc index b5e4ff3a984..ad5614f996d 100644 --- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc @@ -27,7 +27,9 @@ #include "url/gurl.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "components/signin/public/base/signin_switches.h" +#include "chromeos/lacros/lacros_chrome_service_delegate.h" +#include "chromeos/lacros/lacros_chrome_service_impl.h" +#include "chromeos/lacros/lacros_test_helper.h" #endif #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -72,6 +74,14 @@ class SigninHeaderHelperTest : public testing::Test { content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry()); HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); +#if BUILDFLAG(IS_CHROMEOS_LACROS) + // TODO(crbug.com/1198528): remove this after the rollout. + if (!chromeos::LacrosChromeServiceImpl::Get()) { + scoped_lacros_test_helper_ = + std::make_unique<chromeos::ScopedLacrosServiceTestHelper>(); + } +#endif + settings_map_ = new HostContentSettingsMap( &prefs_, false /* is_off_the_record */, false /* store_last_modified */, false /* restore_session */); @@ -93,7 +103,7 @@ class SigninHeaderHelperTest : public testing::Test { net::HttpRequestHeaders CreateRequest( const GURL& url, const std::string& account_id, - const base::Optional<bool>& is_child_account) { + const absl::optional<bool>& is_child_account) { net::HttpRequestHeaders original_headers; RequestAdapterWrapper request_adapter(url, original_headers); AppendOrRemoveMirrorRequestHeader( @@ -121,7 +131,7 @@ class SigninHeaderHelperTest : public testing::Test { void CheckMirrorHeaderRequest(const GURL& url, const std::string& account_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, const std::string& expected_request) { net::HttpRequestHeaders headers = CreateRequest(url, account_id, is_child_account); @@ -132,7 +142,7 @@ class SigninHeaderHelperTest : public testing::Test { #if BUILDFLAG(ENABLE_DICE_SUPPORT) void CheckDiceHeaderRequest(const GURL& url, const std::string& account_id, - const base::Optional<bool>& is_child_account, + const absl::optional<bool>& is_child_account, const std::string& expected_mirror_request, const std::string& expected_dice_request) { net::HttpRequestHeaders headers = @@ -144,7 +154,7 @@ class SigninHeaderHelperTest : public testing::Test { } #endif - base::test::SingleThreadTaskEnvironment task_environment_; + base::test::TaskEnvironment task_environment_; bool sync_enabled_ = false; std::string device_id_ = kTestDeviceId; @@ -154,6 +164,10 @@ class SigninHeaderHelperTest : public testing::Test { sync_preferences::TestingPrefServiceSyncable prefs_; +#if BUILDFLAG(IS_CHROMEOS_LACROS) + std::unique_ptr<chromeos::ScopedLacrosServiceTestHelper> + scoped_lacros_test_helper_; +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) scoped_refptr<HostContentSettingsMap> settings_map_; scoped_refptr<content_settings::CookieSettings> cookie_settings_; }; @@ -163,24 +177,25 @@ class SigninHeaderHelperTest : public testing::Test { // account id). TEST_F(SigninHeaderHelperTest, TestMirrorRequestNoAccountIdChromeOS) { #if BUILDFLAG(IS_CHROMEOS_LACROS) - if (!base::FeatureList::IsEnabled(switches::kUseAccountManagerFacade)) { + const crosapi::mojom::BrowserInitParams* init_params = + chromeos::LacrosChromeServiceImpl::Get()->init_params(); + if (!init_params->use_new_account_manager) return; - } #endif account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest( GURL("https://docs.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); CheckMirrorCookieRequest(GURL("https://docs.google.com"), /*gaia_id=*/"", "mode=0:enable_account_consistency=true:" "consistency_enabled_by_default=false"); } -#else // !BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) -#if defined(OS_ANDROID) || defined(OS_IOS) -// Tests that eligible_for_consistency request is returned on mobile (Android, -// iOS) when reaching to Gaia origin and there's no primary account. Only +#else // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if defined(OS_ANDROID) +// Tests that eligible_for_consistency request is returned on Android +// when reaching to Gaia origin and there's no primary account. Only // applicable when the Mobile Identity Consistency is enabled. TEST_F(SigninHeaderHelperTest, TestEligibleForConsistencyRequestGaiaOrigin) { base::test::ScopedFeatureList feature_list; @@ -188,15 +203,15 @@ TEST_F(SigninHeaderHelperTest, TestEligibleForConsistencyRequestGaiaOrigin) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest(GURL("https://accounts.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,eligible_for_consistency=true"); CheckMirrorCookieRequest(GURL("https://accounts.google.com"), /*gaia_id=*/"", "eligible_for_consistency=true"); } -// Tests that eligible_for_consistency request is NOT returned on mobile -// (Android, iOS) when reaching to NON-Gaia origin and there's no primary -// account. Only applicable when the Mobile Identity Consistency is enabled. +// Tests that eligible_for_consistency request is NOT returned on Android +// when reaching to NON-Gaia origin and there's no primary account +// Only applicable when the Mobile Identity Consistency is enabled. TEST_F(SigninHeaderHelperTest, TestNoEligibleForConsistencyRequestNonGaiaOrigin) { base::test::ScopedFeatureList feature_list; @@ -204,7 +219,7 @@ TEST_F(SigninHeaderHelperTest, account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest(GURL("https://docs.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, ""); + /*is_child_account=*/absl::nullopt, ""); CheckMirrorCookieRequest(GURL("https://docs.google.com"), /*gaia_id=*/"", ""); } @@ -218,18 +233,18 @@ TEST_F(SigninHeaderHelperTest, TestForceAccountConsistencyMobile) { force_account_consistency_ = true; CheckMirrorHeaderRequest( GURL("https://docs.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); } -#endif // defined(OS_ANDROID) || defined(OS_IOS) +#endif // defined(OS_ANDROID) // Tests that no Mirror request is returned when the user is not signed in (no // account id), for non Chrome OS platforms. TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestNoAccountId) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest(GURL("https://docs.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, ""); + /*is_child_account=*/absl::nullopt, ""); CheckMirrorCookieRequest(GURL("https://docs.google.com"), /*gaia_id=*/"", ""); } #endif @@ -240,7 +255,7 @@ TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) { account_consistency_ = AccountConsistencyMethod::kMirror; cookie_settings_->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK); CheckMirrorHeaderRequest(GURL("https://docs.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, ""); + /*is_child_account=*/absl::nullopt, ""); CheckMirrorCookieRequest(GURL("https://docs.google.com"), "0123456789", ""); } @@ -248,7 +263,7 @@ TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestCookieSettingBlocked) { TEST_F(SigninHeaderHelperTest, TestNoMirrorRequestExternalURL) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest(GURL("https://foo.com"), "0123456789", - /*is_child_account=*/base::nullopt, ""); + /*is_child_account=*/absl::nullopt, ""); CheckMirrorCookieRequest(GURL("https://foo.com"), "0123456789", ""); } @@ -258,7 +273,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleTLD) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest( GURL("https://google.fr"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); CheckMirrorCookieRequest(GURL("https://google.de"), "0123456789", @@ -272,7 +287,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleCom) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest( GURL("https://www.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); CheckMirrorCookieRequest(GURL("https://www.google.com"), "0123456789", @@ -287,7 +302,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleComNoProfileConsistency) { original_headers); AppendOrRemoveMirrorRequestHeader( request_adapter.adapter(), GURL(), "0123456789", - /*is_child_account=*/base::nullopt, account_consistency_, + /*is_child_account=*/absl::nullopt, account_consistency_, cookie_settings_.get(), PROFILE_MODE_DEFAULT, kTestSource, false /* force_account_consistency */); CheckAccountConsistencyHeaderRequest(request_adapter.GetFinalHeaders(), @@ -302,7 +317,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleComProfileConsistency) { original_headers); AppendOrRemoveMirrorRequestHeader( request_adapter.adapter(), GURL(), "0123456789", - /*is_child_account=*/base::nullopt, account_consistency_, + /*is_child_account=*/absl::nullopt, account_consistency_, cookie_settings_.get(), PROFILE_MODE_DEFAULT, kTestSource, false /* force_account_consistency */); CheckAccountConsistencyHeaderRequest( @@ -315,17 +330,17 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGoogleComSupervised) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckMirrorHeaderRequest( GURL("https://www.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); CheckMirrorHeaderRequest( GURL("https://www.google.com"), "0123456789", - /*is_child_account=*/base::Optional<bool>(true), + /*is_child_account=*/absl::optional<bool>(true), "source=TestSource,mode=0,enable_account_consistency=true," "supervised=true,consistency_enabled_by_default=false"); CheckMirrorHeaderRequest( GURL("https://www.google.com"), "0123456789", - /*is_child_account=*/base::Optional<bool>(false), + /*is_child_account=*/absl::optional<bool>(false), "source=TestSource,mode=0,enable_account_consistency=true," "supervised=false,consistency_enabled_by_default=false"); } @@ -338,7 +353,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) { // No request when account consistency is disabled. account_consistency_ = AccountConsistencyMethod::kDisabled; CheckMirrorHeaderRequest(GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_request=*/""); CheckMirrorCookieRequest(GURL("https://accounts.google.com"), "0123456789", /*expected_request=*/""); @@ -347,7 +362,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) { // No request when Mirror account consistency enabled, but user not signed in // to Chrome. CheckMirrorHeaderRequest(GURL("https://accounts.google.com"), /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_request=*/""); CheckMirrorCookieRequest(GURL("https://accounts.google.com"), /*gaia_id=*/"", /*expected_request=*/""); @@ -356,7 +371,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) { // signed in to Chrome. CheckMirrorHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false"); CheckMirrorCookieRequest(GURL("https://accounts.google.com"), "0123456789", @@ -370,7 +385,7 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) { // ChromeConnected but no Dice for Docs URLs. CheckDiceHeaderRequest( GURL("https://docs.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,id=0123456789,mode=0,enable_account_consistency=false," "consistency_enabled_by_default=false", /*expected_dice_request=*/""); @@ -381,7 +396,7 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) { ASSERT_FALSE(client_id.empty()); CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_mirror_request=*/"", base::StringPrintf( "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts," @@ -392,7 +407,7 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) { sync_enabled_ = true; CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_mirror_request=*/"", base::StringPrintf("version=%s,client_id=%s,device_id=DeviceID," "sync_account_id=0123456789,signin_mode=all_accounts," @@ -402,7 +417,7 @@ TEST_F(SigninHeaderHelperTest, TestDiceRequest) { // No ChromeConnected and no Dice for other URLs. CheckDiceHeaderRequest(GURL("https://www.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_mirror_request=*/"", /*expected_dice_request=*/""); } @@ -416,7 +431,7 @@ TEST_F(SigninHeaderHelperTest, DiceCookiesBlocked) { ASSERT_FALSE(client_id.empty()); CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, "", + /*is_child_account=*/absl::nullopt, "", base::StringPrintf( "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts," "signout_mode=show_confirmation", @@ -428,7 +443,7 @@ TEST_F(SigninHeaderHelperTest, TestNoDiceRequestWhenDisabled) { account_consistency_ = AccountConsistencyMethod::kMirror; CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,mode=0,enable_account_consistency=true," "consistency_enabled_by_default=false", ""); @@ -443,7 +458,7 @@ TEST_F(SigninHeaderHelperTest, TestDiceEmptyDeviceID) { CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_mirror_request=*/"", base::StringPrintf("version=%s,client_id=%s,signin_mode=all_accounts," "signout_mode=show_confirmation", @@ -458,7 +473,7 @@ TEST_F(SigninHeaderHelperTest, TestSignoutConfirmation) { CheckDiceHeaderRequest( GURL("https://accounts.google.com"), "0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_mirror_request=*/"", base::StringPrintf( "version=%s,client_id=%s,device_id=DeviceID,signin_mode=all_accounts," @@ -477,7 +492,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderRequestDriveSignedOut) { AccountConsistencyMethod::kMirror, AccountConsistencyMethod::kDice}) { account_consistency_ = account_consistency; CheckMirrorHeaderRequest(url, /*gaia_id=*/"", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*expected_request=*/""); CheckMirrorCookieRequest(url, /*gaia_id=*/"", /*expected_request=*/""); @@ -494,7 +509,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderRequestDriveSignedIn) { // Request with Gaia ID when Mirror account consistency is enabled and user // is signed in to Chrome. CheckMirrorHeaderRequest(url, /*gaia_id=*/"0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,id=0123456789,mode=0,enable_" "account_consistency=true," "consistency_enabled_by_default=false"); @@ -508,7 +523,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderRequestDriveSignedIn) { // Request with Gaia ID when DICE account consistency is enabled and user is // opted in to sycn. CheckMirrorHeaderRequest(url, /*gaia_id=*/"0123456789", - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, "source=TestSource,id=0123456789,mode=0,enable_" "account_consistency=false," "consistency_enabled_by_default=false"); @@ -658,7 +673,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderEligibleRedirectURL) { RequestAdapterWrapper request_adapter(url, original_headers); AppendOrRemoveMirrorRequestHeader( request_adapter.adapter(), redirect_url, account_id, - /*is_child_account=*/base::nullopt, account_consistency_, + /*is_child_account=*/absl::nullopt, account_consistency_, cookie_settings_.get(), PROFILE_MODE_DEFAULT, kTestSource, false /* force_account_consistency */); EXPECT_TRUE( @@ -677,7 +692,7 @@ TEST_F(SigninHeaderHelperTest, TestMirrorHeaderNonEligibleRedirectURL) { RequestAdapterWrapper request_adapter(url, original_headers); AppendOrRemoveMirrorRequestHeader( request_adapter.adapter(), redirect_url, account_id, - /*is_child_account=*/base::nullopt, account_consistency_, + /*is_child_account=*/absl::nullopt, account_consistency_, cookie_settings_.get(), PROFILE_MODE_DEFAULT, kTestSource, false /* force_account_consistency */); EXPECT_FALSE( @@ -697,7 +712,7 @@ TEST_F(SigninHeaderHelperTest, TestIgnoreMirrorHeaderNonEligibleURLs) { RequestAdapterWrapper request_adapter(url, original_headers); AppendOrRemoveMirrorRequestHeader( request_adapter.adapter(), redirect_url, account_id, - /*is_child_account=*/base::nullopt, account_consistency_, + /*is_child_account=*/absl::nullopt, account_consistency_, cookie_settings_.get(), PROFILE_MODE_DEFAULT, kTestSource, false /* force_account_consistency */); std::string header; @@ -730,7 +745,7 @@ TEST_F(SigninHeaderHelperTest, TestBuildManageAccountsParams) { EXPECT_EQ(true, params.is_saml); EXPECT_EQ(true, params.is_same_tab); EXPECT_EQ(GURL(kContinueURL), params.continue_url); -#if defined(OS_ANDROID) || defined(OS_IOS) +#if defined(OS_ANDROID) EXPECT_EQ(true, params.show_consistency_promo); #endif } diff --git a/chromium/components/signin/core/browser/signin_internals_util.h b/chromium/components/signin/core/browser/signin_internals_util.h index dc6a0e240ea..09e80e87532 100644 --- a/chromium/components/signin/core/browser/signin_internals_util.h +++ b/chromium/components/signin/core/browser/signin_internals_util.h @@ -11,7 +11,6 @@ #include <memory> #include <string> -#include "base/values.h" namespace signin_internals_util { diff --git a/chromium/components/signin/internal/identity_manager/BUILD.gn b/chromium/components/signin/internal/identity_manager/BUILD.gn index 8f54d7a1cc9..171d8a0719b 100644 --- a/chromium/components/signin/internal/identity_manager/BUILD.gn +++ b/chromium/components/signin/internal/identity_manager/BUILD.gn @@ -96,6 +96,14 @@ source_set("identity_manager") { deps += [ "//components/user_manager" ] } else { + if (is_chromeos_lacros) { + # TODO(bsazonov): Simplify this after removing profile_oauth2_token_service_delegate_chromeos_legacy. + sources += [ + "profile_oauth2_token_service_delegate_chromeos.cc", + "profile_oauth2_token_service_delegate_chromeos.h", + ] + deps += [ "//components/account_manager_core" ] + } sources += [ "primary_account_policy_manager_impl.cc", "primary_account_policy_manager_impl.h", @@ -159,6 +167,7 @@ source_set("unit_tests") { "//components/signin/public/base:signin_buildflags", "//components/signin/public/base:test_support", "//components/signin/public/identity_manager", + "//components/signin/public/identity_manager:test_support", "//components/signin/public/webdata", "//components/sync_preferences:test_support", "//components/webdata/common", @@ -173,8 +182,6 @@ source_set("unit_tests") { if (is_android) { sources += [ "profile_oauth2_token_service_delegate_android_unittest.cc" ] - - deps += [ "//components/signin/public/identity_manager:test_support" ] } if (is_chromeos_ash) { diff --git a/chromium/components/signin/internal/identity_manager/account_info_util.cc b/chromium/components/signin/internal/identity_manager/account_info_util.cc index cb77753ba13..7e2d28d222f 100644 --- a/chromium/components/signin/internal/identity_manager/account_info_util.cc +++ b/chromium/components/signin/internal/identity_manager/account_info_util.cc @@ -18,22 +18,22 @@ const char kLocaleKey[] = "locale"; const char kPictureUrlKey[] = "picture"; } // namespace -base::Optional<AccountInfo> AccountInfoFromUserInfo( +absl::optional<AccountInfo> AccountInfoFromUserInfo( const base::Value& user_info) { if (!user_info.is_dict()) - return base::nullopt; + return absl::nullopt; // Both |gaia_id| and |email| are required value in the JSON reply, so // return empty result if any is missing. const base::Value* gaia_id_value = user_info.FindKeyOfType(kGaiaIdKey, base::Value::Type::STRING); if (!gaia_id_value) - return base::nullopt; + return absl::nullopt; const base::Value* email_value = user_info.FindKeyOfType(kEmailKey, base::Value::Type::STRING); if (!email_value) - return base::nullopt; + return absl::nullopt; AccountInfo account_info; account_info.email = email_value->GetString(); diff --git a/chromium/components/signin/internal/identity_manager/account_info_util.h b/chromium/components/signin/internal/identity_manager/account_info_util.h index 53207e5c483..74330a7bbc3 100644 --- a/chromium/components/signin/internal/identity_manager/account_info_util.h +++ b/chromium/components/signin/internal/identity_manager/account_info_util.h @@ -5,13 +5,13 @@ #ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ #define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ -#include "base/optional.h" #include "base/values.h" #include "components/signin/public/identity_manager/account_info.h" +#include "third_party/abseil-cpp/absl/types/optional.h" // Builds an AccountInfo from the JSON data returned by the gaia servers (the // data should have been converted to base::Value), if possible. -base::Optional<AccountInfo> AccountInfoFromUserInfo( +absl::optional<AccountInfo> AccountInfoFromUserInfo( const base::Value& user_info); #endif // COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_ACCOUNT_INFO_UTIL_H_ diff --git a/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc b/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc index cc658a06104..a3aa467d47f 100644 --- a/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/account_info_util_unittest.cc @@ -49,7 +49,7 @@ using AccountInfoUtilTest = PlatformTest; // Tests that AccountInfoFromUserInfo returns an AccountInfo with the value // extracted from the passed base::Value. TEST_F(AccountInfoUtilTest, FromUserInfo) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/"user@example.com", /*gaia=*/"gaia_id_user_example_com", /*hosted_domain=*/"example.com", /*full_name=*/"full name", @@ -71,7 +71,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo) { // Tests that AccountInfoFromUserInfo returns an AccountInfo with empty or // default values if no fields are set in the user_info. TEST_F(AccountInfoUtilTest, FromUserInfo_EmptyValues) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/"", /*gaia=*/"", /*hosted_domain=*/"", /*full_name=*/"", /*given_name=*/"", /*locale=*/"", /*picture_url=*/"")); @@ -92,7 +92,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo_EmptyValues) { // extracted from the passed base::Value, with default value for |hosted_domain| // if missing. TEST_F(AccountInfoUtilTest, FromUserInfo_NoHostedDomain) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/"user@example.com", /*gaia=*/"gaia_id_user_example_com", /*hosted_domain=*/nullptr, /*full_name=*/"full name", @@ -109,7 +109,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo_NoHostedDomain) { // extracted from the passed base::Value, with default value for |picture_url| // if missing. TEST_F(AccountInfoUtilTest, FromUserInfo_NoPictureUrl) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/"user@example.com", /*gaia=*/"gaia_id_user_example_com", /*hosted_domain=*/"example.com", /*full_name=*/"full name", @@ -125,7 +125,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo_NoPictureUrl) { // Tests that if AccountInfoFromUserInfo fails if the value passed has no // value for |email|. TEST_F(AccountInfoUtilTest, FromUserInfo_NoEmail) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/nullptr, /*gaia=*/"gaia_id_user_example_com", /*hosted_domain=*/"example.com", /*full_name=*/"full name", @@ -138,7 +138,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo_NoEmail) { // Tests that if AccountInfoFromUserInfo fails if the value passed has no // value for |gaia|. TEST_F(AccountInfoUtilTest, FromUserInfo_NoGaiaId) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(CreateUserInfoWithValues( /*email=*/"user@example.com", /*gaia=*/nullptr, /*hosted_domain=*/"example.com", /*full_name=*/"full name", @@ -151,7 +151,7 @@ TEST_F(AccountInfoUtilTest, FromUserInfo_NoGaiaId) { // Tests that if AccountInfoFromUserInfo fails if the value passed is not a // dictionary. TEST_F(AccountInfoUtilTest, FromUserInfo_NotADictionary) { - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(base::Value("not a dictionary")); EXPECT_FALSE(maybe_account_info.has_value()); diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc index 68944ea785b..44a587f233e 100644 --- a/chromium/components/signin/internal/identity_manager/account_tracker_service.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service.cc @@ -238,7 +238,7 @@ void AccountTrackerService::SetAccountInfoFromUserInfo( DCHECK(base::Contains(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; - base::Optional<AccountInfo> maybe_account_info = + absl::optional<AccountInfo> maybe_account_info = AccountInfoFromUserInfo(*user_info); if (maybe_account_info) { // Should we DCHECK that the account stored in |accounts_| has the same diff --git a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc index e52e1547813..276f18b3f51 100644 --- a/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/account_tracker_service_unittest.cc @@ -36,7 +36,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_ANDROID) -#include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" #endif namespace { @@ -177,7 +177,9 @@ class AccountTrackerServiceTest : public testing::Test { : signin_client_(&pref_service_), fake_oauth2_token_service_(&pref_service_) { #if defined(OS_ANDROID) - ChildAccountInfoFetcherAndroid::InitializeForTests(); + // Mock AccountManagerFacade in java code for tests that require its + // initialization. + signin::SetUpMockAccountManagerFacade(); #endif AccountTrackerService::RegisterPrefs(pref_service_.registry()); diff --git a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc index d03c005f806..8ce9809a78d 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.cc @@ -116,4 +116,13 @@ void AccountsCookieMutatorImpl::LogOutAllAccounts( source, std::move(completion_callback)); } +void AccountsCookieMutatorImpl::RemoveLoggedOutAccountByGaiaId( + const std::string& gaia_id) { + // Note that RemoveLoggedOutAccountByGaiaId() does NOT internally trigger a + // ListAccounts fetch. It could make sense to force a request here, e.g. via + // ForceOnCookieChangeProcessing(), but this isn't considered important enough + // to justify the risk for overloading the server. + gaia_cookie_manager_service_->RemoveLoggedOutAccountByGaiaId(gaia_id); +} + } // namespace signin diff --git a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h index cf8dc8e6d5b..d98f0afc47a 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h +++ b/chromium/components/signin/internal/identity_manager/accounts_cookie_mutator_impl.h @@ -69,6 +69,8 @@ class AccountsCookieMutatorImpl : public AccountsCookieMutator { gaia::GaiaSource source, LogOutFromCookieCompletedCallback completion_callback) override; + void RemoveLoggedOutAccountByGaiaId(const std::string& gaia_id) override; + private: class MultiloginHelperWrapper : public SetAccountsInCookieTask { public: diff --git a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc index ab5c2d7499e..f939edfcec3 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.cc @@ -4,7 +4,6 @@ #include "components/signin/internal/identity_manager/accounts_mutator_impl.h" -#include "base/optional.h" #include "build/chromeos_buildflags.h" #include "components/prefs/pref_service.h" #include "components/signin/internal/identity_manager/account_tracker_service.h" @@ -15,6 +14,7 @@ #include "components/signin/public/identity_manager/account_info.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_constants.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace signin { @@ -63,8 +63,8 @@ CoreAccountId AccountsMutatorImpl::AddOrUpdateAccount( void AccountsMutatorImpl::UpdateAccountInfo( const CoreAccountId& account_id, - base::Optional<bool> is_child_account, - base::Optional<bool> is_under_advanced_protection) { + absl::optional<bool> is_child_account, + absl::optional<bool> is_under_advanced_protection) { if (is_child_account.has_value()) { account_tracker_service_->SetIsChildAccount(account_id, is_child_account.value()); diff --git a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h index 742b026de0b..19cc2c096ae 100644 --- a/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h +++ b/chromium/components/signin/internal/identity_manager/accounts_mutator_impl.h @@ -42,8 +42,8 @@ class AccountsMutatorImpl : public AccountsMutator { signin_metrics::SourceForRefreshTokenOperation source) override; void UpdateAccountInfo( const CoreAccountId& account_id, - base::Optional<bool> is_child_account, - base::Optional<bool> is_under_advanced_protection) override; + absl::optional<bool> is_child_account, + absl::optional<bool> is_under_advanced_protection) override; void RemoveAccount( const CoreAccountId& account_id, signin_metrics::SourceForRefreshTokenOperation source) override; diff --git a/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc index e8bad35a5bf..b59fe1cd5e5 100644 --- a/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc +++ b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.cc @@ -31,11 +31,6 @@ ChildAccountInfoFetcherAndroid::Create(AccountFetcherService* service, new ChildAccountInfoFetcherAndroid(service, account_info)); } -void ChildAccountInfoFetcherAndroid::InitializeForTests() { - signin::Java_ChildAccountInfoFetcher_initializeForTests( - base::android::AttachCurrentThread()); -} - ChildAccountInfoFetcherAndroid::ChildAccountInfoFetcherAndroid( AccountFetcherService* service, const CoreAccountInfo& account_info) { diff --git a/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h index e51b1334922..864e7347945 100644 --- a/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h +++ b/chromium/components/signin/internal/identity_manager/child_account_info_fetcher_android.h @@ -6,7 +6,6 @@ #define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_CHILD_ACCOUNT_INFO_FETCHER_ANDROID_H_ #include <jni.h> -#include <string> #include "base/android/scoped_java_ref.h" #include "components/signin/public/identity_manager/account_info.h" diff --git a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc index 2d732c23a33..f284e6a8ca0 100644 --- a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc +++ b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc @@ -19,7 +19,7 @@ DeviceAccountsSynchronizerImpl::~DeviceAccountsSynchronizerImpl() = default; void DeviceAccountsSynchronizerImpl:: ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) { + const absl::optional<CoreAccountId>& primary_account_id) { token_service_delegate_->ReloadAllAccountsFromSystemWithPrimaryAccount( primary_account_id); } diff --git a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h index 0ee9d2fdd56..e2283bc8dde 100644 --- a/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h +++ b/chromium/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h @@ -21,7 +21,7 @@ class DeviceAccountsSynchronizerImpl : public DeviceAccountsSynchronizer { // DeviceAccountsSynchronizer implementation. void ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) override; + const absl::optional<CoreAccountId>& primary_account_id) override; #if defined(OS_IOS) void ReloadAccountFromSystem(const CoreAccountId& account_id) override; diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc index 773aa6063e7..cb65ddf82c9 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.cc @@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/callback_helpers.h" #include "base/json/json_reader.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -99,6 +100,11 @@ void RecordLogoutRequestState(LogoutRequestState logout_state) { UMA_HISTOGRAM_ENUMERATION("Signin.GaiaCookieManager.Logout", logout_state); } +void RecordRemoveLocalAccountOutcome( + GaiaCookieManagerService::RemoveLocalAccountOutcome outcome) { + base::UmaHistogramEnumeration("Signin.RemoveLocalAccountOutcome", outcome); +} + } // namespace GaiaCookieManagerService::GaiaCookieRequest::SetAccountsParams:: @@ -663,6 +669,36 @@ void GaiaCookieManagerService::LogOutAllAccounts( } } +void GaiaCookieManagerService::RemoveLoggedOutAccountByGaiaId( + const std::string& gaia_id) { + VLOG(1) << "GaiaCookieManagerService::RemoveLoggedOutAccountByGaiaId"; + + if (list_accounts_stale_) { + RecordRemoveLocalAccountOutcome(RemoveLocalAccountOutcome::kAccountsStale); + return; + } + + const bool accounts_updated = + base::EraseIf(signed_out_accounts_, + [&gaia_id](const gaia::ListedAccount& account) { + return account.gaia_id == gaia_id; + }) != 0; + + if (!accounts_updated) { + RecordRemoveLocalAccountOutcome( + RemoveLocalAccountOutcome::kSignedOutAccountMissing); + return; + } + + RecordRemoveLocalAccountOutcome(RemoveLocalAccountOutcome::kSuccess); + + if (gaia_accounts_updated_in_cookie_callback_) { + gaia_accounts_updated_in_cookie_callback_.Run( + listed_accounts_, signed_out_accounts_, + GoogleServiceAuthError(GoogleServiceAuthError::NONE)); + } +} + void GaiaCookieManagerService::CancelAll() { VLOG(1) << "GaiaCookieManagerService::CancelAll"; gaia_auth_fetcher_.reset(); diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h index b1c7391940a..201bc6c0674 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service.h @@ -8,7 +8,6 @@ #include <map> #include <memory> #include <string> -#include <unordered_map> #include <utility> #include <vector> @@ -69,6 +68,19 @@ class GaiaCookieManagerService SET_ACCOUNTS }; + // The result of processing a request to remove an account (i.e. + // Google-Accounts-RemoveLocalAccount). Used as entry for histogram + // |Signin.RemoveLocalAccountOutcome|, hence entries should not be renumbered + // and numeric values should never be reused. Exposed publicly for testing + // purposes. + enum class RemoveLocalAccountOutcome { + kSuccess = 0, + kAccountsStale = 1, + // Missing means the account is not listed in |signed_out_accounts_|. + kSignedOutAccountMissing = 2, + kMaxValue = kSignedOutAccountMissing + }; + typedef base::OnceCallback<void(signin::SetAccountsInCookieResult)> SetAccountsInCookieCompletedCallback; typedef base::OnceCallback<void(const CoreAccountId&, @@ -271,6 +283,11 @@ class GaiaCookieManagerService void LogOutAllAccounts(gaia::GaiaSource source, LogOutFromCookieCompletedCallback callback); + // Indicates that an account previously listed via ListAccounts should now + // be removed. Does not trigger a ListAccounts request and does not change the + // staleness of the account information. + void RemoveLoggedOutAccountByGaiaId(const std::string& gaia_id); + // Call observers when setting accounts in cookie completes. void SignalSetAccountsComplete(signin::SetAccountsInCookieResult result); diff --git a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc index bbe4939cd8c..3326598ac54 100644 --- a/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc +++ b/chromium/components/signin/internal/identity_manager/gaia_cookie_manager_service_unittest.cc @@ -98,6 +98,11 @@ MATCHER_P(ListedAccountEquals, expected, "") { return AreAccountListsEqual(expected, arg); } +// Custom matcher for ListedAccount. +MATCHER_P(ListedAccountMatchesGaiaId, gaia_id, "") { + return arg.gaia_id == std::string(gaia_id); +} + class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { public: InstrumentedGaiaCookieManagerService(ProfileOAuth2TokenService* token_service, @@ -248,6 +253,7 @@ class GaiaCookieManagerServiceTest : public testing::Test { } // namespace using ::testing::_; +using ::testing::ElementsAre; TEST_F(GaiaCookieManagerServiceTest, Success) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); @@ -1097,3 +1103,147 @@ TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCCOnce) { EXPECT_CALL(helper, StartFetchingMergeSession()); SimulateUbertokenSuccess(&helper, "token3"); } + +TEST_F(GaiaCookieManagerServiceTest, RemoveLoggedOutAccountByGaiaId) { + const std::string kTestGaiaId1 = "8"; + const std::string kTestGaiaId2 = "9"; + + ::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper( + token_service(), signin_client()); + ::testing::NiceMock<MockObserver> observer(&helper); + + std::vector<gaia::ListedAccount> signed_in_accounts; + std::vector<gaia::ListedAccount> signed_out_accounts; + ASSERT_FALSE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + + // Simulate two signed out accounts being listed. + SimulateListAccountsSuccess( + &helper, + base::StringPrintf( + "[\"f\"," + "[[\"a\", 0, \"n\", \"a@d.com\", \"p\", 0, 0, 0, 0, 1, \"%s\"," + "null,null,null,1]," + "[\"b\", 0, \"n\", \"b@d.com\", \"p\", 0, 0, 0, 0, 1, \"%s\"," + "null,null,null,1]]]", + kTestGaiaId1.c_str(), kTestGaiaId2.c_str())); + + ASSERT_TRUE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + ASSERT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId1), + ListedAccountMatchesGaiaId(kTestGaiaId2))); + + // The removal should notify observers, with one account removed. + EXPECT_CALL(observer, + OnGaiaAccountsInCookieUpdated( + _, /*signed_out_accounts=*/ + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId2)), _)); + EXPECT_CALL(helper, StartFetchingListAccounts()).Times(0); + base::HistogramTester histograms; + helper.RemoveLoggedOutAccountByGaiaId(kTestGaiaId1); + + // Verify that ListAccounts wasn't triggered. + EXPECT_FALSE(helper.is_running()); + EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&helper)); + + ASSERT_TRUE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + EXPECT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId2))); + + histograms.ExpectUniqueSample( + "Signin.RemoveLocalAccountOutcome", + GaiaCookieManagerService::RemoveLocalAccountOutcome::kSuccess, 1); +} + +TEST_F(GaiaCookieManagerServiceTest, + RemoveLoggedOutAccountByGaiaIdWhileAccountsStale) { + const std::string kTestGaiaId1 = "8"; + + ::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper( + token_service(), signin_client()); + ::testing::NiceMock<MockObserver> observer(&helper); + + std::vector<gaia::ListedAccount> signed_in_accounts; + std::vector<gaia::ListedAccount> signed_out_accounts; + ASSERT_FALSE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + + // Simulate one signed out account being listed. + SimulateListAccountsSuccess( + &helper, + base::StringPrintf( + "[\"f\"," + "[[\"a\", 0, \"n\", \"a@d.com\", \"p\", 0, 0, 0, 0, 1, \"%s\"," + "null,null,null,1]]]", + kTestGaiaId1.c_str())); + + // Change list account state to be stale, which will trigger list accounts + // request. + helper.ForceOnCookieChangeProcessing(); + + ASSERT_FALSE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + ASSERT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId1))); + + // The removal should be ignored because the account list is stale. + EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated(_, _, _)).Times(0); + EXPECT_CALL(helper, StartFetchingListAccounts()).Times(0); + base::HistogramTester histograms; + helper.RemoveLoggedOutAccountByGaiaId(kTestGaiaId1); + + // Verify that ListAccounts wasn't triggered again. + EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&helper)); + + ASSERT_FALSE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + EXPECT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId1))); + + histograms.ExpectUniqueSample( + "Signin.RemoveLocalAccountOutcome", + GaiaCookieManagerService::RemoveLocalAccountOutcome::kAccountsStale, 1); +} + +TEST_F(GaiaCookieManagerServiceTest, + RemoveLoggedOutAccountByGaiaIdForMissingAccount) { + const std::string kTestGaiaId1 = "8"; + const std::string kNonListedAccount = "9"; + + ::testing::NiceMock<InstrumentedGaiaCookieManagerService> helper( + token_service(), signin_client()); + ::testing::NiceMock<MockObserver> observer(&helper); + + std::vector<gaia::ListedAccount> signed_in_accounts; + std::vector<gaia::ListedAccount> signed_out_accounts; + ASSERT_FALSE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + + // Simulate one signed out account being listed. + SimulateListAccountsSuccess( + &helper, + base::StringPrintf( + "[\"f\"," + "[[\"a\", 0, \"n\", \"a@d.com\", \"p\", 0, 0, 0, 0, 1, \"%s\"," + "null,null,null,1]]]", + kTestGaiaId1.c_str())); + + ASSERT_TRUE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + ASSERT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId1))); + + // The removal should be ignored because the Gaia ID is not listed/known. + EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated(_, _, _)).Times(0); + EXPECT_CALL(helper, StartFetchingListAccounts()).Times(0); + base::HistogramTester histograms; + helper.RemoveLoggedOutAccountByGaiaId(kNonListedAccount); + + // Verify that ListAccounts wasn't triggered. + EXPECT_FALSE(helper.is_running()); + EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&helper)); + + ASSERT_TRUE(helper.ListAccounts(&signed_in_accounts, &signed_out_accounts)); + EXPECT_THAT(signed_out_accounts, + ElementsAre(ListedAccountMatchesGaiaId(kTestGaiaId1))); + + histograms.ExpectUniqueSample( + "Signin.RemoveLocalAccountOutcome", + GaiaCookieManagerService::RemoveLocalAccountOutcome:: + kSignedOutAccountMissing, + 1); +} diff --git a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc index 26d866036d9..82a6d6698bb 100644 --- a/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc +++ b/chromium/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate.cc @@ -50,30 +50,6 @@ enum class LoadTokenFromDBStatus { NUM_LOAD_TOKEN_FROM_DB_STATUS }; -// Used to record events related to token revocation requests in histograms. -// Do not change existing values, new values can only be added at the end. -enum class TokenRevocationRequestProgress { - // The request was created. - kRequestCreated = 0, - // The request was sent over the network. - kRequestStarted = 1, - // The network request completed with a failure. - kRequestFailed = 2, - // The network request completed with a success. - kRequestSucceeded = 3, - - kMaxValue = kRequestSucceeded -}; - -// Adds a sample to the TokenRevocationRequestProgress histogram. Encapsuled in -// a function to reduce executable size, because histogram macros may generate a -// lot of code. -void RecordRefreshTokenRevocationRequestEvent( - TokenRevocationRequestProgress event) { - UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationRequestProgress", - event); -} - std::string ApplyAccountIdPrefix(const std::string& account_id) { return kAccountIdPrefix + account_id; } @@ -166,8 +142,6 @@ MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: token_service_delegate_->GetURLLoaderFactory()), refresh_token_(refresh_token), attempt_(attempt) { - RecordRefreshTokenRevocationRequestEvent( - TokenRevocationRequestProgress::kRequestCreated); client->DelayNetworkCall( base::BindRepeating(&MutableProfileOAuth2TokenServiceDelegate:: RevokeServerRefreshToken::Start, @@ -176,8 +150,6 @@ MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: Start() { - RecordRefreshTokenRevocationRequestEvent( - TokenRevocationRequestProgress::kRequestStarted); fetcher_.StartRevokeOAuth2Token(refresh_token_); } @@ -207,18 +179,11 @@ bool MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: void MutableProfileOAuth2TokenServiceDelegate::RevokeServerRefreshToken:: OnOAuth2RevokeTokenCompleted( GaiaAuthConsumer::TokenRevocationStatus status) { - UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationStatus", status); if (ShouldRetry(status)) { token_service_delegate_->server_revokes_.push_back( std::make_unique<RevokeServerRefreshToken>( token_service_delegate_, token_service_delegate_->client_, refresh_token_, attempt_ + 1)); - } else { - RecordRefreshTokenRevocationRequestEvent( - (status == GaiaAuthConsumer::TokenRevocationStatus::kSuccess) - ? TokenRevocationRequestProgress::kRequestSucceeded - : TokenRevocationRequestProgress::kRequestFailed); - UMA_HISTOGRAM_ENUMERATION("Signin.RefreshTokenRevocationCompleted", status); } // |this| pointer will be deleted when removed from the vector, so don't // access any members after call to erase(). diff --git a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h index d60787cc73e..f64019b108f 100644 --- a/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h +++ b/chromium/components/signin/internal/identity_manager/oauth_multilogin_helper.h @@ -19,7 +19,6 @@ #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "net/cookies/cookie_access_result.h" -#include "services/network/public/mojom/cookie_manager.mojom.h" class GaiaAuthFetcher; class GoogleServiceAuthError; diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc index 332632058a7..14095b440a4 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_manager.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.cc @@ -56,6 +56,7 @@ void PrimaryAccountManager::RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterBooleanPref(prefs::kAutologinEnabled, true); registry->RegisterListPref(prefs::kReverseAutologinRejectedEmailList); registry->RegisterBooleanPref(prefs::kSigninAllowed, true); + registry->RegisterBooleanPref(prefs::kSigninAllowedByPolicy, true); registry->RegisterBooleanPref(prefs::kSignedInWithCredentialProvider, false); } @@ -353,7 +354,7 @@ void PrimaryAccountManager::OnSignoutDecisionReached( break; case RemoveAccountsOption::kKeepAllAccounts: if (previous_state.consent_level == signin::ConsentLevel::kSignin) { - // Nothing to update as the primary account is already at kNotRequired + // Nothing to update as the primary account is already at kSignin // consent level. Prefer returning to avoid firing useless // OnPrimaryAccountChanged() notifications. return; diff --git a/chromium/components/signin/internal/identity_manager/primary_account_manager.h b/chromium/components/signin/internal/identity_manager/primary_account_manager.h index 700d8217dca..a87da779070 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_manager.h +++ b/chromium/components/signin/internal/identity_manager/primary_account_manager.h @@ -19,18 +19,17 @@ #define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MANAGER_H_ #include <memory> -#include <string> #include "base/macros.h" #include "base/observer_list.h" #include "base/observer_list_types.h" -#include "base/optional.h" #include "build/chromeos_buildflags.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_observer.h" #include "components/signin/public/base/signin_client.h" #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/primary_account_change_event.h" +#include "third_party/abseil-cpp/absl/types/optional.h" class AccountTrackerService; class PrefRegistrySimple; diff --git a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc index 83900c87bc1..b61786ed770 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.cc @@ -46,40 +46,41 @@ PrimaryAccountMutatorImpl::PrimaryAccountMutatorImpl( PrimaryAccountMutatorImpl::~PrimaryAccountMutatorImpl() {} bool PrimaryAccountMutatorImpl::SetPrimaryAccount( - const CoreAccountId& account_id) { + const CoreAccountId& account_id, + ConsentLevel consent_level) { + DCHECK(!account_id.empty()); AccountInfo account_info = account_tracker_->GetAccountInfo(account_id); - -#if !BUILDFLAG(IS_CHROMEOS_ASH) - if (!pref_service_->GetBoolean(prefs::kSigninAllowed)) + if (account_info.IsEmpty()) return false; - if (primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSync)) - return false; + DCHECK_EQ(account_info.account_id, account_id); + DCHECK(!account_info.email.empty()); + DCHECK(!account_info.gaia.empty()); - if (account_info.account_id != account_id || account_info.email.empty()) +#if !BUILDFLAG(IS_CHROMEOS_ASH) + if (!pref_service_->GetBoolean(prefs::kSigninAllowed)) return false; - - // TODO(crbug.com/889899): should check that the account email is allowed. #endif - primary_account_manager_->SetSyncPrimaryAccountInfo(account_info); - return true; -} - -void PrimaryAccountMutatorImpl::SetUnconsentedPrimaryAccount( - const CoreAccountId& account_id) { + switch (consent_level) { + case ConsentLevel::kSync: +#if !BUILDFLAG(IS_CHROMEOS_ASH) + if (primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSync)) + return false; +#endif + primary_account_manager_->SetSyncPrimaryAccountInfo(account_info); + return true; + case ConsentLevel::kSignin: #if BUILDFLAG(IS_CHROMEOS_ASH) - // On Chrome OS the UPA can only be set once and never removed or changed. - DCHECK(!account_id.empty()); - DCHECK(!primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSignin)); + // On Chrome OS the UPA can only be set once and never removed or changed. + DCHECK( + !primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSignin)); #endif - AccountInfo account_info; - if (!account_id.empty()) { - account_info = account_tracker_->GetAccountInfo(account_id); - DCHECK(!account_info.IsEmpty()); + DCHECK(!primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSync)); + primary_account_manager_->SetUnconsentedPrimaryAccountInfo(account_info); + return true; } - - primary_account_manager_->SetUnconsentedPrimaryAccountInfo(account_info); + return false; } #if !BUILDFLAG(IS_CHROMEOS_ASH) diff --git a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h index 99b74257624..0fe82594a61 100644 --- a/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h +++ b/chromium/components/signin/internal/identity_manager/primary_account_mutator_impl.h @@ -5,8 +5,6 @@ #ifndef COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_IMPL_H_ #define COMPONENTS_SIGNIN_INTERNAL_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_IMPL_H_ -#include <string> - #include "build/chromeos_buildflags.h" #include "components/signin/public/base/account_consistency_method.h" #include "components/signin/public/identity_manager/primary_account_mutator.h" @@ -31,8 +29,8 @@ class PrimaryAccountMutatorImpl : public PrimaryAccountMutator { ~PrimaryAccountMutatorImpl() override; // PrimaryAccountMutator implementation. - bool SetPrimaryAccount(const CoreAccountId& account_id) override; - void SetUnconsentedPrimaryAccount(const CoreAccountId& account_id) override; + bool SetPrimaryAccount(const CoreAccountId& account_id, + ConsentLevel consent_level) override; void RevokeSyncConsent(signin_metrics::ProfileSignout source_metric, signin_metrics::SignoutDelete delete_metric) override; #if !BUILDFLAG(IS_CHROMEOS_ASH) diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc index 2757404a0d7..5e9d4c37529 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc @@ -31,6 +31,10 @@ #include "components/user_manager/user_manager.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_LACROS) +#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h" +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) + #if defined(OS_IOS) #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h" #include "components/signin/public/identity_manager/ios/device_accounts_provider.h" @@ -74,6 +78,19 @@ std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate( is_regular_profile); } #elif BUILDFLAG(ENABLE_DICE_SUPPORT) + +#if BUILDFLAG(IS_CHROMEOS_LACROS) +std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate( + AccountTrackerService* account_tracker_service, + network::NetworkConnectionTracker* network_connection_tracker, + account_manager::AccountManagerFacade* account_manager_facade, + bool is_regular_profile) { + return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>( + account_tracker_service, network_connection_tracker, + account_manager_facade, is_regular_profile); +} +#endif // BUILDFLAG(IS_CHROMEOS_LACROS) + std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate> CreateMutableProfileOAuthDelegate( AccountTrackerService* account_tracker_service, @@ -111,9 +128,11 @@ CreateOAuth2TokenServiceDelegate( SigninClient* signin_client, #if BUILDFLAG(IS_CHROMEOS_ASH) ash::AccountManager* account_manager, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) account_manager::AccountManagerFacade* account_manager_facade, bool is_regular_profile, -#endif +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) #if !defined(OS_ANDROID) bool delete_signin_cookies_on_exit, scoped_refptr<TokenWebData> token_web_data, @@ -136,9 +155,24 @@ CreateOAuth2TokenServiceDelegate( return CreateCrOsOAuthDelegate(account_tracker_service, network_connection_tracker, account_manager, account_manager_facade, is_regular_profile); +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + // For the time being, Mirror is enabled only in the first / "Main" Profile in + // Lacros. + if (account_consistency == signin::AccountConsistencyMethod::kMirror) { + return CreateCrOsOAuthDelegate(account_tracker_service, + network_connection_tracker, + account_manager_facade, is_regular_profile); + } else { + // TODO(crbug.com/1198490): Remove this when we don't need DICE and + // `MutableProfileOAuth2TokenServiceDelegate` on Lacros anymore. + return CreateMutableProfileOAuthDelegate( + account_tracker_service, account_consistency, + delete_signin_cookies_on_exit, token_web_data, signin_client, + network_connection_tracker); + } #elif BUILDFLAG(ENABLE_DICE_SUPPORT) // Fall back to |MutableProfileOAuth2TokenServiceDelegate| on all platforms - // other than Android, iOS, and Chrome OS. + // other than Android, iOS, and Chrome OS (Ash and Lacros). return CreateMutableProfileOAuthDelegate( account_tracker_service, account_consistency, delete_signin_cookies_on_exit, token_web_data, signin_client, @@ -161,9 +195,11 @@ std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( signin::AccountConsistencyMethod account_consistency, #if BUILDFLAG(IS_CHROMEOS_ASH) ash::AccountManager* account_manager, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) account_manager::AccountManagerFacade* account_manager_facade, bool is_regular_profile, -#endif +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) #if !defined(OS_ANDROID) bool delete_signin_cookies_on_exit, scoped_refptr<TokenWebData> token_web_data, @@ -190,8 +226,11 @@ std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( CreateOAuth2TokenServiceDelegate( account_tracker_service, account_consistency, signin_client, #if BUILDFLAG(IS_CHROMEOS_ASH) - account_manager, account_manager_facade, is_regular_profile, -#endif + account_manager, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + account_manager_facade, is_regular_profile, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) #if !defined(OS_ANDROID) delete_signin_cookies_on_exit, token_web_data, #endif diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h index 1e7659c9f54..baaaa27fa04 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.h @@ -43,11 +43,13 @@ class TokenWebData; namespace ash { class AccountManager; } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) namespace account_manager { class AccountManagerFacade; } -#endif +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( PrefService* pref_service, @@ -56,9 +58,11 @@ std::unique_ptr<ProfileOAuth2TokenService> BuildProfileOAuth2TokenService( signin::AccountConsistencyMethod account_consistency, #if BUILDFLAG(IS_CHROMEOS_ASH) ash::AccountManager* account_manager, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) account_manager::AccountManagerFacade* account_manager_facade, bool is_regular_profile, -#endif +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) #if !defined(OS_ANDROID) bool delete_signin_cookies_on_exit, scoped_refptr<TokenWebData> token_web_data, diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h index 999e6461529..9e3156a4167 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h @@ -19,6 +19,7 @@ #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_manager.h" #include "net/base/backoff_entry.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #if defined(OS_ANDROID) #include "base/android/jni_android.h" @@ -129,7 +130,7 @@ class ProfileOAuth2TokenServiceDelegate { #if defined(OS_IOS) || defined(OS_ANDROID) // Triggers platform specific implementation to reload accounts from system. virtual void ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) {} + const absl::optional<CoreAccountId>& primary_account_id) {} #endif #if defined(OS_IOS) diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc index c7872b8b7ae..0de4283023b 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.cc @@ -310,7 +310,7 @@ void ProfileOAuth2TokenServiceDelegateAndroid::OnAccessTokenInvalidated( void ProfileOAuth2TokenServiceDelegateAndroid:: ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) { + const absl::optional<CoreAccountId>& primary_account_id) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_account_id = @@ -326,7 +326,7 @@ void ProfileOAuth2TokenServiceDelegateAndroid:: ReloadAllAccountsWithPrimaryAccountAfterSeeding( JNIEnv* env, const base::android::JavaParamRef<jstring>& account_id) { - base::Optional<CoreAccountId> core_account_id; + absl::optional<CoreAccountId> core_account_id; if (account_id) { core_account_id = CoreAccountId::FromString(ConvertJavaStringToUTF8(env, account_id)); @@ -335,7 +335,7 @@ void ProfileOAuth2TokenServiceDelegateAndroid:: } void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList( - const base::Optional<CoreAccountId>& signed_in_account_id, + const absl::optional<CoreAccountId>& signed_in_account_id, const std::vector<CoreAccountId>& prev_ids, const std::vector<CoreAccountId>& curr_ids) { DVLOG(1) << "ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList:" @@ -388,7 +388,7 @@ void ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList( } bool ProfileOAuth2TokenServiceDelegateAndroid::UpdateAccountList( - const base::Optional<CoreAccountId>& signed_in_id, + const absl::optional<CoreAccountId>& signed_in_id, const std::vector<CoreAccountId>& prev_ids, const std::vector<CoreAccountId>& curr_ids, std::vector<CoreAccountId>* refreshed_ids, diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h index 95196ec1410..46ed5cdd374 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_android.h @@ -51,7 +51,7 @@ class ProfileOAuth2TokenServiceDelegateAndroid void LoadCredentials(const CoreAccountId& primary_account_id) override; void ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) override; + const absl::optional<CoreAccountId>& primary_account_id) override; // Resumes the reload of accounts once the account seeding is complete. // TODO(crbug.com/934688) Once ProfileOAuth2TokenServiceDelegate.java is @@ -65,7 +65,7 @@ class ProfileOAuth2TokenServiceDelegateAndroid // NOTE: TokenAvailable notifications will be sent for all accounts, even if // they were already known. See https://crbug.com/939470 for details. void UpdateAccountList( - const base::Optional<CoreAccountId>& signed_in_account_id, + const absl::optional<CoreAccountId>& signed_in_account_id, const std::vector<CoreAccountId>& prev_ids, const std::vector<CoreAccountId>& curr_ids); @@ -100,7 +100,7 @@ class ProfileOAuth2TokenServiceDelegateAndroid // Return whether accounts are valid and we have access to all the tokens in // |curr_ids|. - bool UpdateAccountList(const base::Optional<CoreAccountId>& signed_in_id, + bool UpdateAccountList(const absl::optional<CoreAccountId>& signed_in_id, const std::vector<CoreAccountId>& prev_ids, const std::vector<CoreAccountId>& curr_ids, std::vector<CoreAccountId>* refreshed_ids, diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc index 7c773db3497..eff34b2c6e5 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc @@ -11,6 +11,8 @@ #include "base/bind.h" #include "base/containers/contains.h" #include "base/logging.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" #include "components/signin/internal/identity_manager/account_tracker_service.h" #include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h" #include "net/base/backoff_entry.h" @@ -98,6 +100,7 @@ class PersistentErrorsHelper : public base::RefCounted<PersistentErrorsHelper> { // No accounts to get error status for, run callback immediately. std::move(callback).Run( std::map<account_manager::AccountKey, GoogleServiceAuthError>()); + return; } // The ownership of this object is shared between callbacks passed to @@ -318,9 +321,21 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::LoadCredentials( void ProfileOAuth2TokenServiceDelegateChromeOS::UpdateCredentials( const CoreAccountId& account_id, const std::string& refresh_token) { +#if BUILDFLAG(IS_CHROMEOS_LACROS) + NOTREACHED() + << "If you're seeing this error in a browser_test, consider " + "disabling the test while we set up the testing " + "infrastructure to talk to Ash in a browser_test. Also, please add a " + "comment / CL ref. to crbug.com/1197201 if you disable your test."; + // TODO(sinhak): We need a way to write accounts to Account Manager in + // browser_tests and lacros_chrome_browsertests. For browser_tests, the + // solution may be to build Account Manager in Lacros. For + // lacros_chrome_browsertests, we will need to talk to EngProd. +#else // UpdateCredentials should not be called on Chrome OS. Credentials should be // updated through Chrome OS Account Manager. NOTREACHED(); +#endif } scoped_refptr<network::SharedURLLoaderFactory> @@ -502,12 +517,12 @@ void ProfileOAuth2TokenServiceDelegateChromeOS::OnAccountRemoved( void ProfileOAuth2TokenServiceDelegateChromeOS::RevokeCredentials( const CoreAccountId& account_id) { - // Signing out of Chrome is not possible on Chrome OS. + // Signing out of Chrome is not possible on Chrome OS Ash / Lacros. NOTREACHED(); } void ProfileOAuth2TokenServiceDelegateChromeOS::RevokeAllCredentials() { - // Signing out of Chrome is not possible on Chrome OS. + // Signing out of Chrome is not possible on Chrome OS Ash / Lacros. NOTREACHED(); } diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h index 3cac71d9c1f..ef414ca0ee3 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h @@ -54,7 +54,7 @@ class ProfileOAuth2TokenServiceIOSDelegate void RevokeAllCredentials() override; void ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) override; + const absl::optional<CoreAccountId>& primary_account_id) override; void ReloadAccountFromSystem(const CoreAccountId& account_id) override; // Adds |account_id| to |accounts_| if it does not exist or udpates diff --git a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm index 62c473e0b2c..cde6ddfd83a 100644 --- a/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm +++ b/chromium/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm @@ -294,7 +294,7 @@ void ProfileOAuth2TokenServiceIOSDelegate::RevokeAllCredentials() { void ProfileOAuth2TokenServiceIOSDelegate:: ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) { + const absl::optional<CoreAccountId>& primary_account_id) { ReloadCredentials(primary_account_id.value_or(CoreAccountId())); } diff --git a/chromium/components/signin/ios/DIR_METADATA b/chromium/components/signin/ios/DIR_METADATA deleted file mode 100644 index 5fad6891646..00000000000 --- a/chromium/components/signin/ios/DIR_METADATA +++ /dev/null @@ -1,3 +0,0 @@ -monorail { - component: "Services>SignIn" -} diff --git a/chromium/components/signin/ios/browser/account_consistency_service.h b/chromium/components/signin/ios/browser/account_consistency_service.h index ca895d52b10..7220a445097 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service.h +++ b/chromium/components/signin/ios/browser/account_consistency_service.h @@ -7,7 +7,6 @@ #include <map> #include <set> -#include <string> #include "base/callback.h" #include "base/macros.h" @@ -52,14 +51,6 @@ class AccountConsistencyService : public KeyedService, // Removes the handler associated with |web_state|. void RemoveWebStateHandler(web::WebState* web_state); - // Checks for the presence of Gaia cookies and if they have been deleted - // notifies the AccountReconcilor (the class responsible for rebuilding Gaia - // cookies if needed). Calls callback if Gaia cookies were restored. - // - // Applies a one hour time restriction in between updates to avoid too many - // |GetAllCookies| calls on the cookie manager. - void SetGaiaCookiesIfDeleted(base::OnceClosure cookies_restored_callback); - // Notifies the AccountReconcilor that Gaia cookies have been deleted. Calls // callback once the Gaia cookies have been restored and returns YES on // success. Note that in order to avoid redirect loops this method applies a @@ -100,16 +91,6 @@ class AccountConsistencyService : public KeyedService, void OnDeleteCookiesFinished(base::OnceClosure callback, uint32_t num_cookies_deleted); - // Triggers a Gaia cookie update on the Google domain. Calls - // |cookies_restored_callback| if the Gaia cookies were restored. - void TriggerGaiaCookieChangeIfDeleted( - base::OnceClosure cookies_restored_callback, - const net::CookieAccessResultList& cookie_list, - const net::CookieAccessResultList& excluded_cookies); - - // Clears all pending cookie requests and cached domains. - void ResetInternalState(); - // IdentityManager::Observer implementation. void OnPrimaryAccountChanged( const signin::PrimaryAccountChangeEvent& event) override; @@ -134,7 +115,6 @@ class AccountConsistencyService : public KeyedService, int64_t active_cookie_manager_requests_for_testing_; // Last time Gaia cookie was updated for the Google domain. - base::Time last_gaia_cookie_verification_time_; base::Time last_gaia_cookie_update_time_; // List of callbacks to be called following GAIA cookie restoration. diff --git a/chromium/components/signin/ios/browser/account_consistency_service.mm b/chromium/components/signin/ios/browser/account_consistency_service.mm index 5ff4951ab1d..0e46b18de4c 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service.mm @@ -136,14 +136,10 @@ class AccountConsistencyService::AccountConsistencyHandler base::OnceCallback<void(PolicyDecision)> callback) override; void WebStateDestroyed() override; - // Marks that GAIA cookies have been restored. - void MarkGaiaCookiesRestored(); - // Loads |url| in the current tab. void NavigateToURL(GURL url); bool show_consistency_promo_ = false; - bool gaia_cookies_restored_ = false; AccountConsistencyService* account_consistency_service_; // Weak. AccountReconcilor* account_reconcilor_; // Weak. signin::IdentityManager* identity_manager_; @@ -199,38 +195,36 @@ void AccountConsistencyService::AccountConsistencyHandler::ShouldAllowResponse( GURL url = net::GURLWithNSURL(http_response.URL); // User is showing intent to navigate to a Google-owned domain. Set GAIA and - // CHROME_CONNECTED cookies if the user is signed in or if they are not signed - // in and navigating to a GAIA sign-on (this is filtered in + // CHROME_CONNECTED cookies if the user is signed in (this is filtered in // ChromeConnectedHelper). if (signin::IsUrlEligibleForMirrorCookie(url)) { account_consistency_service_->SetChromeConnectedCookieWithUrls( {url, GURL(kGoogleUrl)}); } - // Chrome monitors GAIA cookies when navigating to Google associated domains - // to ensure that signed-in users remain signed-in to their Google services on - // the web. This includes redirects to accounts.google.com. - if (google_util::IsGoogleAssociatedDomainUrl(url)) { - // TODO(crbug.com/1131027): Disable GAIA cookie restore on Google URLs that - // may display cookie consent in the content area that conflict with the - // sign-in notification. This will be removed once we perform cookie - // restoration before sending a navigation request. - if (!(google_util::IsGoogleHomePageUrl(url) || - google_util::IsGoogleSearchUrl(url))) { - // Reset boolean that tracks displaying the sign-in notification infobar. - // This ensures that only the most recent navigation will trigger an - // infobar. - gaia_cookies_restored_ = false; - account_consistency_service_->SetGaiaCookiesIfDeleted( - base::BindOnce(&AccountConsistencyHandler::MarkGaiaCookiesRestored, - weak_ptr_factory_.GetWeakPtr())); - } - } + // Reset boolean that tracks displaying the sign-in consistency promo. This + // ensures that the promo is cancelled once navigation has started and the + // WKWebView is cancelling previous navigations. + show_consistency_promo_ = false; if (!gaia::IsGaiaSignonRealm(url.GetOrigin())) { std::move(callback).Run(PolicyDecision::Allow()); return; } + + // If the user has been prompted to enter their credentials on a Gaia sign-on + // page show the account consistency promo. + NSString* x_autologin_header = [[http_response allHeaderFields] + objectForKey:[NSString stringWithUTF8String:signin::kAutoLoginHeader]]; + if (signin::IsMICEWebSignInEnabled() && x_autologin_header) { + show_consistency_promo_ = true; + // Allows the URL response to load before showing the consistency promo. + // The promo should always be displayed in the foreground of Gaia + // sign-on. + std::move(callback).Run(PolicyDecision::Allow()); + return; + } + NSString* manage_accounts_header = [[http_response allHeaderFields] objectForKey: [NSString stringWithUTF8String:signin::kChromeManageAccountsHeader]]; @@ -243,10 +237,6 @@ void AccountConsistencyService::AccountConsistencyHandler::ShouldAllowResponse( base::SysNSStringToUTF8(manage_accounts_header)); account_reconcilor_->OnReceivedManageAccountsResponse(params.service_type); - // Reset boolean that tracks displaying the sign-in consistency promo. This - // ensures that the promo is cancelled once navigation has started and the - // WKWebView is cancelling previous navigations. - show_consistency_promo_ = false; switch (params.service_type) { case signin::GAIA_SERVICE_TYPE_INCOGNITO: { @@ -278,16 +268,7 @@ void AccountConsistencyService::AccountConsistencyHandler::ShouldAllowResponse( } } } - if (params.show_consistency_promo) { - show_consistency_promo_ = true; - // Allows the URL response to load before showing the consistency promo. - // The promo should always be displayed in the foreground of Gaia - // sign-on. - std::move(callback).Run(PolicyDecision::Allow()); - return; - } else { - [delegate_ onAddAccount]; - } + [delegate_ onAddAccount]; break; case signin::GAIA_SERVICE_TYPE_SIGNOUT: case signin::GAIA_SERVICE_TYPE_DEFAULT: @@ -308,11 +289,6 @@ void AccountConsistencyService::AccountConsistencyHandler::ShouldAllowResponse( std::move(callback).Run(PolicyDecision::Cancel()); } -void AccountConsistencyService::AccountConsistencyHandler:: - MarkGaiaCookiesRestored() { - gaia_cookies_restored_ = true; -} - void AccountConsistencyService::AccountConsistencyHandler::NavigateToURL( GURL url) { web_state_->OpenURL(web::WebState::OpenURLParams( @@ -334,26 +310,9 @@ void AccountConsistencyService::AccountConsistencyHandler::PageLoaded( return; } - // Displays the sign-in notification infobar if GAIA cookies have been - // restored. This occurs once the URL has been loaded to avoid a race - // condition in which the infobar is dismissed prior to the page load. - if (gaia_cookies_restored_) { - [delegate_ onRestoreGaiaCookies]; - LogIOSGaiaCookiesState( - GaiaCookieStateOnSignedInNavigation::kGaiaCookieRestoredOnShowInfobar); - gaia_cookies_restored_ = false; - } - if (show_consistency_promo_ && gaia::IsGaiaSignonRealm(url.GetOrigin())) { - [delegate_ onShowConsistencyPromo]; + [delegate_ onShowConsistencyPromo:url]; show_consistency_promo_ = false; - - // Chrome uses the CHROME_CONNECTED cookie to determine whether the - // eligibility promo should be shown. Once it is shown we should remove the - // cookie, since it should otherwise not be used unless the user is signed - // in. - account_consistency_service_->RemoveAllChromeConnectedCookies( - base::OnceClosure()); } } @@ -423,55 +382,6 @@ void AccountConsistencyService::RemoveWebStateHandler( web_state->RemoveObserver(handler.get()); } -void AccountConsistencyService::SetGaiaCookiesIfDeleted( - base::OnceClosure cookies_restored_callback) { - // We currently enforce a time threshold to update the Gaia cookie - // for signed-in users to prevent calling the expensive method - // |GetAllCookies| in the cookie manager. - if (base::Time::Now() - last_gaia_cookie_verification_time_ < - GetDelayThresholdToUpdateGaiaCookie() || - !identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin)) { - return; - } - network::mojom::CookieManager* cookie_manager = - browser_state_->GetCookieManager(); - cookie_manager->GetCookieList( - GaiaUrls::GetInstance()->secure_google_url(), - net::CookieOptions::MakeAllInclusive(), - base::BindOnce( - &AccountConsistencyService::TriggerGaiaCookieChangeIfDeleted, - base::Unretained(this), std::move(cookies_restored_callback))); - last_gaia_cookie_verification_time_ = base::Time::Now(); -} - -void AccountConsistencyService::TriggerGaiaCookieChangeIfDeleted( - base::OnceClosure cookies_restored_callback, - const net::CookieAccessResultList& cookie_list, - const net::CookieAccessResultList& unused_excluded_cookies) { - for (const auto& cookie : cookie_list) { - if (cookie.cookie.Name() == GaiaConstants::kGaiaSigninCookieName) { - LogIOSGaiaCookiesState( - GaiaCookieStateOnSignedInNavigation::kGaiaCookiePresentOnNavigation); - return; - } - } - - // The SAPISID cookie may have been deleted previous to this update due to - // ITP restrictions marking Google domains as potential trackers. - LogIOSGaiaCookiesState( - GaiaCookieStateOnSignedInNavigation:: - kGaiaCookieAbsentOnGoogleAssociatedDomainNavigation); - - if (!base::FeatureList::IsEnabled(signin::kRestoreGaiaCookiesIfDeleted)) { - return; - } - - // Re-generate cookie to ensure that the user is properly signed in. - identity_manager_->GetAccountsCookieMutator()->ForceTriggerOnCookieChange(); - gaia_cookies_restored_callbacks_.push_back( - std::move(cookies_restored_callback)); -} - void AccountConsistencyService::RemoveAllChromeConnectedCookies( base::OnceClosure callback) { DCHECK(!browser_state_->IsOffTheRecord()); @@ -488,7 +398,6 @@ void AccountConsistencyService::RemoveAllChromeConnectedCookies( std::move(filter), base::BindOnce(&AccountConsistencyService::OnDeleteCookiesFinished, base::Unretained(this), std::move(callback))); - last_gaia_cookie_verification_time_ = base::Time(); } void AccountConsistencyService::OnDeleteCookiesFinished( @@ -518,7 +427,7 @@ void AccountConsistencyService::SetChromeConnectedCookieWithUrl( const std::string domain = GetDomainFromUrl(url); std::string cookie_value = signin::BuildMirrorRequestCookieIfPossible( url, - identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSync) + identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin) .gaia, signin::AccountConsistencyMethod::kMirror, cookie_settings_.get(), signin::PROFILE_MODE_DEFAULT); @@ -572,10 +481,6 @@ void AccountConsistencyService::AddChromeConnectedCookies() { } void AccountConsistencyService::OnBrowsingDataRemoved() { - // CHROME_CONNECTED cookies have been removed, update internal state - // accordingly. - last_gaia_cookie_verification_time_ = base::Time(); - // SAPISID cookie has been removed, notify the GCMS. // TODO(https://crbug.com/930582) : Remove the need to expose this method // or move it to the network::CookieManager. diff --git a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm index 6c1f3807ffe..f87374e3423 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm @@ -195,7 +195,8 @@ class AccountConsistencyServiceTest : public PlatformTest { // Identity APIs. void SignIn() { signin::MakePrimaryAccountAvailable(identity_test_env_->identity_manager(), - kFakeEmail); + kFakeEmail, + signin::ConsentLevel::kSync); WaitUntilAllCookieRequestsAreApplied(); } @@ -225,9 +226,8 @@ class AccountConsistencyServiceTest : public PlatformTest { // Verifies the time that the Gaia cookie was last updated for google.com. void CheckGaiaCookieWithUpdateTime(base::Time time) { - EXPECT_EQ( - time, - account_consistency_service_->last_gaia_cookie_verification_time_); + EXPECT_EQ(time, + account_consistency_service_->last_gaia_cookie_update_time_); } // Navigation APIs. @@ -280,7 +280,7 @@ class AccountConsistencyServiceTest : public PlatformTest { network::mojom::CookieDeletionFilterPtr filter = network::mojom::CookieDeletionFilter::New(); filter->including_domains = - base::Optional<std::vector<std::string>>({kGoogleDomain}); + absl::optional<std::vector<std::string>>({kGoogleDomain}); cookie_manager->DeleteCookies(std::move(filter), base::OnceCallback<void(uint)>()); } @@ -456,25 +456,25 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsDefault) { } // Tests that the ManageAccountsDelegate is notified when a navigation on Gaia -// signon realm returns with a X-Chrome-Manage-Accounts header with show -// consistency promo and ADDSESSION action. -TEST_F(AccountConsistencyServiceTest, - ChromeManageAccountsShowConsistencyPromo) { +// signon realm returns with a X-Auto-Login header. +TEST_F(AccountConsistencyServiceTest, ChromeShowConsistencyPromo) { + base::test::ScopedFeatureList consistency_feature_list; + consistency_feature_list.InitAndEnableFeature( + signin::kMobileIdentityConsistency); + base::test::ScopedFeatureList websignin_feature_list; + websignin_feature_list.InitAndEnableFeature(signin::kMICEWebSignIn); + id delegate = [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; - [[delegate expect] onShowConsistencyPromo]; + [[[delegate expect] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()]; - NSDictionary* headers = [NSDictionary - dictionaryWithObject:@"action=ADDSESSION,show_consistency_promo=true" - forKey:@"X-Chrome-Manage-Accounts"]; + NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused" + forKey:@"X-Auto-Login"]; NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers]; - EXPECT_CALL(*account_reconcilor_, OnReceivedManageAccountsResponse( - signin::GAIA_SERVICE_TYPE_ADDSESSION)) - .Times(1); SimulateNavigateToURL(response, delegate); @@ -483,22 +483,20 @@ TEST_F(AccountConsistencyServiceTest, // Tests that the consistency promo is not displayed when a page fails to load. TEST_F(AccountConsistencyServiceTest, - ChromeManageAccountsNotShowConsistencyPromoOnPageLoadFailure) { + ChromeNotShowConsistencyPromoOnPageLoadFailure) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(signin::kMobileIdentityConsistency); id delegate = [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; - [[delegate reject] onShowConsistencyPromo]; + [[[delegate reject] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()]; - NSDictionary* headers = [NSDictionary - dictionaryWithObject:@"action=ADDSESSION,show_consistency_promo=true" - forKey:@"X-Chrome-Manage-Accounts"]; + NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused" + forKey:@"X-Auto-Login"]; NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers]; - EXPECT_CALL(*account_reconcilor_, OnReceivedManageAccountsResponse( - signin::GAIA_SERVICE_TYPE_ADDSESSION)) - .Times(1); SimulateNavigateToURLWithPageLoadFailure(response, delegate); @@ -508,19 +506,20 @@ TEST_F(AccountConsistencyServiceTest, // Tests that the consistency promo is not displayed when a page fails to load // and user chooses another action. TEST_F(AccountConsistencyServiceTest, - ChromeManageAccountsNotShowConsistencyPromoOnPageLoadFailureRedirect) { + ChromeNotShowConsistencyPromoOnPageLoadFailureRedirect) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(signin::kMobileIdentityConsistency); + id delegate = [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; [[delegate expect] onAddAccount]; - [[delegate reject] onShowConsistencyPromo]; + [[[delegate reject] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()]; EXPECT_CALL(*account_reconcilor_, OnReceivedManageAccountsResponse( - signin::GAIA_SERVICE_TYPE_ADDSESSION)) - .Times(2); + signin::GAIA_SERVICE_TYPE_ADDSESSION)); - NSDictionary* headers = [NSDictionary - dictionaryWithObject:@"action=ADDSESSION,show_consistency_promo=true" - forKey:@"X-Chrome-Manage-Accounts"]; + NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused" + forKey:@"X-Auto-Login"]; NSHTTPURLResponse* responseSignin = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] statusCode:200 @@ -543,28 +542,6 @@ TEST_F(AccountConsistencyServiceTest, EXPECT_OCMOCK_VERIFY(delegate); } -// Tests that the consistency promo is not displayed when a non GAIA URL is -// committed. -TEST_F(AccountConsistencyServiceTest, - ChromeManageAccountsNotShowConsistencyPromoOnNonGaiaURL) { - id delegate = - [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; - [[delegate reject] onShowConsistencyPromo]; - - NSDictionary* headers = [NSDictionary - dictionaryWithObject:@"action=ADDSESSION,show_consistency_promo=true" - forKey:@"X-Chrome-Manage-Accounts"]; - NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] - initWithURL:[NSURL URLWithString:@"https://youtube.com//"] - statusCode:200 - HTTPVersion:@"HTTP/1.1" - headerFields:headers]; - - SimulateNavigateToURL(response, delegate); - - EXPECT_OCMOCK_VERIFY(delegate); -} - // Tests that the ManageAccountsDelegate is notified when a navigation on Gaia // signon realm returns with a X-Chrome-Manage-Accounts header with ADDSESSION // action. @@ -673,74 +650,6 @@ TEST_F(AccountConsistencyServiceTest, SetChromeConnectedCookie) { CheckDomainHasChromeConnectedCookie(kYoutubeDomain); } -// Tests that the GAIA cookie update time is not updated before the scheduled -// interval. -TEST_F(AccountConsistencyServiceTest, SetGaiaCookieUpdateNotUpdateTime) { - SignIn(); - - // HTTP response URL is eligible for Mirror (the test does not use google.com - // since the CHROME_CONNECTED cookie is generated for it by default. - NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] - initWithURL:[NSURL URLWithString:@"https://youtube.com"] - statusCode:200 - HTTPVersion:@"HTTP/1.1" - headerFields:@{}]; - - SimulateNavigateToURL(response, nil); - - // Advance clock, but stay within the one-hour Gaia update time. - base::TimeDelta oneMinuteDelta = base::TimeDelta::FromMinutes(1); - task_environment_.FastForwardBy(oneMinuteDelta); - SimulateNavigateToURL(response, nil); - - CheckGaiaCookieWithUpdateTime(base::Time::Now() - oneMinuteDelta); -} - -// Tests that the GAIA cookie update time is updated at the scheduled interval. -TEST_F(AccountConsistencyServiceTest, SetGaiaCookieUpdateAtUpdateTime) { - SignIn(); - - // HTTP response URL is eligible for Mirror (the test does not use google.com - // since the CHROME_CONNECTED cookie is generated for it by default. - NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] - initWithURL:[NSURL URLWithString:@"https://youtube.com"] - statusCode:200 - HTTPVersion:@"HTTP/1.1" - headerFields:@{}]; - - SimulateNavigateToURL(response, nil); - - // Advance clock past one-hour Gaia update time. - task_environment_.FastForwardBy(base::TimeDelta::FromHours(2)); - SimulateNavigateToURL(response, nil); - - CheckGaiaCookieWithUpdateTime(base::Time::Now()); -} - -// Ensures that the presence or absence of GAIA cookies is logged even if the -// |kRestoreGAIACookiesIfDeleted| experiment is disabled. -TEST_F(AccountConsistencyServiceTest, GAIACookieStatusLoggedProperly) { - // HTTP response URL is eligible for Mirror (the test does not use google.com - // since the CHROME_CONNECTED cookie is generated for it by default. - NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] - initWithURL:[NSURL URLWithString:@"https://youtube.com"] - statusCode:200 - HTTPVersion:@"HTTP/1.1" - headerFields:@{}]; - - base::HistogramTester histogram_tester; - histogram_tester.ExpectTotalCount(kGAIACookieOnNavigationHistogram, 0); - - SimulateNavigateToURL(response, nil); - base::RunLoop().RunUntilIdle(); - histogram_tester.ExpectTotalCount(kGAIACookieOnNavigationHistogram, 0); - - SignIn(); - SimulateNavigateToURL(response, nil); - base::RunLoop().RunUntilIdle(); - histogram_tester.ExpectTotalCount(kGAIACookieOnNavigationHistogram, 1); -} - // Tests that navigating to accounts.google.com without a GAIA cookie is logged // by the navigation histogram. TEST_F(AccountConsistencyServiceTest, GAIACookieMissingOnSignin) { @@ -817,7 +726,7 @@ TEST_F(AccountConsistencyServiceTest, SetChromeConnectedCookiesAfterDelete) { // is signed out and navigating to google.com for |kMobileIdentityConsistency| // experiment. TEST_F(AccountConsistencyServiceTest, - SetMiceChromeConnectedCookiesSignedOutGoogleVisitor) { + SetChromeConnectedCookiesSignedOutGoogleVisitor) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(signin::kMobileIdentityConsistency); id delegate = @@ -840,21 +749,21 @@ TEST_F(AccountConsistencyServiceTest, EXPECT_OCMOCK_VERIFY(delegate); } -// Ensures that CHROME_CONNECTED cookies are only set on GAIA urls when the user -// is signed out and taps sign-in button for |kMobileIdentityConsistency| -// experiment. These cookies are immediately removed after the sign-in promo is -// shown. +// Ensures that CHROME_CONNECTED cookies are not set when the user is signed out +// after the sign-in promo is shown. TEST_F(AccountConsistencyServiceTest, - SetMiceChromeConnectedCookiesSignedOutGaiaVisitor) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(signin::kMobileIdentityConsistency); + SetChromeConnectedCookiesSignedOutGaiaVisitor) { + base::test::ScopedFeatureList consistency_feature_list; + consistency_feature_list.InitAndEnableFeature( + signin::kMobileIdentityConsistency); + base::test::ScopedFeatureList websignin_feature_list; + websignin_feature_list.InitAndEnableFeature(signin::kMICEWebSignIn); id delegate = [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; - [[delegate expect] onShowConsistencyPromo]; + [[[delegate expect] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()]; - NSDictionary* headers = [NSDictionary - dictionaryWithObject:@"action=ADDSESSION,show_consistency_promo=true" - forKey:@"X-Chrome-Manage-Accounts"]; + NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused" + forKey:@"X-Auto-Login"]; NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] statusCode:200 @@ -865,11 +774,61 @@ TEST_F(AccountConsistencyServiceTest, EXPECT_TRUE(web_state_.ShouldAllowResponse(response, /* for_main_frame = */ true)); - CheckDomainHasChromeConnectedCookie("accounts.google.com"); - web_state_.SetCurrentURL(net::GURLWithNSURL(response.URL)); web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS); CheckNoChromeConnectedCookies(); EXPECT_OCMOCK_VERIFY(delegate); } + +TEST_F(AccountConsistencyServiceTest, SetGaiaCookieUpdateBeforeDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(signin::kRestoreGaiaCookiesOnUserAction); + + SignIn(); + + NSDictionary* headers = + [NSDictionary dictionaryWithObject:@"action=ADDSESSION" + forKey:@"X-Chrome-Manage-Accounts"]; + NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] + initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] + statusCode:200 + HTTPVersion:@"HTTP/1.1" + headerFields:headers]; + + SimulateNavigateToURL(response, nil); + + // Advance clock, but stay within the one-hour Gaia update time. + base::TimeDelta oneMinuteDelta = base::TimeDelta::FromMinutes(1); + task_environment_.FastForwardBy(oneMinuteDelta); + SimulateNavigateToURLWithInterruption(response, nil); + + // Does not process the second Gaia restore event. + CheckGaiaCookieWithUpdateTime(base::Time::Now() - oneMinuteDelta); +} + +TEST_F(AccountConsistencyServiceTest, SetGaiaCookieUpdateAfterDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(signin::kRestoreGaiaCookiesOnUserAction); + + SignIn(); + + NSDictionary* headers = + [NSDictionary dictionaryWithObject:@"action=ADDSESSION" + forKey:@"X-Chrome-Manage-Accounts"]; + NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] + initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"] + statusCode:200 + HTTPVersion:@"HTTP/1.1" + headerFields:headers]; + + SimulateNavigateToURL(response, nil); + + // Advance clock past the one-hour Gaia update time. + base::TimeDelta twoHourDelta = base::TimeDelta::FromHours(2); + task_environment_.FastForwardBy(twoHourDelta); + SimulateNavigateToURL(response, nil); + + // Will process the second Gaia restore event, since it is past the delay. + CheckGaiaCookieWithUpdateTime(base::Time::Now()); +} diff --git a/chromium/components/signin/ios/browser/features.cc b/chromium/components/signin/ios/browser/features.cc index 8d2e2601f9a..a6e86425ff8 100644 --- a/chromium/components/signin/ios/browser/features.cc +++ b/chromium/components/signin/ios/browser/features.cc @@ -16,9 +16,6 @@ bool ForceStartupSigninPromo() { return base::FeatureList::IsEnabled(kForceStartupSigninPromo); } -const base::Feature kRestoreGaiaCookiesIfDeleted{ - "RestoreGAIACookiesIfDeleted", base::FEATURE_DISABLED_BY_DEFAULT}; - const base::Feature kRestoreGaiaCookiesOnUserAction{ "RestoreGAIACookiesOnUserAction", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/components/signin/ios/browser/features.h b/chromium/components/signin/ios/browser/features.h index 89efdcecc00..3ee40d81779 100644 --- a/chromium/components/signin/ios/browser/features.h +++ b/chromium/components/signin/ios/browser/features.h @@ -18,9 +18,6 @@ extern const base::Feature kSimplifySignOutIOS; // Returns true if the startup sign-in promo should be displayed at boot. bool ForceStartupSigninPromo(); -// Feature controlling whether to restore GAIA cookies if they are deleted. -extern const base::Feature kRestoreGaiaCookiesIfDeleted; - // Feature controlling whether to restore GAIA cookies when the user explicitly // requests to sign in to a Google service. extern const base::Feature kRestoreGaiaCookiesOnUserAction; diff --git a/chromium/components/signin/ios/browser/manage_accounts_delegate.h b/chromium/components/signin/ios/browser/manage_accounts_delegate.h index 763d93944cc..637b30f492c 100644 --- a/chromium/components/signin/ios/browser/manage_accounts_delegate.h +++ b/chromium/components/signin/ios/browser/manage_accounts_delegate.h @@ -22,7 +22,9 @@ class GURL; // Called when the user taps a sign-in or add account button in a Google web // property with signin::kMobileIdentityConsistency enabled. -- (void)onShowConsistencyPromo; +// |url| is the continuation URL received from the server. If it is valid, +// then this delegate should navigate to |url|. +- (void)onShowConsistencyPromo:(const GURL&)url; // Called when the user taps on go incognito button in a Google web property. // |url| is the continuation URL received from the server. If it is valid, diff --git a/chromium/components/signin/public/android/BUILD.gn b/chromium/components/signin/public/android/BUILD.gn index 06f18bf999e..f25e8537171 100644 --- a/chromium/components/signin/public/android/BUILD.gn +++ b/chromium/components/signin/public/android/BUILD.gn @@ -5,9 +5,11 @@ android_library("java") { "$google_play_services_package:google_play_services_auth_base_java", "$google_play_services_package:google_play_services_base_java", "//base:base_java", + "//components/externalauth/android:java", "//net/android:net_java", "//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:chromium_play_services_availability_java", + "//third_party/android_deps:guava_android_java", "//third_party/androidx:androidx_annotation_annotation_java", ] @@ -19,23 +21,17 @@ android_library("java") { sources = [ "java/src/org/chromium/components/signin/AccessTokenData.java", "java/src/org/chromium/components/signin/AccountManagerDelegate.java", - "java/src/org/chromium/components/signin/AccountManagerDelegateException.java", "java/src/org/chromium/components/signin/AccountManagerFacade.java", "java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java", "java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", - "java/src/org/chromium/components/signin/AccountManagerResult.java", "java/src/org/chromium/components/signin/AccountRenameChecker.java", - "java/src/org/chromium/components/signin/AccountTrackerService.java", + "java/src/org/chromium/components/signin/AccountRestrictionPatternReceiver.java", "java/src/org/chromium/components/signin/AccountUtils.java", "java/src/org/chromium/components/signin/AccountsChangeObserver.java", "java/src/org/chromium/components/signin/AuthException.java", "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java", "java/src/org/chromium/components/signin/ChildAccountStatus.java", "java/src/org/chromium/components/signin/ConnectionRetry.java", - "java/src/org/chromium/components/signin/GmsAvailabilityException.java", - "java/src/org/chromium/components/signin/GmsJustUpdatedException.java", - "java/src/org/chromium/components/signin/MutableObservableValue.java", - "java/src/org/chromium/components/signin/ObservableValue.java", "java/src/org/chromium/components/signin/PatternMatcher.java", "java/src/org/chromium/components/signin/ProfileDataSource.java", "java/src/org/chromium/components/signin/SystemAccountManagerDelegate.java", @@ -44,6 +40,7 @@ android_library("java") { "java/src/org/chromium/components/signin/base/CoreAccountInfo.java", "java/src/org/chromium/components/signin/base/GoogleServiceAuthError.java", "java/src/org/chromium/components/signin/identitymanager/AccountInfoService.java", + "java/src/org/chromium/components/signin/identitymanager/AccountTrackerService.java", "java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", "java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", "java/src/org/chromium/components/signin/identitymanager/PrimaryAccountChangeEvent.java", @@ -57,12 +54,12 @@ generate_jni("jni_headers") { namespace = "signin" sources = [ "java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java", - "java/src/org/chromium/components/signin/AccountTrackerService.java", "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java", "java/src/org/chromium/components/signin/base/AccountInfo.java", "java/src/org/chromium/components/signin/base/CoreAccountId.java", "java/src/org/chromium/components/signin/base/CoreAccountInfo.java", "java/src/org/chromium/components/signin/base/GoogleServiceAuthError.java", + "java/src/org/chromium/components/signin/identitymanager/AccountTrackerService.java", "java/src/org/chromium/components/signin/identitymanager/IdentityManager.java", "java/src/org/chromium/components/signin/identitymanager/IdentityMutator.java", "java/src/org/chromium/components/signin/identitymanager/PrimaryAccountChangeEvent.java", @@ -83,6 +80,7 @@ android_library("signin_java_test_support") { ":test_support_jni_headers", "//base:base_java", "//base:jni_java", + "//third_party/android_deps:guava_android_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/mockito:mockito_java", ] @@ -129,10 +127,9 @@ java_library("junit") { sources = [ "junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java", "junit/src/org/chromium/components/signin/AccountRenameCheckerTest.java", - "junit/src/org/chromium/components/signin/AccountTrackerServiceTest.java", - "junit/src/org/chromium/components/signin/ObservableValueTest.java", "junit/src/org/chromium/components/signin/PatternMatcherTest.java", "junit/src/org/chromium/components/signin/identitymanager/AccountInfoServiceTest.java", + "junit/src/org/chromium/components/signin/identitymanager/AccountTrackerServiceTest.java", ] deps = [ ":java", @@ -141,8 +138,13 @@ java_library("junit") { "//base:base_java", "//base:base_java_test_support", "//base:base_junit_test_support", + "//components/externalauth/android:google_delegate_public_impl_java", + "//components/externalauth/android:java", "//testing/android/junit:junit_test_support", + "//third_party/android_deps:guava_android_java", "//third_party/android_deps:robolectric_all_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_test_rules_java", "//third_party/junit", "//third_party/mockito:mockito_java", ] diff --git a/chromium/components/signin/public/android/DEPS b/chromium/components/signin/public/android/DEPS index b8ac3035f71..944f0130c2e 100644 --- a/chromium/components/signin/public/android/DEPS +++ b/chromium/components/signin/public/android/DEPS @@ -1,3 +1,4 @@ include_rules = [ + "+components/externalauth/android/java/src/org/chromium/components/externalauth/ExternalAuthUtils.java", "+content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestThreadUtils.java", ] diff --git a/chromium/components/signin/public/android/DIR_METADATA b/chromium/components/signin/public/android/DIR_METADATA deleted file mode 100644 index 4735e25f37d..00000000000 --- a/chromium/components/signin/public/android/DIR_METADATA +++ /dev/null @@ -1,5 +0,0 @@ -monorail { - component: "Services>SignIn" -} - -team_email: "chrome-signin@chromium.org" diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java index 2da2805f72a..07343569a1d 100644 --- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java +++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountManagerFacadeImplTest.java @@ -4,18 +4,21 @@ package org.chromium.components.signin; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import android.Manifest; import android.accounts.Account; +import android.accounts.AccountManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.UserManager; +import androidx.test.rule.GrantPermissionRule; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -24,87 +27,95 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAccountManager; +import org.robolectric.shadows.ShadowUserManager; +import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.UmaRecorder; import org.chromium.base.metrics.UmaRecorderHolder; import org.chromium.base.task.test.CustomShadowAsyncTask; import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.CallbackHelper; +import org.chromium.components.externalauth.ExternalAuthUtils; import org.chromium.components.signin.AccountManagerFacade.ChildAccountStatusListener; import org.chromium.components.signin.test.util.AccountHolder; import org.chromium.components.signin.test.util.FakeAccountManagerDelegate; -import org.chromium.testing.local.CustomShadowUserManager; import java.util.List; -import java.util.concurrent.TimeoutException; /** * Robolectric tests for {@link AccountManagerFacade}. See also {@link AccountManagerFacadeTest}. */ @RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE, - shadows = {CustomShadowAsyncTask.class, CustomShadowUserManager.class}) +@Config(shadows = {CustomShadowAsyncTask.class, ShadowUserManager.class, + ShadowAccountManager.class}) public class AccountManagerFacadeImplTest { private static final String TEST_TOKEN_SCOPE = "test-token-scope"; - private CustomShadowUserManager mShadowUserManager; - private FakeAccountManagerDelegate mDelegate; - private AccountManagerFacade mFacade; + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + public final GrantPermissionRule mGrantPermissionRule = + GrantPermissionRule.grant(Manifest.permission.GET_ACCOUNTS); @Mock private UmaRecorder mUmaRecorderMock; + @Mock + ExternalAuthUtils mExternalAuthUtilsMock; + + @Mock + private AccountsChangeObserver mObserverMock; + + @Mock + private ChildAccountStatusListener mChildAccountStatusListenerMock; + + private final Context mContext = RuntimeEnvironment.application; + private ShadowUserManager mShadowUserManager; + private ShadowAccountManager mShadowAccountManager; + private FakeAccountManagerDelegate mDelegate; + private AccountManagerFacade mFacade; + + // Prefer to use the facade with the real system delegate instead of the fake delegate + // to test the facade more thoroughly + private AccountManagerFacade mFacadeWithSystemDelegate; + @Before public void setUp() { UmaRecorderHolder.setNonNativeDelegate(mUmaRecorderMock); - Context context = RuntimeEnvironment.application; - UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mShadowUserManager = (CustomShadowUserManager) shadowOf(userManager); + when(mExternalAuthUtilsMock.canUseGooglePlayServices()).thenReturn(true); + ExternalAuthUtils.setInstanceForTesting(mExternalAuthUtilsMock); + + mShadowUserManager = + shadowOf((UserManager) mContext.getSystemService(Context.USER_SERVICE)); + mShadowAccountManager = shadowOf(AccountManager.get(mContext)); + ThreadUtils.setThreadAssertsDisabledForTesting(true); mDelegate = new FakeAccountManagerDelegate(); mFacade = new AccountManagerFacadeImpl(mDelegate); - } - private void setAccountRestrictionPatterns(String... patterns) { - Bundle restrictions = new Bundle(); - restrictions.putStringArray( - AccountManagerFacadeImpl.ACCOUNT_RESTRICTION_PATTERNS_KEY, patterns); - mShadowUserManager.setApplicationRestrictions( - RuntimeEnvironment.application.getPackageName(), restrictions); - RuntimeEnvironment.application.sendBroadcast( - new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)); - } - - private void clearAccountRestrictionPatterns() { - mShadowUserManager.setApplicationRestrictions( - RuntimeEnvironment.application.getPackageName(), new Bundle()); - RuntimeEnvironment.application.sendBroadcast( - new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)); + mFacadeWithSystemDelegate = + new AccountManagerFacadeImpl(new SystemAccountManagerDelegate()); } @Test - public void testRegisterObserversCalledInConstructor() { - FakeAccountManagerDelegate delegate = spy(new FakeAccountManagerDelegate()); - verify(delegate, never()).registerObservers(); - AccountManagerFacade accountManagerFacade = new AccountManagerFacadeImpl(delegate); - verify(delegate).registerObservers(); + public void testAccountsChangerObservationInitialization() { + mFacadeWithSystemDelegate.addObserver(mObserverMock); + verify(mObserverMock, never()).onAccountsChanged(); + + mContext.sendBroadcast(new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + + verify(mObserverMock).onAccountsChanged(); } @Test public void testCountOfAccountLoggedAfterAccountsFetched() { addTestAccount("test@gmail.com"); + AccountManagerFacade facade = new AccountManagerFacadeImpl(mDelegate); - CallbackHelper callbackHelper = new CallbackHelper(); - facade.runAfterCacheIsPopulated(() -> callbackHelper.notifyCalled()); - try { - callbackHelper.waitForFirst(); - } catch (TimeoutException e) { - throw new RuntimeException("Timed out waiting for callback", e); - } + verify(mUmaRecorderMock) .recordLinearHistogram("Signin.AndroidNumberOfDeviceAccounts", 1, 1, 50, 51); } @@ -112,7 +123,7 @@ public class AccountManagerFacadeImplTest { @Test public void testCanonicalAccount() { addTestAccount("test@gmail.com"); - List<Account> accounts = mFacade.tryGetGoogleAccounts(); + List<Account> accounts = mFacade.getGoogleAccounts().get(); Assert.assertNotNull(AccountUtils.findAccountByName(accounts, "test@gmail.com")); Assert.assertNotNull(AccountUtils.findAccountByName(accounts, "Test@gmail.com")); @@ -125,7 +136,7 @@ public class AccountManagerFacadeImplTest { @Test public void testNonCanonicalAccount() { addTestAccount("test.me@gmail.com"); - List<Account> accounts = mFacade.tryGetGoogleAccounts(); + List<Account> accounts = mFacade.getGoogleAccounts().get(); Assert.assertNotNull(AccountUtils.findAccountByName(accounts, "test.me@gmail.com")); Assert.assertNotNull(AccountUtils.findAccountByName(accounts, "testme@gmail.com")); @@ -134,106 +145,152 @@ public class AccountManagerFacadeImplTest { } @Test - public void testGetAccounts() throws AccountManagerDelegateException { - Assert.assertEquals(List.of(), mFacade.getGoogleAccounts()); + public void testGetAccounts() { + Assert.assertEquals(List.of(), mFacade.getGoogleAccounts().get()); Account account = addTestAccount("test@gmail.com"); - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); Account account2 = addTestAccount("test2@gmail.com"); - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts().get()); Account account3 = addTestAccount("test3@gmail.com"); - Assert.assertEquals(List.of(account, account2, account3), mFacade.getGoogleAccounts()); + Assert.assertEquals( + List.of(account, account2, account3), mFacade.getGoogleAccounts().get()); removeTestAccount(account2); - Assert.assertEquals(List.of(account, account3), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account3), mFacade.getGoogleAccounts().get()); } @Test - public void testGetAccountsWithAccountPattern() throws AccountManagerDelegateException { + public void testGetAccountsWithAccountPattern() { setAccountRestrictionPatterns("*@example.com"); Account account = addTestAccount("test@example.com"); - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); addTestAccount("test@gmail.com"); // Doesn't match the pattern. - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); Account account2 = addTestAccount("test2@example.com"); - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts().get()); addTestAccount("test2@gmail.com"); // Doesn't match the pattern. - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts().get()); removeTestAccount(account); - Assert.assertEquals(List.of(account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account2), mFacade.getGoogleAccounts().get()); } @Test - public void testGetAccountsWithTwoAccountPatterns() throws AccountManagerDelegateException { + public void testGetAccountsWithTwoAccountPatterns() { setAccountRestrictionPatterns("test1@example.com", "test2@gmail.com"); addTestAccount("test@gmail.com"); // Doesn't match the pattern. addTestAccount("test@example.com"); // Doesn't match the pattern. - Assert.assertEquals(List.of(), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(), mFacade.getGoogleAccounts().get()); Account account = addTestAccount("test1@example.com"); - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); addTestAccount("test2@example.com"); // Doesn't match the pattern. - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); Account account2 = addTestAccount("test2@gmail.com"); - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts().get()); } @Test - public void testGetAccountsWithAccountPatternsChange() throws AccountManagerDelegateException { - Assert.assertEquals(List.of(), mFacade.getGoogleAccounts()); + public void testGetAccountsWithAccountPatternsChange() { + Assert.assertEquals(List.of(), mFacade.getGoogleAccounts().get()); Account account = addTestAccount("test@gmail.com"); - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); Account account2 = addTestAccount("test2@example.com"); - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts().get()); Account account3 = addTestAccount("test3@gmail.com"); - Assert.assertEquals(List.of(account, account2, account3), mFacade.getGoogleAccounts()); + Assert.assertEquals( + List.of(account, account2, account3), mFacade.getGoogleAccounts().get()); setAccountRestrictionPatterns("test@gmail.com"); - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); setAccountRestrictionPatterns("*@example.com", "test3@gmail.com"); - Assert.assertEquals(List.of(account2, account3), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account2, account3), mFacade.getGoogleAccounts().get()); removeTestAccount(account3); - Assert.assertEquals(List.of(account2), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account2), mFacade.getGoogleAccounts().get()); + } + + @Test + public void testGetAccountsWithAccountPatternsCleared() { + final Account account1 = addTestAccount("test1@gmail.com"); + final Account account2 = addTestAccount("testexample2@example.com"); + setAccountRestrictionPatterns("*@example.com"); + Assert.assertEquals(List.of(account2), mFacade.getGoogleAccounts().get()); - clearAccountRestrictionPatterns(); - Assert.assertEquals(List.of(account, account2), mFacade.getGoogleAccounts()); + mShadowUserManager.setApplicationRestrictions(mContext.getPackageName(), new Bundle()); + mContext.sendBroadcast(new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)); + + Assert.assertEquals(List.of(account1, account2), mFacade.getGoogleAccounts().get()); } @Test - public void testGetAccountsMultipleMatchingPatterns() throws AccountManagerDelegateException { + public void testGetAccountsMultipleMatchingPatterns() { setAccountRestrictionPatterns("*@gmail.com", "test@gmail.com"); Account account = addTestAccount("test@gmail.com"); // Matches both patterns - Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts()); + Assert.assertEquals(List.of(account), mFacade.getGoogleAccounts().get()); } @Test - public void testCheckChildAccount() { - Account testAccount = addTestAccount("test@gmail.com"); - Account ucaAccount = addTestAccount( + public void testCheckChildAccountForRegularChild() { + final Account account = setFeaturesForAccount( "uca@gmail.com", AccountManagerFacadeImpl.FEATURE_IS_CHILD_ACCOUNT_KEY); - Account usmAccount = addTestAccount( + + mFacadeWithSystemDelegate.checkChildAccountStatus(account, mChildAccountStatusListenerMock); + + verify(mChildAccountStatusListenerMock).onStatusReady(ChildAccountStatus.REGULAR_CHILD); + } + + @Test + public void testCheckChildAccountForUSMChild() { + final Account account = setFeaturesForAccount( "usm@gmail.com", AccountManagerFacadeImpl.FEATURE_IS_USM_ACCOUNT_KEY); - Account bothAccount = addTestAccount("uca_usm@gmail.com", - AccountManagerFacadeImpl.FEATURE_IS_CHILD_ACCOUNT_KEY, - AccountManagerFacadeImpl.FEATURE_IS_USM_ACCOUNT_KEY); - - assertChildAccountStatus(testAccount, ChildAccountStatus.NOT_CHILD); - assertChildAccountStatus(ucaAccount, ChildAccountStatus.REGULAR_CHILD); - assertChildAccountStatus(usmAccount, ChildAccountStatus.USM_CHILD); - assertChildAccountStatus(bothAccount, ChildAccountStatus.REGULAR_CHILD); + + mFacadeWithSystemDelegate.checkChildAccountStatus(account, mChildAccountStatusListenerMock); + + verify(mChildAccountStatusListenerMock).onStatusReady(ChildAccountStatus.USM_CHILD); + } + + @Test + public void testCheckChildAccountForRegularUSMChild() { + final Account account = setFeaturesForAccount("usm_uca@gmail.com", + AccountManagerFacadeImpl.FEATURE_IS_USM_ACCOUNT_KEY, + AccountManagerFacadeImpl.FEATURE_IS_CHILD_ACCOUNT_KEY); + + mFacadeWithSystemDelegate.checkChildAccountStatus(account, mChildAccountStatusListenerMock); + + verify(mChildAccountStatusListenerMock).onStatusReady(ChildAccountStatus.REGULAR_CHILD); + } + + @Test + public void testCheckChildAccountForAdult() { + final Account account = setFeaturesForAccount("adult@gmail.com"); + + mFacadeWithSystemDelegate.checkChildAccountStatus(account, mChildAccountStatusListenerMock); + + verify(mChildAccountStatusListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD); + } + + @Test + public void testGetAccessToken() throws AuthException { + final Account account = AccountUtils.createAccountFromName("test@gmail.com"); + final AccessTokenData originalToken = + mFacadeWithSystemDelegate.getAccessToken(account, TEST_TOKEN_SCOPE); + + Assert.assertEquals("The same token should be returned.", + mFacadeWithSystemDelegate.getAccessToken(account, TEST_TOKEN_SCOPE).getToken(), + originalToken.getToken()); } @Test @@ -243,7 +300,9 @@ public class AccountManagerFacadeImplTest { Assert.assertEquals("The same token should be returned before invalidating the token.", mFacade.getAccessToken(account, TEST_TOKEN_SCOPE).getToken(), originalToken.getToken()); + mFacade.invalidateAccessToken(originalToken.getToken()); + final AccessTokenData newToken = mFacade.getAccessToken(account, TEST_TOKEN_SCOPE); Assert.assertNotEquals( "A different token should be returned since the original token is invalidated.", @@ -255,22 +314,26 @@ public class AccountManagerFacadeImplTest { AccountManagerFacadeProvider.getInstance(); } - private Account addTestAccount(String accountEmail, String... features) { - AccountHolder holder = AccountHolder.builder(accountEmail) - .addFeatures(features) - .build(); + private Account setFeaturesForAccount(String email, String... features) { + final Account account = AccountUtils.createAccountFromName(email); + mShadowAccountManager.setFeatures(account, features); + return account; + } + + private void setAccountRestrictionPatterns(String... patterns) { + Bundle restrictions = new Bundle(); + restrictions.putStringArray("RestrictAccountsToPatterns", patterns); + mShadowUserManager.setApplicationRestrictions(mContext.getPackageName(), restrictions); + mContext.sendBroadcast(new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)); + } + + private Account addTestAccount(String accountEmail) { + final AccountHolder holder = AccountHolder.createFromEmail(accountEmail); mDelegate.addAccount(holder); - Assert.assertFalse(((AccountManagerFacadeImpl) mFacade).isUpdatePending().get()); return holder.getAccount(); } private void removeTestAccount(Account account) { - mDelegate.removeAccount(AccountHolder.builder(account).build()); - } - - private void assertChildAccountStatus(Account account, @ChildAccountStatus.Status int status) { - ChildAccountStatusListener listenerMock = mock(ChildAccountStatusListener.class); - mFacade.checkChildAccountStatus(account, listenerMock); - verify(listenerMock).onStatusReady(status); + mDelegate.removeAccount(AccountHolder.createFromAccount(account)); } } diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountRenameCheckerTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountRenameCheckerTest.java index 52d9c158509..af336538277 100644 --- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountRenameCheckerTest.java +++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountRenameCheckerTest.java @@ -7,6 +7,8 @@ package org.chromium.components.signin; import android.accounts.Account; import android.content.Context; +import androidx.annotation.Nullable; + import com.google.android.gms.auth.AccountChangeEvent; import com.google.android.gms.auth.GoogleAuthUtil; @@ -18,6 +20,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.chromium.base.task.test.CustomShadowAsyncTask; import org.chromium.base.test.BaseRobolectricTestRunner; import java.util.ArrayList; @@ -25,31 +28,35 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * JUnit tests of the class {@link AccountRenameChecker}. */ @RunWith(BaseRobolectricTestRunner.class) -@Config(shadows = {AccountRenameCheckerTest.ShadowGoogleAuthUtil.class}) +@Config(shadows = {AccountRenameCheckerTest.ShadowGoogleAuthUtil.class, + CustomShadowAsyncTask.class}) public class AccountRenameCheckerTest { @Implements(GoogleAuthUtil.class) static final class ShadowGoogleAuthUtil { - private static final Map<String, String> sEvents = new HashMap<>(); + private static final Map<String, List<AccountChangeEvent>> sEvents = new HashMap<>(); @Implementation public static List<AccountChangeEvent> getAccountChangeEvents( Context context, int eventIndex, String accountEmail) { - if (sEvents.containsKey(accountEmail)) { - final AccountChangeEvent event = new AccountChangeEvent(0L, accountEmail, - GoogleAuthUtil.CHANGE_TYPE_ACCOUNT_RENAMED_TO, 0, - sEvents.get(accountEmail)); - return List.of(event); - } - return Collections.emptyList(); + return sEvents.getOrDefault(accountEmail, Collections.emptyList()); } static void insertRenameEvent(String from, String to) { - sEvents.put(from, to); + addEvent(from, + new AccountChangeEvent( + 0L, from, GoogleAuthUtil.CHANGE_TYPE_ACCOUNT_RENAMED_TO, 0, to)); + } + + static void addEvent(String email, AccountChangeEvent event) { + final List<AccountChangeEvent> events = sEvents.getOrDefault(email, new ArrayList<>()); + events.add(event); + sEvents.put(email, events); } static void clearAllEvents() { @@ -57,7 +64,7 @@ public class AccountRenameCheckerTest { } } - private final AccountRenameChecker mChecker = new AccountRenameChecker(); + private final AccountRenameChecker mChecker = AccountRenameChecker.get(); @After public void tearDown() { @@ -68,21 +75,30 @@ public class AccountRenameCheckerTest { public void newNameIsValidWhenTheRenamedAccountIsPresent() { ShadowGoogleAuthUtil.insertRenameEvent("A", "B"); - Assert.assertEquals("B", mChecker.getNewNameOfRenamedAccount("A", getAccounts("B"))); + Assert.assertEquals("B", getNewNameOfRenamedAccount("A", List.of("B"))); + } + + @Test + public void newNameIsValidWhenOldAccountIsRemovedAndThenRenamed() { + ShadowGoogleAuthUtil.addEvent("A", + new AccountChangeEvent(0L, "A", GoogleAuthUtil.CHANGE_TYPE_ACCOUNT_REMOVED, 0, "")); + ShadowGoogleAuthUtil.insertRenameEvent("A", "B"); + + Assert.assertEquals("B", getNewNameOfRenamedAccount("A", List.of("B"))); } @Test public void newNameIsNullWhenTheOldAccountIsNotRenamed() { ShadowGoogleAuthUtil.insertRenameEvent("B", "C"); - Assert.assertNull(mChecker.getNewNameOfRenamedAccount("A", getAccounts("D"))); + Assert.assertNull(getNewNameOfRenamedAccount("A", List.of("D"))); } @Test public void newNameIsNullWhenTheRenamedAccountIsNotPresent() { ShadowGoogleAuthUtil.insertRenameEvent("B", "C"); - Assert.assertNull(mChecker.getNewNameOfRenamedAccount("B", getAccounts("D"))); + Assert.assertNull(getNewNameOfRenamedAccount("B", List.of("D"))); } @Test @@ -90,7 +106,7 @@ public class AccountRenameCheckerTest { ShadowGoogleAuthUtil.insertRenameEvent("A", "B"); ShadowGoogleAuthUtil.insertRenameEvent("B", "C"); - Assert.assertEquals("C", mChecker.getNewNameOfRenamedAccount("A", getAccounts("C"))); + Assert.assertEquals("C", getNewNameOfRenamedAccount("A", List.of("C"))); } @Test @@ -102,7 +118,7 @@ public class AccountRenameCheckerTest { ShadowGoogleAuthUtil.insertRenameEvent("B", "C"); ShadowGoogleAuthUtil.insertRenameEvent("C", "D"); - Assert.assertEquals("D", mChecker.getNewNameOfRenamedAccount("A", getAccounts("D"))); + Assert.assertEquals("D", getNewNameOfRenamedAccount("A", List.of("D"))); } @Test @@ -115,14 +131,18 @@ public class AccountRenameCheckerTest { ShadowGoogleAuthUtil.insertRenameEvent("C", "D"); ShadowGoogleAuthUtil.insertRenameEvent("D", "A"); // Looped. - Assert.assertEquals("D", mChecker.getNewNameOfRenamedAccount("A", getAccounts("D", "X"))); + Assert.assertEquals("D", getNewNameOfRenamedAccount("A", List.of("D", "X"))); } - private List<Account> getAccounts(String... names) { + private @Nullable String getNewNameOfRenamedAccount( + String oldAccountEmail, List<String> accountEmails) { final List<Account> accounts = new ArrayList<>(); - for (String name : names) { - accounts.add(AccountUtils.createAccountFromName(name)); + for (String email : accountEmails) { + accounts.add(AccountUtils.createAccountFromName(email)); } - return accounts; + final AtomicReference<String> newAccountName = new AtomicReference<>(); + mChecker.getNewNameOfRenamedAccountAsync(oldAccountEmail, accounts) + .then(newAccountName::set); + return newAccountName.get(); } } diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/ObservableValueTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/ObservableValueTest.java deleted file mode 100644 index ab684c86961..00000000000 --- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/ObservableValueTest.java +++ /dev/null @@ -1,109 +0,0 @@ -// 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. -package org.chromium.components.signin; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Robolectric tests for {@link ObservableValue}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class ObservableValueTest { - @Test - public void testNullAllowed() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(null); - // Using null as a value should be allowed. - assertNull(value.get()); - } - - @Test - public void testAddObserverNoCallsToOnValueChanged() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(0); - ObservableValue.Observer observer = mock(ObservableValue.Observer.class); - value.addObserver(observer); - verifyZeroInteractions(observer); - } - - @Test - public void testObserverIsNotifiedOnSet() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(0); - ObservableValue.Observer observer = mock(ObservableValue.Observer.class); - value.addObserver(observer); - - value.set(1); - assertEquals(1, (int) value.get()); - verify(observer, times(1)).onValueChanged(); - } - - @Test - public void testObserverIsNotNotifiedOnSetWithTheSameValue() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(123); - ObservableValue.Observer observer = mock(ObservableValue.Observer.class); - value.addObserver(observer); - - // Manually allocate the new value to make sure it's a different object and not a reference - // to the same object from Java integer pool. - @SuppressWarnings("UnnecessaryBoxing") - Integer newValue = new Integer(123); - - value.set(newValue); - assertEquals(123, (int) value.get()); - verifyZeroInteractions(observer); - } - - @Test - public void testObserverIsNotNotifiedAfterRemoval() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(0); - ObservableValue.Observer observer = mock(ObservableValue.Observer.class); - value.addObserver(observer); - - value.removeObserver(observer); - value.set(321); - assertEquals(321, (int) value.get()); - verifyZeroInteractions(observer); - } - - @Test - public void testGetReturnsUpdatedValueFromObserver() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(0); - AtomicInteger valueHolder = new AtomicInteger(0); - value.addObserver(() -> valueHolder.set(value.get())); - - value.set(123); - assertEquals(123, valueHolder.get()); - } - - @Test - public void testCanModifyObserverListFromOnValueChanged() { - MutableObservableValue<Integer> value = new MutableObservableValue<>(0); - AtomicInteger callCounter = new AtomicInteger(0); - ObservableValue.Observer observer = new ObservableValue.Observer() { - @Override - public void onValueChanged() { - callCounter.incrementAndGet(); - value.removeObserver(this); - } - }; - value.addObserver(observer); - value.set(234); - assertEquals("Observer should be invoked once", 1, callCounter.get()); - value.set(345); - assertEquals("Observer should've been removed after the first call", 1, callCounter.get()); - } -} diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountInfoServiceTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountInfoServiceTest.java index c3a3a692757..88e7a54aa87 100644 --- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountInfoServiceTest.java +++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountInfoServiceTest.java @@ -37,6 +37,9 @@ public class AccountInfoServiceTest { private IdentityManager mIdentityManagerMock; @Mock + private AccountTrackerService mAccountTrackerServiceMock; + + @Mock private AccountInfoService.Observer mObserverMock; private final AccountInfo mAccountInfoWithAvatar = @@ -47,7 +50,7 @@ public class AccountInfoServiceTest { @Before public void setUp() { - AccountInfoService.init(mIdentityManagerMock); + AccountInfoService.init(mIdentityManagerMock, mAccountTrackerServiceMock); mService = AccountInfoService.get(); } diff --git a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountTrackerServiceTest.java b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountTrackerServiceTest.java index e8fcb271e69..14597aad2e8 100644 --- a/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/AccountTrackerServiceTest.java +++ b/chromium/components/signin/public/android/junit/src/org/chromium/components/signin/identitymanager/AccountTrackerServiceTest.java @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.components.signin; +package org.chromium.components.signin.identitymanager; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -38,6 +38,8 @@ import org.chromium.base.Callback; import org.chromium.base.task.test.CustomShadowAsyncTask; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.JniMocker; +import org.chromium.components.signin.AccountManagerFacadeProvider; +import org.chromium.components.signin.AccountUtils; import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.test.util.FakeAccountManagerFacade; @@ -98,16 +100,6 @@ public class AccountTrackerServiceTest { } @Test - public void testSeedAccountsWithoutGooglePlayServices() { - when(mFakeAccountManagerFacade.isGooglePlayServicesAvailable()).thenReturn(false); - - mService.seedAccountsIfNeeded(mRunnableMock); - - verify(mFakeAccountManagerFacade, never()).tryGetGoogleAccounts(any()); - verify(mRunnableMock, never()).run(); - } - - @Test public void testSeedAccountsIfNeededBeforeAccountsAreSeeded() { mService.seedAccountsIfNeeded(mRunnableMock); @@ -227,7 +219,7 @@ public class AccountTrackerServiceTest { final AtomicInteger invocationCount = new AtomicInteger(0); doAnswer(AdditionalAnswers.answerVoid((VoidAnswer1<Callback<List<Account>>>) argument0 -> { if (invocationCount.incrementAndGet() < expectedNumberOfInvocations) { - argument0.onResult(mFakeAccountManagerFacade.tryGetGoogleAccounts()); + argument0.onResult(mFakeAccountManagerFacade.getGoogleAccounts().get()); } })) .when(mFakeAccountManagerFacade) diff --git a/chromium/components/signin/public/base/account_consistency_method.cc b/chromium/components/signin/public/base/account_consistency_method.cc index 9898ec09bed..1e706d0d992 100644 --- a/chromium/components/signin/public/base/account_consistency_method.cc +++ b/chromium/components/signin/public/base/account_consistency_method.cc @@ -19,17 +19,29 @@ const base::Feature kMobileIdentityConsistencyVar{ // Feature flag for FRE related changes as part of MICE. const base::Feature kMobileIdentityConsistencyFRE{ "MobileIdentityConsistencyFRE", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kMobileIdentityConsistencyPromos{ + "MobileIdentityConsistencyPromos", base::FEATURE_DISABLED_BY_DEFAULT}; #endif #if defined(OS_IOS) const base::Feature kMobileIdentityConsistency{ "MobileIdentityConsistency", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif + +const base::Feature kMICEWebSignIn{"MICEWebSignInEnabled", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif // defined(OS_IOS) #if defined(OS_ANDROID) || defined(OS_IOS) bool IsMobileIdentityConsistencyEnabled() { return base::FeatureList::IsEnabled(kMobileIdentityConsistency); } -#endif +#endif // defined(OS_ANDROID) || defined(OS_IOS) + +#if defined(OS_IOS) +bool IsMICEWebSignInEnabled() { + return IsMobileIdentityConsistencyEnabled() && + base::FeatureList::IsEnabled(kMICEWebSignIn); +} +#endif // defined(OS_IOS) } // namespace signin diff --git a/chromium/components/signin/public/base/account_consistency_method.h b/chromium/components/signin/public/base/account_consistency_method.h index b0779772fb5..e4a6ea6ee40 100644 --- a/chromium/components/signin/public/base/account_consistency_method.h +++ b/chromium/components/signin/public/base/account_consistency_method.h @@ -27,7 +27,19 @@ bool IsMobileIdentityConsistencyEnabled(); // Feature flag for FRE related changes as part of MICE. extern const base::Feature kMobileIdentityConsistencyFRE; -#endif + +// Feature flag for promo-related changes of `kMobileIdentityConsistency`. +extern const base::Feature kMobileIdentityConsistencyPromos; +#endif // defined(OS_ANDROID) || defined(OS_IOS) + +#if defined(OS_IOS) +// Feature flag for promo-related changes of `kMobileIdentityConsistency`. +extern const base::Feature kMICEWebSignIn; + +// Returns true if the flags |kMICEWebSignInEnabled| and +// |kMobileIdentityConsistency| are enabled for the platform. +bool IsMICEWebSignInEnabled(); +#endif // defined(OS_IOS) enum class AccountConsistencyMethod : int { // No account consistency. diff --git a/chromium/components/signin/public/base/multilogin_parameters.cc b/chromium/components/signin/public/base/multilogin_parameters.cc index ce5878cf683..19ec7b85267 100644 --- a/chromium/components/signin/public/base/multilogin_parameters.cc +++ b/chromium/components/signin/public/base/multilogin_parameters.cc @@ -6,8 +6,7 @@ namespace signin { -MultiloginParameters::MultiloginParameters() - : mode(gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER) {} +MultiloginParameters::MultiloginParameters() = default; MultiloginParameters::MultiloginParameters( const gaia::MultiloginMode mode, diff --git a/chromium/components/signin/public/base/multilogin_parameters.h b/chromium/components/signin/public/base/multilogin_parameters.h index efb672e3e0b..4859c7c6749 100644 --- a/chromium/components/signin/public/base/multilogin_parameters.h +++ b/chromium/components/signin/public/base/multilogin_parameters.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_SIGNIN_PUBLIC_BASE_MULTILOGIN_PARAMETERS_H_ #define COMPONENTS_SIGNIN_PUBLIC_BASE_MULTILOGIN_PARAMETERS_H_ -#include <string> #include <vector> #include "google_apis/gaia/core_account_id.h" @@ -26,7 +25,12 @@ struct MultiloginParameters { return mode == other.mode && accounts_to_send == other.accounts_to_send; } - gaia::MultiloginMode mode; + bool operator!=(const MultiloginParameters& other) const { + return !(*this == other); + } + + gaia::MultiloginMode mode = + gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER; std::vector<CoreAccountId> accounts_to_send; }; } // namespace signin diff --git a/chromium/components/signin/public/base/signin_client.h b/chromium/components/signin/public/base/signin_client.h index 2f917a15cab..48b496d10a4 100644 --- a/chromium/components/signin/public/base/signin_client.h +++ b/chromium/components/signin/public/base/signin_client.h @@ -9,7 +9,6 @@ #include "base/callback.h" #include "base/callback_list.h" -#include "base/time/time.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "components/keyed_service/core/keyed_service.h" @@ -19,8 +18,8 @@ #include "url/gurl.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/optional.h" #include "components/account_manager_core/account.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #endif class PrefService; @@ -100,7 +99,7 @@ class SigninClient : public KeyedService { virtual bool IsNonEnterpriseUser(const std::string& username); #if BUILDFLAG(IS_CHROMEOS_LACROS) - virtual base::Optional<account_manager::Account> + virtual absl::optional<account_manager::Account> GetInitialPrimaryAccount() = 0; #endif }; diff --git a/chromium/components/signin/public/base/signin_metrics.cc b/chromium/components/signin/public/base/signin_metrics.cc index 9f4b9c7853a..40e268f9be6 100644 --- a/chromium/components/signin/public/base/signin_metrics.cc +++ b/chromium/components/signin/public/base/signin_metrics.cc @@ -43,10 +43,6 @@ void RecordSigninUserActionForAccessPoint(AccessPoint access_point) { base::RecordAction( base::UserMetricsAction("Signin_Signin_FromExtensions")); break; - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: - base::RecordAction( - base::UserMetricsAction("Signin_Signin_FromAppsPageLink")); - break; case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: base::RecordAction( base::UserMetricsAction("Signin_Signin_FromBookmarkBubble")); @@ -198,7 +194,6 @@ void RecordSigninWithDefaultUserActionForAccessPoint( case AccessPoint::ACCESS_POINT_MENU: case AccessPoint::ACCESS_POINT_SUPERVISED_USER: case AccessPoint::ACCESS_POINT_EXTENSIONS: - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: case AccessPoint::ACCESS_POINT_USER_MANAGER: case AccessPoint::ACCESS_POINT_DEVICES_PAGE: case AccessPoint::ACCESS_POINT_CLOUD_PRINT: @@ -278,7 +273,6 @@ void RecordSigninNotDefaultUserActionForAccessPoint( case AccessPoint::ACCESS_POINT_MENU: case AccessPoint::ACCESS_POINT_SUPERVISED_USER: case AccessPoint::ACCESS_POINT_EXTENSIONS: - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: case AccessPoint::ACCESS_POINT_USER_MANAGER: case AccessPoint::ACCESS_POINT_DEVICES_PAGE: case AccessPoint::ACCESS_POINT_CLOUD_PRINT: @@ -362,7 +356,6 @@ void RecordSigninNewAccountNoExistingAccountUserActionForAccessPoint( case AccessPoint::ACCESS_POINT_MENU: case AccessPoint::ACCESS_POINT_SUPERVISED_USER: case AccessPoint::ACCESS_POINT_EXTENSIONS: - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: case AccessPoint::ACCESS_POINT_USER_MANAGER: case AccessPoint::ACCESS_POINT_DEVICES_PAGE: case AccessPoint::ACCESS_POINT_CLOUD_PRINT: @@ -449,7 +442,6 @@ void RecordSigninNewAccountExistingAccountUserActionForAccessPoint( case AccessPoint::ACCESS_POINT_MENU: case AccessPoint::ACCESS_POINT_SUPERVISED_USER: case AccessPoint::ACCESS_POINT_EXTENSIONS: - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: case AccessPoint::ACCESS_POINT_USER_MANAGER: case AccessPoint::ACCESS_POINT_DEVICES_PAGE: case AccessPoint::ACCESS_POINT_CLOUD_PRINT: @@ -825,10 +817,6 @@ void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point) { base::RecordAction(base::UserMetricsAction( "Signin_Impression_FromExtensionInstallBubble")); break; - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: - base::RecordAction( - base::UserMetricsAction("Signin_Impression_FromAppsPageLink")); - break; case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: base::RecordAction( base::UserMetricsAction("Signin_Impression_FromBookmarkBubble")); @@ -1025,7 +1013,6 @@ void RecordSigninImpressionWithAccountUserActionForAccessPoint( case AccessPoint::ACCESS_POINT_MENU: case AccessPoint::ACCESS_POINT_SUPERVISED_USER: case AccessPoint::ACCESS_POINT_EXTENSIONS: - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: case AccessPoint::ACCESS_POINT_USER_MANAGER: case AccessPoint::ACCESS_POINT_DEVICES_PAGE: case AccessPoint::ACCESS_POINT_CLOUD_PRINT: diff --git a/chromium/components/signin/public/base/signin_metrics.h b/chromium/components/signin/public/base/signin_metrics.h index b9e4d70c593..f4cf883cb24 100644 --- a/chromium/components/signin/public/base/signin_metrics.h +++ b/chromium/components/signin/public/base/signin_metrics.h @@ -148,7 +148,7 @@ enum class AccessPoint : int { ACCESS_POINT_SUPERVISED_USER = 4, ACCESS_POINT_EXTENSION_INSTALL_BUBBLE = 5, ACCESS_POINT_EXTENSIONS = 6, - ACCESS_POINT_APPS_PAGE_LINK = 7, + // ACCESS_POINT_APPS_PAGE_LINK = 7, no longer used. ACCESS_POINT_BOOKMARK_BUBBLE = 8, ACCESS_POINT_BOOKMARK_MANAGER = 9, ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN = 10, @@ -222,52 +222,51 @@ enum class AccountConsistencyPromoAction : int { // Promo is not shown as there are no accounts on device. SUPPRESSED_NO_ACCOUNTS = 0, // User has dismissed the promo by tapping back button. - DISMISSED_BACK, + DISMISSED_BACK = 1, // User has tapped |Add account to device| from expanded account list. - ADD_ACCOUNT_STARTED, - // User tapped the button from the expanded account list to open the incognito - // interstitial - // then confirmed opening the page in the incognito tab by tapping |Continue| - // in the incognito - // interstitial. - STARTED_INCOGNITO_SESSION, + ADD_ACCOUNT_STARTED = 2, + + // Deprecated 05/2021, since the Incognito option has been removed from + // account picker bottomsheet. + // STARTED_INCOGNITO_SESSION = 3, + // User has selected the default account and signed in with it - SIGNED_IN_WITH_DEFAULT_ACCOUNT, + SIGNED_IN_WITH_DEFAULT_ACCOUNT = 4, // User has selected one of the non default accounts and signed in with it. - SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT, + SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT = 5, // The promo was shown to user. - SHOWN, + SHOWN = 6, // Promo is not shown due to sign-in being disallowed either by an enterprise // policy // or by |Allow Chrome sign-in| toggle. - SUPPRESSED_SIGNIN_NOT_ALLOWED, + SUPPRESSED_SIGNIN_NOT_ALLOWED = 7, // User has added an account and signed in with this account. // When this metric is recorded, we won't record // SIGNED_IN_WITH_DEFAULT_ACCOUNT or // SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT. - SIGNED_IN_WITH_ADDED_ACCOUNT, + SIGNED_IN_WITH_ADDED_ACCOUNT = 8, // User has dismissed the promo by tapping on the scrim above the bottom // sheet. - DISMISSED_SCRIM, + DISMISSED_SCRIM = 9, // User has dismissed the promo by swiping down the bottom sheet. - DISMISSED_SWIPE_DOWN, + DISMISSED_SWIPE_DOWN = 10, // User has dismissed the promo by other means. - DISMISSED_OTHER, + DISMISSED_OTHER = 11, // The auth error screen was shown to the user. - AUTH_ERROR_SHOWN, + AUTH_ERROR_SHOWN = 12, // The generic error screen was shown to the user. - GENERIC_ERROR_SHOWN, + GENERIC_ERROR_SHOWN = 13, // User has dismissed the promo by tapping on the dismissal button in the // bottom sheet. - DISMISSED_BUTTON, + DISMISSED_BUTTON = 14, // User has completed the account addition flow triggered from the bottom // sheet. - ADD_ACCOUNT_COMPLETED, + ADD_ACCOUNT_COMPLETED = 15, // The bottom sheet was suppressed as the user hit consecutive active // dismissal limit. - SUPPRESSED_CONSECUTIVE_DISMISSALS, + SUPPRESSED_CONSECUTIVE_DISMISSALS = 16, - MAX, + MAX = 17, }; // This class is used to record web sign-in events within 2 minutes after diff --git a/chromium/components/signin/public/base/signin_metrics_unittest.cc b/chromium/components/signin/public/base/signin_metrics_unittest.cc index bda46e4f579..540566d8d54 100644 --- a/chromium/components/signin/public/base/signin_metrics_unittest.cc +++ b/chromium/components/signin/public/base/signin_metrics_unittest.cc @@ -23,7 +23,6 @@ const AccessPoint kAccessPointsThatSupportUserAction[] = { AccessPoint::ACCESS_POINT_SUPERVISED_USER, AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE, AccessPoint::ACCESS_POINT_EXTENSIONS, - AccessPoint::ACCESS_POINT_APPS_PAGE_LINK, AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE, AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER, AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN, @@ -50,7 +49,6 @@ const AccessPoint kAccessPointsThatSupportImpression[] = { AccessPoint::ACCESS_POINT_MENU, AccessPoint::ACCESS_POINT_SETTINGS, AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE, - AccessPoint::ACCESS_POINT_APPS_PAGE_LINK, AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE, AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER, AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN, @@ -97,8 +95,6 @@ class SigninMetricsTest : public ::testing::Test { return "ExtensionInstallBubble"; case AccessPoint::ACCESS_POINT_EXTENSIONS: return "Extensions"; - case AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: - return "AppsPageLink"; case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: return "BookmarkBubble"; case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER: diff --git a/chromium/components/signin/public/base/signin_pref_names.cc b/chromium/components/signin/public/base/signin_pref_names.cc index bba71b0bc08..1803fb0eb3a 100644 --- a/chromium/components/signin/public/base/signin_pref_names.cc +++ b/chromium/components/signin/public/base/signin_pref_names.cc @@ -85,6 +85,10 @@ const char kSignedInWithCredentialProvider[] = // Boolean which stores if the user is allowed to signin to chrome. const char kSigninAllowed[] = "signin.allowed"; +// Boolean which stores if the user is allowed to signin to chrome with the +// current applied policies. +const char kSigninAllowedByPolicy[] = "signin.allowed_by_policy"; + // True if the token service has been prepared for Dice migration. const char kTokenServiceDiceCompatible[] = "token_service.dice_compatible"; diff --git a/chromium/components/signin/public/base/signin_pref_names.h b/chromium/components/signin/public/base/signin_pref_names.h index 3230e8533db..8d49c5b4c57 100644 --- a/chromium/components/signin/public/base/signin_pref_names.h +++ b/chromium/components/signin/public/base/signin_pref_names.h @@ -27,6 +27,7 @@ extern const char kGoogleServicesUsernamePattern[]; extern const char kReverseAutologinRejectedEmailList[]; extern const char kSignedInWithCredentialProvider[]; extern const char kSigninAllowed[]; +extern const char kSigninAllowedByPolicy[]; extern const char kTokenServiceDiceCompatible[]; extern const char kGaiaCookieLastListAccountsData[]; diff --git a/chromium/components/signin/public/base/signin_switches.cc b/chromium/components/signin/public/base/signin_switches.cc index 8243123737b..58f66e6b66e 100644 --- a/chromium/components/signin/public/base/signin_switches.cc +++ b/chromium/components/signin/public/base/signin_switches.cc @@ -4,6 +4,7 @@ #include "components/signin/public/base/signin_switches.h" +#include "base/feature_list.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -29,13 +30,16 @@ const base::Feature kForceAccountIdMigration{"ForceAccountIdMigration", // Menagerie API. const base::Feature kDeprecateMenagerieAPI{"DeprecateMenagerieAPI", base::FEATURE_DISABLED_BY_DEFAULT}; +// This feature flag is used to wipe device data on child account signin. +const base::Feature kWipeDataOnChildAccountSignin{ + "WipeDataOnChildAccountSignin", base::FEATURE_DISABLED_BY_DEFAULT}; #endif #if BUILDFLAG(IS_CHROMEOS_ASH) const base::Feature kUseAccountManagerFacade{"kUseAccountManagerFacade", base::FEATURE_ENABLED_BY_DEFAULT}; -#elif BUILDFLAG(IS_CHROMEOS_LACROS) -const base::Feature kUseAccountManagerFacade{"kUseAccountManagerFacade", - base::FEATURE_DISABLED_BY_DEFAULT}; #endif + +const base::Feature kMinorModeSupport{"MinorModeSupport", + base::FEATURE_DISABLED_BY_DEFAULT}; } // namespace switches diff --git a/chromium/components/signin/public/base/signin_switches.h b/chromium/components/signin/public/base/signin_switches.h index fa46dd79e23..9a634b37dbc 100644 --- a/chromium/components/signin/public/base/signin_switches.h +++ b/chromium/components/signin/public/base/signin_switches.h @@ -31,12 +31,17 @@ extern const base::Feature kForceAccountIdMigration; // This feature flag is for the deprecating of the Android profile data // Menagerie API. extern const base::Feature kDeprecateMenagerieAPI; +// This feature flag is used to wipe device data on child account signin. +extern const base::Feature kWipeDataOnChildAccountSignin; #endif // defined(OS_ANDROID) -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_CHROMEOS_ASH) // Killswitch for PO2TS migration to AccountManagerFacade. extern const base::Feature kUseAccountManagerFacade; #endif + +// Support for the minor mode. +extern const base::Feature kMinorModeSupport; } // namespace switches #endif // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_ diff --git a/chromium/components/signin/public/base/test_signin_client.cc b/chromium/components/signin/public/base/test_signin_client.cc index 767a7aa6bd8..871b1bd2501 100644 --- a/chromium/components/signin/public/base/test_signin_client.cc +++ b/chromium/components/signin/public/base/test_signin_client.cc @@ -14,8 +14,8 @@ #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/optional.h" #include "components/account_manager_core/account.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #endif TestSigninClient::TestSigninClient( @@ -122,14 +122,14 @@ bool TestSigninClient::IsNonEnterpriseUser(const std::string& email) { } #if BUILDFLAG(IS_CHROMEOS_LACROS) -base::Optional<account_manager::Account> +absl::optional<account_manager::Account> TestSigninClient::GetInitialPrimaryAccount() { return initial_primary_account_; } void TestSigninClient::SetInitialPrimaryAccountForTests( const account_manager::Account& account) { - initial_primary_account_ = base::make_optional(account); + initial_primary_account_ = absl::make_optional(account); } #endif diff --git a/chromium/components/signin/public/base/test_signin_client.h b/chromium/components/signin/public/base/test_signin_client.h index 7c2b09f37dc..a8bd37e7788 100644 --- a/chromium/components/signin/public/base/test_signin_client.h +++ b/chromium/components/signin/public/base/test_signin_client.h @@ -21,8 +21,8 @@ #include "services/network/test/test_url_loader_factory.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "base/optional.h" #include "components/account_manager_core/account.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #endif class PrefService; @@ -96,7 +96,7 @@ class TestSigninClient : public SigninClient { void SetDiceMigrationCompleted() override; bool IsNonEnterpriseUser(const std::string& email) override; #if BUILDFLAG(IS_CHROMEOS_LACROS) - base::Optional<account_manager::Account> GetInitialPrimaryAccount() override; + absl::optional<account_manager::Account> GetInitialPrimaryAccount() override; #endif #if BUILDFLAG(IS_CHROMEOS_LACROS) @@ -119,7 +119,7 @@ class TestSigninClient : public SigninClient { std::vector<base::OnceClosure> delayed_network_calls_; #if BUILDFLAG(IS_CHROMEOS_LACROS) - base::Optional<account_manager::Account> initial_primary_account_; + absl::optional<account_manager::Account> initial_primary_account_; #endif DISALLOW_COPY_AND_ASSIGN(TestSigninClient); diff --git a/chromium/components/signin/public/identity_manager/access_token_fetcher.cc b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc index 54ddaf3a6af..0e6161b5236 100644 --- a/chromium/components/signin/public/identity_manager/access_token_fetcher.cc +++ b/chromium/components/signin/public/identity_manager/access_token_fetcher.cc @@ -8,6 +8,7 @@ #include "base/check_op.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "components/signin/public/identity_manager/access_token_constants.h" #include "components/signin/public/identity_manager/access_token_info.h" #include "google_apis/gaia/gaia_constants.h" diff --git a/chromium/components/signin/public/identity_manager/account_info.h b/chromium/components/signin/public/identity_manager/account_info.h index dbffa5e86ff..739f77ceab3 100644 --- a/chromium/components/signin/public/identity_manager/account_info.h +++ b/chromium/components/signin/public/identity_manager/account_info.h @@ -7,9 +7,9 @@ #include <string> -#include "base/optional.h" #include "build/build_config.h" #include "google_apis/gaia/core_account_id.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/gfx/image/image.h" #if defined(OS_ANDROID) diff --git a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h index 200fd2a1f69..81df07c1fd8 100644 --- a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator.h @@ -132,6 +132,10 @@ class AccountsCookieMutator { gaia::GaiaSource source, LogOutFromCookieCompletedCallback completion_callback) = 0; + // Indicates that an account previously listed via ListAccounts should now + // be removed. + virtual void RemoveLoggedOutAccountByGaiaId(const std::string& gaia_id) = 0; + private: DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutator); }; diff --git a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc index 77a4839d460..3ab7cb41a99 100644 --- a/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc +++ b/chromium/components/signin/public/identity_manager/accounts_cookie_mutator_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/task_environment.h" diff --git a/chromium/components/signin/public/identity_manager/accounts_mutator.h b/chromium/components/signin/public/identity_manager/accounts_mutator.h index ab675b1712d..b2c34f4e908 100644 --- a/chromium/components/signin/public/identity_manager/accounts_mutator.h +++ b/chromium/components/signin/public/identity_manager/accounts_mutator.h @@ -8,9 +8,9 @@ #include <string> #include "base/macros.h" -#include "base/optional.h" #include "build/chromeos_buildflags.h" #include "components/signin/public/base/signin_buildflags.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace signin_metrics { enum class SourceForRefreshTokenOperation; @@ -39,8 +39,8 @@ class AccountsMutator { // Updates the information about account identified by |account_id|. virtual void UpdateAccountInfo( const CoreAccountId& account_id, - base::Optional<bool> is_child_account, - base::Optional<bool> is_under_advanced_protection) = 0; + absl::optional<bool> is_child_account, + absl::optional<bool> is_under_advanced_protection) = 0; // Removes the account given by |account_id|. Also revokes the token // server-side if needed. diff --git a/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc index fef09d7213b..0daef5ace2b 100644 --- a/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc +++ b/chromium/components/signin/public/identity_manager/accounts_mutator_unittest.cc @@ -5,7 +5,6 @@ #include "components/signin/internal/identity_manager/accounts_mutator_impl.h" #include "base/bind.h" -#include "base/optional.h" #include "base/test/gtest_util.h" #include "base/test/task_environment.h" #include "build/chromeos_buildflags.h" @@ -18,6 +17,7 @@ #include "components/sync_preferences/testing_pref_service_syncable.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace { @@ -157,7 +157,7 @@ TEST_F(AccountsMutatorTest, UpdateAccountInfo) { accounts_mutator()->UpdateAccountInfo( account_id, /*is_child_account=*/true, - /*is_under_advanced_protection=*/base::nullopt); + /*is_under_advanced_protection=*/absl::nullopt); AccountInfo updated_account_info_1 = identity_manager() ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( @@ -175,7 +175,7 @@ TEST_F(AccountsMutatorTest, UpdateAccountInfo) { original_account_info.is_under_advanced_protection); accounts_mutator()->UpdateAccountInfo(account_id, - /*is_child_account=*/base::nullopt, + /*is_child_account=*/absl::nullopt, /*is_under_advanced_protection=*/true); AccountInfo updated_account_info_2 = identity_manager() @@ -334,8 +334,8 @@ TEST_F(AccountsMutatorTest, // Set up the primary account. std::string primary_account_email("primary.account@example.com"); - AccountInfo primary_account_info = - MakePrimaryAccountAvailable(identity_manager(), primary_account_email); + AccountInfo primary_account_info = MakePrimaryAccountAvailable( + identity_manager(), primary_account_email, signin::ConsentLevel::kSync); // Now try invalidating the primary account, and check that it gets updated. base::RunLoop run_loop; @@ -372,8 +372,8 @@ TEST_F( // Set up the primary account. std::string primary_account_email("primary.account@example.com"); - AccountInfo primary_account_info = - MakePrimaryAccountAvailable(identity_manager(), primary_account_email); + AccountInfo primary_account_info = MakePrimaryAccountAvailable( + identity_manager(), primary_account_email, signin::ConsentLevel::kSync); // Next, add a secondary account. base::RunLoop run_loop; diff --git a/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h b/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h index 3c441ae850f..20c2e3a551f 100644 --- a/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h +++ b/chromium/components/signin/public/identity_manager/device_accounts_synchronizer.h @@ -5,9 +5,9 @@ #ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_H_ #define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_DEVICE_ACCOUNTS_SYNCHRONIZER_H_ -#include "base/optional.h" #include "build/build_config.h" #include "google_apis/gaia/core_account_id.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace signin { @@ -22,7 +22,7 @@ class DeviceAccountsSynchronizer { // accounts will be visible in IdentityManager::GetAccountsWithRefreshTokens() // with any persistent errors cleared after this method is called. virtual void ReloadAllAccountsFromSystemWithPrimaryAccount( - const base::Optional<CoreAccountId>& primary_account_id) = 0; + const absl::optional<CoreAccountId>& primary_account_id) = 0; #if defined(OS_IOS) // Reloads the information of the device-level account with |account_id|. The diff --git a/chromium/components/signin/public/identity_manager/identity_manager.cc b/chromium/components/signin/public/identity_manager/identity_manager.cc index 16c774d763c..2c6f6372c02 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager.cc @@ -7,7 +7,6 @@ #include <string> #include "base/bind.h" -#include "base/optional.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "components/signin/internal/identity_manager/account_fetcher_service.h" @@ -23,6 +22,7 @@ #include "components/signin/public/identity_manager/diagnostics_provider.h" #include "components/signin/public/identity_manager/primary_account_mutator.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #if defined(OS_ANDROID) #include "base/android/jni_string.h" @@ -60,12 +60,13 @@ void SetPrimaryAccount(IdentityManager* identity_manager, const CoreAccountId account_id = account_tracker_service->SeedAccountInfo( /*gaia=*/device_account.key.id, device_account.raw_email); // TODO(https://crbug.com/1194983): Figure out how split sync settings will - // work here. - identity_manager->GetPrimaryAccountMutator()->SetUnconsentedPrimaryAccount( - account_id); + // work here. For now, we will mimic Ash's behaviour of having sync turned on + // by default. + identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount( + account_id, ConsentLevel::kSync); - CHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSignin)); - CHECK_EQ(identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSignin).gaia, + CHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync)); + CHECK_EQ(identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSync).gaia, device_account.key.id); } #endif @@ -136,7 +137,7 @@ IdentityManager::IdentityManager(IdentityManager::InitParameters&& parameters) // Profile / KeyedServices - but with the availability of IdentityManager. We // don't have such a place in Lacros - which guarantees that the Primary // Account will be available on startup - just like Ash. - base::Optional<account_manager::Account> initial_account = + absl::optional<account_manager::Account> initial_account = signin_client_->GetInitialPrimaryAccount(); if (initial_account.has_value()) { SetPrimaryAccount(this, account_tracker_service_.get(), signin_client_, @@ -178,7 +179,7 @@ void IdentityManager::RemoveObserver(Observer* observer) { observer_list_.RemoveObserver(observer); } -// TODO(862619) change return type to base::Optional<CoreAccountInfo> +// TODO(862619) change return type to absl::optional<CoreAccountInfo> CoreAccountInfo IdentityManager::GetPrimaryAccountInfo( ConsentLevel consent) const { return primary_account_manager_->GetPrimaryAccountInfo(consent); @@ -297,7 +298,16 @@ GoogleServiceAuthError IdentityManager::GetErrorStateOfRefreshTokenForAccount( return token_service_->GetAuthError(account_id); } -base::Optional<AccountInfo> +absl::optional<AccountInfo> IdentityManager::FindExtendedAccountInfoByAccountId( + const CoreAccountId& account_id) const { + AccountInfo account_info = + account_tracker_service_->GetAccountInfo(account_id); + if (account_info.IsEmpty()) + return absl::nullopt; + return account_info; +} + +absl::optional<AccountInfo> IdentityManager::FindExtendedAccountInfoForAccountWithRefreshToken( const CoreAccountInfo& account_info) const { AccountInfo extended_account_info = @@ -307,12 +317,12 @@ IdentityManager::FindExtendedAccountInfoForAccountWithRefreshToken( // case of failure, the AccountInfo will be unpopulated, thus we should not // be able to find a valid refresh token. if (!HasAccountWithRefreshToken(extended_account_info.account_id)) - return base::nullopt; + return absl::nullopt; return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); } -base::Optional<AccountInfo> +absl::optional<AccountInfo> IdentityManager::FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( const CoreAccountId& account_id) const { AccountInfo account_info = @@ -322,12 +332,12 @@ IdentityManager::FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( // case of failure, the AccountInfo will be unpopulated, thus we should not // be able to find a valid refresh token. if (!HasAccountWithRefreshToken(account_info.account_id)) - return base::nullopt; + return absl::nullopt; return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); } -base::Optional<AccountInfo> IdentityManager:: +absl::optional<AccountInfo> IdentityManager:: FindExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( const std::string& email_address) const { AccountInfo account_info = @@ -337,12 +347,12 @@ base::Optional<AccountInfo> IdentityManager:: // case of failure, the AccountInfo will be unpopulated, thus we should not // be able to find a valid refresh token. if (!HasAccountWithRefreshToken(account_info.account_id)) - return base::nullopt; + return absl::nullopt; return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); } -base::Optional<AccountInfo> +absl::optional<AccountInfo> IdentityManager::FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId( const std::string& gaia_id) const { AccountInfo account_info = @@ -352,7 +362,7 @@ IdentityManager::FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId( // case of failure, the AccountInfo will be unpopulated, thus we should not // be able to find a valid refresh token. if (!HasAccountWithRefreshToken(account_info.account_id)) - return base::nullopt; + return absl::nullopt; return GetAccountInfoForAccountWithRefreshToken(account_info.account_id); } diff --git a/chromium/components/signin/public/identity_manager/identity_manager.h b/chromium/components/signin/public/identity_manager/identity_manager.h index ce7d2a4cc23..20c54fca5ca 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager.h +++ b/chromium/components/signin/public/identity_manager/identity_manager.h @@ -251,17 +251,23 @@ class IdentityManager : public KeyedService, GoogleServiceAuthError GetErrorStateOfRefreshTokenForAccount( const CoreAccountId& account_id) const; + // Returns extended information for account identified by |account_id|. + // The information will be returned if the information is available regardless + // of whether the refresh token is available for the account. + absl::optional<AccountInfo> FindExtendedAccountInfoByAccountId( + const CoreAccountId& account_id) const; + // Returns extended information for account identified by |account_info|. // The information will be returned if the information is available and // refresh token is available for account. - base::Optional<AccountInfo> FindExtendedAccountInfoForAccountWithRefreshToken( + absl::optional<AccountInfo> FindExtendedAccountInfoForAccountWithRefreshToken( const CoreAccountInfo& account_info) const; // Looks up and returns information for account with given |account_id|. If // the account cannot be found, return an empty optional. This is equivalent // to searching on the vector returned by GetAccountsWithRefreshTokens() but // without allocating memory for the vector. - base::Optional<AccountInfo> + absl::optional<AccountInfo> FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( const CoreAccountId& account_id) const; @@ -269,7 +275,7 @@ class IdentityManager : public KeyedService, // the account cannot be found, return an empty optional. This is equivalent // to searching on the vector returned by GetAccountsWithRefreshTokens() but // without allocating memory for the vector. - base::Optional<AccountInfo> + absl::optional<AccountInfo> FindExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( const std::string& email_address) const; @@ -277,7 +283,7 @@ class IdentityManager : public KeyedService, // account cannot be found, return an empty optional. This is equivalent to // searching on the vector returned by GetAccountsWithRefreshTokens() but // without allocating memory for the vector. - base::Optional<AccountInfo> + absl::optional<AccountInfo> FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId( const std::string& gaia_id) const; @@ -486,10 +492,8 @@ class IdentityManager : public KeyedService, private: // These test helpers need to use some of the private methods below. friend CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, - const std::string& email); - friend CoreAccountInfo SetUnconsentedPrimaryAccount( - IdentityManager* identity_manager, - const std::string& email); + const std::string& email, + ConsentLevel consent_level); friend void SetRefreshTokenForPrimaryAccount( IdentityManager* identity_manager, const std::string& token_value); @@ -499,7 +503,8 @@ class IdentityManager : public KeyedService, IdentityManager* identity_manager); friend AccountInfo MakePrimaryAccountAvailable( IdentityManager* identity_manager, - const std::string& email); + const std::string& email, + ConsentLevel consent_level); friend void RevokeSyncConsent(IdentityManager* identity_manager); friend void ClearPrimaryAccount(IdentityManager* identity_manager); friend AccountInfo MakeAccountAvailable(IdentityManager* identity_manager, diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc index cff722fb004..18eef228dd3 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager_builder.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.cc @@ -115,9 +115,11 @@ IdentityManager::InitParameters BuildIdentityManagerInitParameters( params->pref_service, account_tracker_service.get(), params->network_connection_tracker, params->account_consistency, #if BUILDFLAG(IS_CHROMEOS_ASH) - params->account_manager, params->account_manager_facade, - params->is_regular_profile, -#endif + params->account_manager, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + params->account_manager_facade, params->is_regular_profile, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) #if !defined(OS_ANDROID) params->delete_signin_cookies_on_exit, params->token_web_data, #endif diff --git a/chromium/components/signin/public/identity_manager/identity_manager_builder.h b/chromium/components/signin/public/identity_manager/identity_manager_builder.h index dfd07ac8789..26818a57ebf 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager_builder.h +++ b/chromium/components/signin/public/identity_manager/identity_manager_builder.h @@ -51,6 +51,12 @@ class AccountManagerFacade; } #endif +#if BUILDFLAG(IS_CHROMEOS_LACROS) +namespace account_manager { +class AccountManagerFacade; +} +#endif + namespace signin { enum class AccountConsistencyMethod; @@ -58,25 +64,31 @@ struct IdentityManagerBuildParams { IdentityManagerBuildParams(); ~IdentityManagerBuildParams(); - AccountConsistencyMethod account_consistency; + AccountConsistencyMethod account_consistency = + AccountConsistencyMethod::kDisabled; std::unique_ptr<AccountTrackerService> account_tracker_service; std::unique_ptr<image_fetcher::ImageDecoder> image_decoder; - PrefService* local_state; + PrefService* local_state = nullptr; network::NetworkConnectionTracker* network_connection_tracker; - PrefService* pref_service; + PrefService* pref_service = nullptr; base::FilePath profile_path; - SigninClient* signin_client; + SigninClient* signin_client = nullptr; std::unique_ptr<ProfileOAuth2TokenService> token_service; #if !defined(OS_ANDROID) - bool delete_signin_cookies_on_exit; + bool delete_signin_cookies_on_exit = false; scoped_refptr<TokenWebData> token_web_data; #endif #if BUILDFLAG(IS_CHROMEOS_ASH) - ash::AccountManager* account_manager; - account_manager::AccountManagerFacade* account_manager_facade; - bool is_regular_profile; + ash::AccountManager* account_manager = nullptr; + account_manager::AccountManagerFacade* account_manager_facade = nullptr; + bool is_regular_profile = false; +#endif + +#if BUILDFLAG(IS_CHROMEOS_LACROS) + account_manager::AccountManagerFacade* account_manager_facade = nullptr; + bool is_regular_profile = false; #endif #if defined(OS_IOS) diff --git a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc index bdf6ef105fb..9206820d912 100644 --- a/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc +++ b/chromium/components/signin/public/identity_manager/identity_manager_unittest.cc @@ -562,7 +562,7 @@ TEST_F(IdentityManagerTest, PrimaryAccountInfoAtStartup) { TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSignin) { ClearPrimaryAccount(identity_manager()); - SetPrimaryAccount(identity_manager(), kTestEmail); + SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync); auto event = identity_manager_observer()->GetPrimaryAccountChangedEvent(); EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet, event.GetEventTypeFor(ConsentLevel::kSync)); @@ -598,7 +598,7 @@ TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSigninAndSignout) { ClearPrimaryAccount(identity_manager()); // First ensure that the user is signed in from the POV of the // IdentityManager. - SetPrimaryAccount(identity_manager(), kTestEmail); + SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync); // Sign the user out and check that the IdentityManager responds // appropriately. @@ -642,7 +642,7 @@ TEST_F(IdentityManagerTest, ClearPrimaryAccount(identity_manager()); // First ensure that the user is signed in from the POV of the // IdentityManager. - SetPrimaryAccount(identity_manager(), kTestEmail); + SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync); identity_manager()->account_fetcher_service_->EnableAccountRemovalForTest(); // Revoke the primary's account credentials from the token service and @@ -1259,8 +1259,7 @@ TEST_F(IdentityManagerTest, RemoveAccessTokenFromCache) { identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId, kTestEmail); identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( - primary_account_id()); - + primary_account_id(), ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager(), primary_account_id(), "refresh_token"); @@ -1303,7 +1302,7 @@ TEST_F(IdentityManagerTest, identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId, kTestEmail); identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( - primary_account_id()); + primary_account_id(), ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager(), primary_account_id(), "refresh_token"); @@ -1396,7 +1395,7 @@ TEST_F(IdentityManagerTest, ObserveAccessTokenFetch) { identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId, kTestEmail); identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( - primary_account_id()); + primary_account_id(), ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager(), primary_account_id(), "refresh_token"); @@ -1454,7 +1453,7 @@ TEST_F(IdentityManagerTest, identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId, kTestEmail); identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( - primary_account_id()); + primary_account_id(), ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager(), primary_account_id(), "refresh_token"); token_service()->set_auto_post_fetch_response_on_message_loop(true); @@ -2146,7 +2145,7 @@ TEST_F(IdentityManagerTest, identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId, kTestEmail); identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( - primary_account_id()); + primary_account_id(), ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager(), primary_account_id(), "refresh_token"); @@ -2157,6 +2156,26 @@ TEST_F(IdentityManagerTest, identity_manager_observer()->BatchChangeRecords().at(0).at(0)); } +// Checks that FindExtendedAccountInfoByAccountId() returns information about +// the account if the account is found or nullopt if there are no accounts with +// requested |account_id|. +TEST_F(IdentityManagerTest, FindExtendedAccountInfoByAccountId) { + auto account_id = + identity_manager()->PickAccountIdForAccount(kTestGaiaId, kTestEmail); + + absl::optional<AccountInfo> maybe_account_info; + maybe_account_info = identity_manager()->FindExtendedAccountInfoByAccountId( + CoreAccountId("dummy_value")); + EXPECT_FALSE(maybe_account_info.has_value()); + + maybe_account_info = + identity_manager()->FindExtendedAccountInfoByAccountId(account_id); + EXPECT_TRUE(maybe_account_info.has_value()); + EXPECT_EQ(account_id, maybe_account_info.value().account_id); + EXPECT_EQ(kTestEmail, maybe_account_info.value().email); + EXPECT_EQ(kTestGaiaId, maybe_account_info.value().gaia); +} + // Checks that FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId() // returns information about the account if the account is found or nullopt if // there are no accounts with requested |account_id|. @@ -2167,7 +2186,7 @@ TEST_F(IdentityManagerTest, const AccountInfo foo_account_info = MakeAccountAvailable(identity_manager(), "foo@bar.com"); - base::Optional<AccountInfo> maybe_account_info; + absl::optional<AccountInfo> maybe_account_info; maybe_account_info = identity_manager() ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( @@ -2194,7 +2213,7 @@ TEST_F(IdentityManagerTest, const AccountInfo foo_account_info = MakeAccountAvailable(identity_manager(), "foo@bar.com"); - base::Optional<AccountInfo> maybe_account_info; + absl::optional<AccountInfo> maybe_account_info; maybe_account_info = identity_manager() ->FindExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress( @@ -2221,7 +2240,7 @@ TEST_F(IdentityManagerTest, const AccountInfo foo_account_info = MakeAccountAvailable(identity_manager(), "foo@bar.com"); - base::Optional<AccountInfo> maybe_account_info; + absl::optional<AccountInfo> maybe_account_info; maybe_account_info = identity_manager() ->FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId( @@ -2375,7 +2394,7 @@ TEST_F(IdentityManagerTest, FindExtendedAccountInfoForAccountWithRefreshToken) { // FindExtendedAccountInfoForAccountWithRefreshToken() returns extended // account information if the account is known and has valid refresh token. - const base::Optional<AccountInfo> extended_account_info = + const absl::optional<AccountInfo> extended_account_info = identity_manager()->FindExtendedAccountInfoForAccountWithRefreshToken( account_info); @@ -2388,9 +2407,9 @@ TEST_F(IdentityManagerTest, FindExtendedAccountInfoForAccountWithRefreshToken) { #if defined(OS_ANDROID) TEST_F(IdentityManagerTest, ForceRefreshOfExtendedAccountInfo) { // The flow of this test results in an interaction with - // ChildAccountInfoFetcherAndroid, which requires initialization in order to - // avoid a crash. - ChildAccountInfoFetcherAndroid::InitializeForTests(); + // ChildAccountInfoFetcherAndroid, which requires initialization of + // AccountManagerFacade in java code to avoid a crash. + SetUpMockAccountManagerFacade(); identity_manager()->GetAccountFetcherService()->OnNetworkInitialized(); AccountInfo account_info = diff --git a/chromium/components/signin/public/identity_manager/identity_mutator.cc b/chromium/components/signin/public/identity_manager/identity_mutator.cc index fe5b5bac5c9..094e4406a8f 100644 --- a/chromium/components/signin/public/identity_manager/identity_mutator.cc +++ b/chromium/components/signin/public/identity_manager/identity_mutator.cc @@ -29,20 +29,10 @@ bool JniIdentityMutator::SetPrimaryAccount( PrimaryAccountMutator* primary_account_mutator = identity_mutator_->GetPrimaryAccountMutator(); DCHECK(primary_account_mutator); - // TODO(https://crbug.com/1046746): Refactor PrimaryAccountMutator API and - // pass ConsentLevel directly there. - switch (static_cast<ConsentLevel>(j_consent_level)) { - case ConsentLevel::kSync: - return primary_account_mutator->SetPrimaryAccount( - ConvertFromJavaCoreAccountId(env, primary_account_id)); - case ConsentLevel::kSignin: - primary_account_mutator->SetUnconsentedPrimaryAccount( - ConvertFromJavaCoreAccountId(env, primary_account_id)); - return true; - default: - NOTREACHED() << "Unknown consent level: " << j_consent_level; - return false; - } + + return primary_account_mutator->SetPrimaryAccount( + ConvertFromJavaCoreAccountId(env, primary_account_id), + static_cast<ConsentLevel>(j_consent_level)); } bool JniIdentityMutator::ClearPrimaryAccount(JNIEnv* env, @@ -62,7 +52,7 @@ void JniIdentityMutator::ReloadAllAccountsFromSystemWithPrimaryAccount( DeviceAccountsSynchronizer* device_accounts_synchronizer = identity_mutator_->GetDeviceAccountsSynchronizer(); DCHECK(device_accounts_synchronizer); - base::Optional<CoreAccountId> primary_account_id; + absl::optional<CoreAccountId> primary_account_id; if (j_primary_account_id) { primary_account_id = ConvertFromJavaCoreAccountId(env, j_primary_account_id); diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.cc b/chromium/components/signin/public/identity_manager/identity_test_environment.cc index d5bd7fee328..72280aa9e8a 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_environment.cc +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.cc @@ -376,12 +376,14 @@ void IdentityTestEnvironment::WaitForRefreshTokensLoaded() { CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount( const std::string& email) { - return signin::SetPrimaryAccount(identity_manager(), email); + return signin::SetPrimaryAccount(identity_manager(), email, + signin::ConsentLevel::kSync); } CoreAccountInfo IdentityTestEnvironment::SetUnconsentedPrimaryAccount( const std::string& email) { - return signin::SetUnconsentedPrimaryAccount(identity_manager(), email); + return signin::SetPrimaryAccount(identity_manager(), email, + signin::ConsentLevel::kSignin); } void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() { @@ -398,7 +400,8 @@ void IdentityTestEnvironment::RemoveRefreshTokenForPrimaryAccount() { AccountInfo IdentityTestEnvironment::MakePrimaryAccountAvailable( const std::string& email) { - return signin::MakePrimaryAccountAvailable(identity_manager(), email); + return signin::MakePrimaryAccountAvailable(identity_manager(), email, + signin::ConsentLevel::kSync); } AccountInfo IdentityTestEnvironment::MakeUnconsentedPrimaryAccountAvailable( @@ -408,8 +411,8 @@ AccountInfo IdentityTestEnvironment::MakeUnconsentedPrimaryAccountAvailable( // Chrome OS sets the unconsented primary account during login and does not // allow signout. AccountInfo account_info = MakeAccountAvailable(email); - identity_manager()->GetPrimaryAccountMutator()->SetUnconsentedPrimaryAccount( - account_info.account_id); + identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( + account_info.account_id, signin::ConsentLevel::kSignin); #elif defined(OS_IOS) // iOS only support the primary account. AccountInfo account_info = MakePrimaryAccountAvailable(email); @@ -421,9 +424,8 @@ AccountInfo IdentityTestEnvironment::MakeUnconsentedPrimaryAccountAvailable( // Tests that don't use the |SigninManager| needs the unconsented primary // account to be set manually. if (!identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin)) { - identity_manager() - ->GetPrimaryAccountMutator() - ->SetUnconsentedPrimaryAccount(account_info.account_id); + identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount( + account_info.account_id, signin::ConsentLevel::kSignin); } #endif DCHECK(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin)); @@ -491,7 +493,7 @@ void IdentityTestEnvironment:: const std::string& token, const base::Time& expiration, const std::string& id_token) { - WaitForAccessTokenRequestIfNecessary(base::nullopt); + WaitForAccessTokenRequestIfNecessary(absl::nullopt); fake_token_service()->IssueTokenForAllPendingRequests( TokenResponseBuilder() .WithAccessToken(token) @@ -521,7 +523,7 @@ void IdentityTestEnvironment:: const base::Time& expiration, const std::string& id_token, const ScopeSet& scopes) { - WaitForAccessTokenRequestIfNecessary(base::nullopt); + WaitForAccessTokenRequestIfNecessary(absl::nullopt); fake_token_service()->IssueTokenForScope(scopes, TokenResponseBuilder() .WithAccessToken(token) @@ -533,7 +535,7 @@ void IdentityTestEnvironment:: void IdentityTestEnvironment:: WaitForAccessTokenRequestIfNecessaryAndRespondWithError( const GoogleServiceAuthError& error) { - WaitForAccessTokenRequestIfNecessary(base::nullopt); + WaitForAccessTokenRequestIfNecessary(absl::nullopt); fake_token_service()->IssueErrorForAllPendingRequests(error); } @@ -611,7 +613,7 @@ void IdentityTestEnvironment::HandleOnAccessTokenRequested( } void IdentityTestEnvironment::WaitForAccessTokenRequestIfNecessary( - base::Optional<CoreAccountId> account_id) { + absl::optional<CoreAccountId> account_id) { // Handle HandleOnAccessTokenRequested getting called before // WaitForAccessTokenRequestIfNecessary. if (account_id) { diff --git a/chromium/components/signin/public/identity_manager/identity_test_environment.h b/chromium/components/signin/public/identity_manager/identity_test_environment.h index 48054519d75..5db0d967ae9 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_environment.h +++ b/chromium/components/signin/public/identity_manager/identity_test_environment.h @@ -10,7 +10,6 @@ #include <vector> #include "base/callback.h" -#include "base/optional.h" #include "base/scoped_observation.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -19,6 +18,7 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/signin/public/identity_manager/scope_set.h" +#include "third_party/abseil-cpp/absl/types/optional.h" class FakeProfileOAuth2TokenService; class IdentityTestEnvironmentProfileAdaptor; @@ -346,7 +346,7 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver, kPending, kAvailable, } state; - base::Optional<CoreAccountId> account_id; + absl::optional<CoreAccountId> account_id; base::OnceClosure on_available; }; @@ -421,7 +421,7 @@ class IdentityTestEnvironment : public IdentityManager::DiagnosticsObserver, // Otherwise and runs a nested runloop until a matching access token request // is observed. void WaitForAccessTokenRequestIfNecessary( - base::Optional<CoreAccountId> account_id); + absl::optional<CoreAccountId> account_id); // Returns the FakeProfileOAuth2TokenService owned by IdentityManager. FakeProfileOAuth2TokenService* fake_token_service(); diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.cc b/chromium/components/signin/public/identity_manager/identity_test_utils.cc index 5562e06d554..81c68a3fcca 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_utils.cc +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.cc @@ -16,7 +16,6 @@ #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h" #include "components/signin/public/base/list_accounts_test_utils.h" -#include "components/signin/public/identity_manager/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/primary_account_mutator.h" #include "components/signin/public/identity_manager/test_identity_manager_observer.h" @@ -132,26 +131,9 @@ void WaitForRefreshTokensLoaded(IdentityManager* identity_manager) { } CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, - const std::string& email) { - DCHECK(!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); - PrimaryAccountManager* primary_account_manager = - identity_manager->GetPrimaryAccountManager(); - DCHECK(!primary_account_manager->HasPrimaryAccount(ConsentLevel::kSync)); - - AccountInfo account_info = - EnsureAccountExists(identity_manager->GetAccountTrackerService(), email); - DCHECK(!account_info.gaia.empty()); - - primary_account_manager->SetSyncPrimaryAccountInfo(account_info); - - DCHECK(primary_account_manager->HasPrimaryAccount(ConsentLevel::kSync)); - DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); - return identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); -} - -CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager, - const std::string& email) { - DCHECK(!identity_manager->HasPrimaryAccount(ConsentLevel::kSignin)); + const std::string& email, + ConsentLevel consent_level) { + DCHECK(!identity_manager->HasPrimaryAccount(consent_level)); AccountInfo account_info = EnsureAccountExists(identity_manager->GetAccountTrackerService(), email); @@ -159,48 +141,54 @@ CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager, PrimaryAccountManager* primary_account_manager = identity_manager->GetPrimaryAccountManager(); - primary_account_manager->SetUnconsentedPrimaryAccountInfo(account_info); - - DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSignin)); - DCHECK_EQ( - account_info.gaia, - identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin) - .gaia); - return identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); + switch (consent_level) { + case ConsentLevel::kSync: + primary_account_manager->SetSyncPrimaryAccountInfo(account_info); + break; + case ConsentLevel::kSignin: + primary_account_manager->SetUnconsentedPrimaryAccountInfo(account_info); + } + + DCHECK(identity_manager->HasPrimaryAccount(consent_level)); + DCHECK_EQ(account_info.gaia, + identity_manager->GetPrimaryAccountInfo(consent_level).gaia); + return identity_manager->GetPrimaryAccountInfo(consent_level); } void SetRefreshTokenForPrimaryAccount(IdentityManager* identity_manager, const std::string& token_value) { - DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); + DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync)); CoreAccountId account_id = - identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync); + identity_manager->GetPrimaryAccountId(ConsentLevel::kSync); SetRefreshTokenForAccount(identity_manager, account_id, token_value); } void SetInvalidRefreshTokenForPrimaryAccount( IdentityManager* identity_manager) { - DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); + DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync)); CoreAccountId account_id = - identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync); + identity_manager->GetPrimaryAccountId(ConsentLevel::kSync); SetInvalidRefreshTokenForAccount(identity_manager, account_id); } void RemoveRefreshTokenForPrimaryAccount(IdentityManager* identity_manager) { - if (!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) + if (!identity_manager->HasPrimaryAccount(ConsentLevel::kSync)) return; CoreAccountId account_id = - identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync); + identity_manager->GetPrimaryAccountId(ConsentLevel::kSync); RemoveRefreshTokenForAccount(identity_manager, account_id); } AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager, - const std::string& email) { - CoreAccountInfo account_info = SetPrimaryAccount(identity_manager, email); + const std::string& email, + ConsentLevel consent_level) { + CoreAccountInfo account_info = + SetPrimaryAccount(identity_manager, email, consent_level); SetRefreshTokenForPrimaryAccount(identity_manager); - base::Optional<AccountInfo> primary_account_info = + absl::optional<AccountInfo> primary_account_info = identity_manager ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId( account_info.account_id); diff --git a/chromium/components/signin/public/identity_manager/identity_test_utils.h b/chromium/components/signin/public/identity_manager/identity_test_utils.h index 619d83be4f0..40dc2edc7e6 100644 --- a/chromium/components/signin/public/identity_manager/identity_test_utils.h +++ b/chromium/components/signin/public/identity_manager/identity_test_utils.h @@ -10,6 +10,7 @@ #include "base/bind.h" #include "build/build_config.h" #include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/consent_level.h" namespace network { class TestURLLoaderFactory; @@ -39,18 +40,15 @@ class IdentityManager; void WaitForRefreshTokensLoaded(IdentityManager* identity_manager); // Sets the primary account (which must not already be set) to the given email -// address, generating a GAIA ID that corresponds uniquely to that email -// address. On non-ChromeOS, results in the firing of the IdentityManager and -// PrimaryAccountManager callbacks for signin success. Blocks until the primary -// account is set. Returns the CoreAccountInfo of the newly-set account. +// address with corresponding consent level, generating a GAIA ID that +// corresponds uniquely to that email address. On non-ChromeOS, results in the +// firing of the IdentityManager and PrimaryAccountManager callbacks for signin +// success. Blocks until the primary account is set. Returns the CoreAccountInfo +// of the newly-set account. // NOTE: See disclaimer at top of file re: direct usage. CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager, - const std::string& email); - -// As above, but adds an "unconsented" primary account. See ./README.md for -// the distinction between primary and unconsented primary accounts. -CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager, - const std::string& email); + const std::string& email, + ConsentLevel consent_level); // Sets a refresh token for the primary account (which must already be set). // Blocks until the refresh token is set. If |token_value| is empty a default @@ -78,7 +76,8 @@ void RemoveRefreshTokenForPrimaryAccount(IdentityManager* identity_manager); // newly-available account. // NOTE: See disclaimer at top of file re: direct usage. AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager, - const std::string& email); + const std::string& email, + ConsentLevel consent_level); // Revokes sync consent from the primary account: the primary account is left // at ConsentLevel::kSignin. diff --git a/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h index 975eccf41a5..3af5f868746 100644 --- a/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h +++ b/chromium/components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h @@ -6,7 +6,6 @@ #define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_OBJC_IDENTITY_MANAGER_OBSERVER_BRIDGE_H_ #import <Foundation/Foundation.h> -#include <vector> #include "components/signin/public/identity_manager/identity_manager.h" diff --git a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h index 0675918925b..f05f22c7a9a 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h +++ b/chromium/components/signin/public/identity_manager/primary_account_access_token_fetcher.h @@ -166,7 +166,7 @@ class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer { bool access_token_request_retried() { return access_token_retried_; } private: - // Returns the primary account ID. If consent is |kNotRequired| this may be + // Returns the primary account ID. If consent is |kSignin| this may be // the "unconsented" primary account ID. CoreAccountId GetAccountId() const; diff --git a/chromium/components/signin/public/identity_manager/primary_account_mutator.h b/chromium/components/signin/public/identity_manager/primary_account_mutator.h index 9695b5993b8..604daa2ab88 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_mutator.h +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator.h @@ -5,8 +5,6 @@ #ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_H_ #define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_MUTATOR_H_ -#include <string> - #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -18,6 +16,7 @@ enum class SignoutDelete; struct CoreAccountId; namespace signin { +enum class ConsentLevel; // PrimaryAccountMutator is the interface to set and clear the primary account // (see IdentityManager for more information). @@ -40,6 +39,7 @@ class PrimaryAccountMutator { PrimaryAccountMutator const& operator=(const PrimaryAccountMutator& other) = delete; + // For ConsentLevel::kSync - // Marks the account with |account_id| as the primary account, and returns // whether the operation succeeded or not. To succeed, this requires that: // - the account is known by the IdentityManager. @@ -49,14 +49,14 @@ class PrimaryAccountMutator { // - there is not already a primary account set. // TODO(https://crbug.com/983124): Investigate adding all the extra // requirements on ChromeOS as well. - virtual bool SetPrimaryAccount(const CoreAccountId& account_id) = 0; - + // + // For ConsentLevel::kSignin - // Sets the account with |account_id| as the unconsented primary account // (i.e. without implying browser sync consent). Requires that the account // is known by the IdentityManager. See README.md for details on the meaning - // of "unconsented". - virtual void SetUnconsentedPrimaryAccount( - const CoreAccountId& account_id) = 0; + // of "unconsented". Returns whether the operation succeeded or not. + virtual bool SetPrimaryAccount(const CoreAccountId& account_id, + ConsentLevel consent_level) = 0; // Revokes sync consent from the primary account. We distinguish the following // cases: diff --git a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc index d77e1876dc3..68288e3474b 100644 --- a/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc +++ b/chromium/components/signin/public/identity_manager/primary_account_mutator_unittest.cc @@ -127,8 +127,8 @@ void RunRevokeConsentTest( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); AccountInfo account_info = environment.MakeAccountAvailable(kPrimaryAccountEmail); - EXPECT_TRUE( - primary_account_mutator->SetPrimaryAccount(account_info.account_id)); + EXPECT_TRUE(primary_account_mutator->SetPrimaryAccount( + account_info.account_id, signin::ConsentLevel::kSync)); EXPECT_TRUE(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_TRUE(identity_manager->HasPrimaryAccountWithRefreshToken( signin::ConsentLevel::kSync)); @@ -265,8 +265,8 @@ TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount) { EXPECT_FALSE(environment.identity_manager()->HasPrimaryAccount( signin::ConsentLevel::kSync)); - EXPECT_TRUE( - primary_account_mutator->SetPrimaryAccount(account_info.account_id)); + EXPECT_TRUE(primary_account_mutator->SetPrimaryAccount( + account_info.account_id, signin::ConsentLevel::kSync)); EXPECT_TRUE(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_EQ(identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync), @@ -297,7 +297,7 @@ TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_NoAccount) { EXPECT_FALSE( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( - CoreAccountId(kUnknownAccountId))); + CoreAccountId(kUnknownAccountId), signin::ConsentLevel::kSync)); } // Checks that setting the primary account fails if the account is unknown. @@ -320,7 +320,7 @@ TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_UnknownAccount) { EXPECT_FALSE( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( - CoreAccountId(kUnknownAccountId))); + CoreAccountId(kUnknownAccountId), signin::ConsentLevel::kSync)); } // Checks that trying to set the primary account fails when there is already a @@ -346,11 +346,11 @@ TEST_F(PrimaryAccountMutatorTest, SetPrimaryAccount_AlreadyHasPrimaryAccount) { EXPECT_FALSE( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_TRUE(primary_account_mutator->SetPrimaryAccount( - primary_account_info.account_id)); + primary_account_info.account_id, signin::ConsentLevel::kSync)); EXPECT_TRUE(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( - another_account_info.account_id)); + another_account_info.account_id, signin::ConsentLevel::kSync)); EXPECT_EQ(identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync), primary_account_info.account_id); @@ -384,7 +384,7 @@ TEST_F(PrimaryAccountMutatorTest, EXPECT_FALSE( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)); EXPECT_FALSE(primary_account_mutator->SetPrimaryAccount( - primary_account_info.account_id)); + primary_account_info.account_id, signin::ConsentLevel::kSync)); } // End of tests of preconditions not being satisfied causing the setting of diff --git a/chromium/components/signin/public/webdata/token_web_data.cc b/chromium/components/signin/public/webdata/token_web_data.cc index 9d6077da0ab..a213d159f7c 100644 --- a/chromium/components/signin/public/webdata/token_web_data.cc +++ b/chromium/components/signin/public/webdata/token_web_data.cc @@ -13,7 +13,7 @@ #include "components/signin/public/webdata/token_service_table.h" #include "components/webdata/common/web_database_service.h" -using base::Bind; +using base::BindOnce; using base::Time; class TokenWebDataBackend @@ -78,27 +78,28 @@ TokenWebData::TokenWebData( void TokenWebData::SetTokenForService(const std::string& service, const std::string& token) { - wdbs_->ScheduleDBTask( - FROM_HERE, Bind(&TokenWebDataBackend::SetTokenForService, token_backend_, - service, token)); + wdbs_->ScheduleDBTask(FROM_HERE, + BindOnce(&TokenWebDataBackend::SetTokenForService, + token_backend_, service, token)); } void TokenWebData::RemoveAllTokens() { wdbs_->ScheduleDBTask( - FROM_HERE, Bind(&TokenWebDataBackend::RemoveAllTokens, token_backend_)); + FROM_HERE, + BindOnce(&TokenWebDataBackend::RemoveAllTokens, token_backend_)); } void TokenWebData::RemoveTokenForService(const std::string& service) { wdbs_->ScheduleDBTask(FROM_HERE, - Bind(&TokenWebDataBackend::RemoveTokenForService, - token_backend_, service)); + BindOnce(&TokenWebDataBackend::RemoveTokenForService, + token_backend_, service)); } // Null on failure. Success is WDResult<std::string> WebDataServiceBase::Handle TokenWebData::GetAllTokens( WebDataServiceConsumer* consumer) { return wdbs_->ScheduleDBTaskWithResult( - FROM_HERE, Bind(&TokenWebDataBackend::GetAllTokens, token_backend_), + FROM_HERE, BindOnce(&TokenWebDataBackend::GetAllTokens, token_backend_), consumer); } diff --git a/chromium/components/signin/public/webdata/token_web_data.h b/chromium/components/signin/public/webdata/token_web_data.h index 680b904ed7c..fdefff11e66 100644 --- a/chromium/components/signin/public/webdata/token_web_data.h +++ b/chromium/components/signin/public/webdata/token_web_data.h @@ -13,8 +13,6 @@ #include <string> #include <vector> -#include "base/callback_forward.h" -#include "base/files/file_path.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" |