diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-07-31 15:50:41 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:35:23 +0000 |
commit | 7b2ffa587235a47d4094787d72f38102089f402a (patch) | |
tree | 30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/components/signin | |
parent | d94af01c90575348c4e81a418257f254b6f8d225 (diff) | |
download | qtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz |
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/signin')
46 files changed, 2474 insertions, 1600 deletions
diff --git a/chromium/components/signin/OWNERS b/chromium/components/signin/OWNERS index ef731172939..184a152c6fd 100644 --- a/chromium/components/signin/OWNERS +++ b/chromium/components/signin/OWNERS @@ -1,3 +1,4 @@ +bsazonov@chromium.org droger@chromium.org msarda@chromium.org diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index 462981bb0f9..d2700ec7b52 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -32,6 +32,7 @@ static_library("shared") { "avatar_icon_util.h", "identity_utils.cc", "identity_utils.h", + "set_accounts_in_cookie_result.h", "signin_metrics.cc", "signin_metrics.h", "signin_pref_names.cc", @@ -80,6 +81,10 @@ static_library("internals") { "gaia_cookie_manager_service.h", "oauth2_token_service_delegate_android.cc", "oauth2_token_service_delegate_android.h", + "oauth_multilogin_helper.cc", + "oauth_multilogin_helper.h", + "oauth_multilogin_token_fetcher.cc", + "oauth_multilogin_token_fetcher.h", "profile_oauth2_token_service.cc", "profile_oauth2_token_service.h", "profile_oauth2_token_service_delegate_chromeos.cc", @@ -102,7 +107,6 @@ static_library("internals") { ":shared", ":signin_buildflags", "//base", - "//components/data_use_measurement/core", "//components/image_fetcher/core", "//components/keyed_service/core", "//components/prefs", @@ -118,7 +122,13 @@ static_library("internals") { } if (is_chromeos) { - deps += [ "//chromeos/components/account_manager" ] + deps += [ + "//chromeos/components/account_manager", + + # TODO(crbug.com/816954): Remove this line when Account Manager is + # launched. + "//chromeos/constants", + ] } } @@ -190,7 +200,6 @@ static_library("browser") { ] deps = [ "//base:i18n", - "//components/data_use_measurement/core", "//components/google/core/browser", "//components/metrics", "//components/os_crypt", @@ -274,9 +283,10 @@ source_set("unit_tests") { "device_id_helper_unittest.cc", "dice_account_reconcilor_delegate_unittest.cc", "gaia_cookie_manager_service_unittest.cc", - "identity_utils_unittest.cc", "mice_account_reconcilor_delegate_unittest.cc", "mutable_profile_oauth2_token_service_delegate_unittest.cc", + "oauth_multilogin_helper_unittest.cc", + "oauth_multilogin_token_fetcher_unittest.cc", "profile_oauth2_token_service_delegate_chromeos_unittest.cc", "signin_error_controller_unittest.cc", "signin_header_helper_unittest.cc", @@ -321,7 +331,10 @@ source_set("unit_tests") { } if (is_android) { - sources += [ "consistency_cookie_manager_unittest.cc" ] + sources += [ + "consistency_cookie_manager_unittest.cc", + "oauth2_token_service_delegate_android_unittest.cc", + ] } if (!enable_dice_support) { diff --git a/chromium/components/signin/core/browser/DEPS b/chromium/components/signin/core/browser/DEPS index 8ad0b6d3de8..6aa48343a03 100644 --- a/chromium/components/signin/core/browser/DEPS +++ b/chromium/components/signin/core/browser/DEPS @@ -1,7 +1,9 @@ include_rules = [ "+chromeos/components/account_manager", + # TODO(crbug.com/816954): Remove this line when Account Manager is + # launched. + "+chromeos/constants", "+components/account_id", - "+components/data_use_measurement/core", "+components/image_fetcher/core", "+components/metrics", "+jni", diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc index bdb28ddc86d..b3e4d988d4c 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.cc +++ b/chromium/components/signin/core/browser/about_signin_internals.cc @@ -33,6 +33,35 @@ namespace { // |kMaxRefreshTokenListSize| events are kept in memory. const size_t kMaxRefreshTokenListSize = 50; +enum class GaiaCookiesState { + kAllowed, + kClearOnExit, + kBlocked, +}; + +GaiaCookiesState GetGaiaCookiesState(SigninClient* signin_client) { + bool signin_cookies_allowed = signin_client->AreSigninCookiesAllowed(); + if (!signin_cookies_allowed) + return GaiaCookiesState::kBlocked; + + bool clear_cookies_on_exit = signin_client->AreSigninCookiesDeletedOnExit(); + if (clear_cookies_on_exit) + return GaiaCookiesState::kClearOnExit; + + return GaiaCookiesState::kAllowed; +} + +std::string GetGaiaCookiesStateAsString(const GaiaCookiesState state) { + switch (state) { + case GaiaCookiesState::kBlocked: + return "Not allowed"; + case GaiaCookiesState::kClearOnExit: + return "Cleared on exit"; + case GaiaCookiesState::kAllowed: + return "Allowed"; + } +} + std::string GetTimeStr(base::Time time) { return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)); } @@ -290,17 +319,31 @@ void AboutSigninInternals::Initialize(SigninClient* client) { RefreshSigninPrefs(); + client_->AddContentSettingsObserver(this); signin_error_controller_->AddObserver(this); identity_manager_->AddObserver(this); identity_manager_->AddDiagnosticsObserver(this); } void AboutSigninInternals::Shutdown() { + client_->RemoveContentSettingsObserver(this); signin_error_controller_->RemoveObserver(this); identity_manager_->RemoveObserver(this); identity_manager_->RemoveDiagnosticsObserver(this); } +void AboutSigninInternals::OnContentSettingChanged( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type, + const std::string& resource_identifier) { + // If this is not a change to cookie settings, just ignore. + if (content_type != CONTENT_SETTINGS_TYPE_COOKIES) + return; + + NotifyObservers(); +} + void AboutSigninInternals::NotifyObservers() { if (!signin_observers_.might_have_observers()) return; @@ -578,6 +621,9 @@ AboutSigninInternals::SigninStatus::ToValue( ->GetDetailedStateOfLoadingOfRefreshTokens(); AddSectionEntry(basic_info, "TokenService Load Status", TokenServiceLoadCredentialsStateToLabel(load_tokens_state)); + AddSectionEntry( + basic_info, "Gaia cookies state", + GetGaiaCookiesStateAsString(GetGaiaCookiesState(signin_client))); if (identity_manager->HasPrimaryAccount()) { CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo(); @@ -681,16 +727,16 @@ AboutSigninInternals::SigninStatus::ToValue( // Account info section auto account_info_section = std::make_unique<base::ListValue>(); - const std::vector<AccountInfo>& accounts_with_refresh_tokens = + const std::vector<CoreAccountInfo>& accounts_with_refresh_tokens = identity_manager->GetAccountsWithRefreshTokens(); if (accounts_with_refresh_tokens.size() == 0) { auto no_token_entry = std::make_unique<base::DictionaryValue>(); no_token_entry->SetString("accountId", "No token in Token Service."); account_info_section->Append(std::move(no_token_entry)); } else { - for (const AccountInfo account_info : accounts_with_refresh_tokens) { + for (const CoreAccountInfo& account_info : accounts_with_refresh_tokens) { auto entry = std::make_unique<base::DictionaryValue>(); - entry->SetString("accountId", account_info.account_id); + entry->SetString("accountId", account_info.account_id.id); // TODO(https://crbug.com/919793): Remove this field once the token // service is internally consistent on all platforms. entry->SetBoolean("hasRefreshToken", diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h index 370e47f21b9..ccfd208c6f8 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.h +++ b/chromium/components/signin/core/browser/about_signin_internals.h @@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "base/values.h" +#include "components/content_settings/core/browser/content_settings_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_error_controller.h" @@ -35,11 +36,11 @@ using TimedSigninStatusValue = std::pair<std::string, std::string>; // This class collects authentication, signin and token information // to propagate to about:signin-internals via SigninInternalsUI. -class AboutSigninInternals - : public KeyedService, - SigninErrorController::Observer, - identity::IdentityManager::Observer, - identity::IdentityManager::DiagnosticsObserver { +class AboutSigninInternals : public KeyedService, + public content_settings::Observer, + SigninErrorController::Observer, + identity::IdentityManager::Observer, + identity::IdentityManager::DiagnosticsObserver { public: class Observer { public: @@ -217,6 +218,12 @@ class AboutSigninInternals // SigninErrorController::Observer implementation void OnErrorChanged() override; + // content_settings::Observer implementation. + void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type, + const std::string& resource_identifier) override; + // Weak pointer to the identity manager. identity::IdentityManager* identity_manager_; diff --git a/chromium/components/signin/core/browser/account_fetcher_service.cc b/chromium/components/signin/core/browser/account_fetcher_service.cc index 059f0e34131..6ff6a0421b4 100644 --- a/chromium/components/signin/core/browser/account_fetcher_service.cc +++ b/chromium/components/signin/core/browser/account_fetcher_service.cc @@ -331,7 +331,6 @@ void AccountFetcherService::FetchAccountImage(const std::string& account_id) { void AccountFetcherService::OnUserInfoFetchFailure( const std::string& account_id) { LOG(WARNING) << "Failed to get UserInfo for " << account_id; - account_tracker_service_->NotifyAccountUpdateFailed(account_id); user_info_requests_.erase(account_id); } diff --git a/chromium/components/signin/core/browser/account_info.cc b/chromium/components/signin/core/browser/account_info.cc index c089abf4033..3c164ea34ab 100644 --- a/chromium/components/signin/core/browser/account_info.cc +++ b/chromium/components/signin/core/browser/account_info.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "components/signin/core/browser/account_info.h" +#include "google_apis/gaia/gaia_auth_util.h" namespace { @@ -103,6 +104,21 @@ bool AccountInfo::UpdateWith(const AccountInfo& other) { return modified; } +bool operator==(const CoreAccountInfo& l, const CoreAccountInfo& r) { + return l.account_id == r.account_id && l.gaia == r.gaia && + gaia::AreEmailsSame(l.email, r.email) && + l.is_under_advanced_protection == r.is_under_advanced_protection; +} +bool operator!=(const CoreAccountInfo& l, const CoreAccountInfo& r) { + return !(l == r); +} + +std::ostream& operator<<(std::ostream& os, const CoreAccountInfo& account) { + os << "account_id: " << account.account_id << ", gaia: " << account.gaia + << ", email: " << account.email << ", adv_prot: " << std::boolalpha + << account.is_under_advanced_protection; + return os; +} AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info) { if (account_info.email.empty() || account_info.gaia.empty()) diff --git a/chromium/components/signin/core/browser/account_info.h b/chromium/components/signin/core/browser/account_info.h index fa581f0ca88..ca47ac7fd82 100644 --- a/chromium/components/signin/core/browser/account_info.h +++ b/chromium/components/signin/core/browser/account_info.h @@ -8,6 +8,7 @@ #include <string> #include "components/account_id/account_id.h" +#include "google_apis/gaia/core_account_id.h" #include "ui/gfx/image/image.h" // Value representing no hosted domain associated with an account. @@ -29,7 +30,7 @@ struct CoreAccountInfo { CoreAccountInfo& operator=(const CoreAccountInfo& other); CoreAccountInfo& operator=(CoreAccountInfo&& other) noexcept; - std::string account_id; + CoreAccountId account_id; std::string gaia; std::string email; @@ -70,6 +71,10 @@ struct AccountInfo : public CoreAccountInfo { bool UpdateWith(const AccountInfo& other); }; +bool operator==(const CoreAccountInfo& l, const CoreAccountInfo& r); +bool operator!=(const CoreAccountInfo& l, const CoreAccountInfo& r); +std::ostream& operator<<(std::ostream& os, const CoreAccountInfo& account); + // Returns AccountID populated from |account_info|. AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info); diff --git a/chromium/components/signin/core/browser/account_reconcilor.cc b/chromium/components/signin/core/browser/account_reconcilor.cc index 4075ad6bb46..a2f98a11ed4 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.cc +++ b/chromium/components/signin/core/browser/account_reconcilor.cc @@ -23,6 +23,7 @@ #include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_reconcilor_delegate.h" #include "components/signin/core/browser/consistency_cookie_manager_base.h" +#include "components/signin/core/browser/set_accounts_in_cookie_result.h" #include "components/signin/core/browser/signin_buildflags.h" #include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_metrics.h" @@ -89,7 +90,7 @@ bool RevokeAllSecondaryTokens( if (revoke_option == AccountReconcilorDelegate::RevokeTokenOption::kDoNotRevoke) return false; - for (const AccountInfo& account_info : + for (const CoreAccountInfo& account_info : identity_manager->GetAccountsWithRefreshTokens()) { std::string account(account_info.account_id); if (account == primary_account) @@ -395,7 +396,7 @@ void AccountReconcilor::PerformSetCookiesAction( const signin::MultiloginParameters& parameters) { reconcile_is_noop_ = false; if (!delegate_->IsAccountConsistencyEnforced()) { - OnSetAccountsInCookieCompleted(GoogleServiceAuthError::AuthErrorNone()); + OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult::kSuccess); return; } VLOG(1) << "AccountReconcilor::PerformSetCookiesAction: " @@ -509,7 +510,8 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( // instead. PerformLogoutAllAccountsAction(); gaia_accounts.clear(); - OnSetAccountsInCookieCompleted(GoogleServiceAuthError::AuthErrorNone()); + OnSetAccountsInCookieCompleted( + signin::SetAccountsInCookieResult::kSuccess); DCHECK(!is_reconcile_started_); } else { // Reconcilor has to do some calls to gaia. is_reconcile_started_ is true @@ -521,7 +523,7 @@ void AccountReconcilor::FinishReconcileWithMultiloginEndpoint( } } else { // Nothing to do, accounts already match. - OnSetAccountsInCookieCompleted(GoogleServiceAuthError::AuthErrorNone()); + OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult::kSuccess); DCHECK(!is_reconcile_started_); } @@ -865,13 +867,17 @@ bool AccountReconcilor::IsIdentityManagerReady() { } void AccountReconcilor::OnSetAccountsInCookieCompleted( - const GoogleServiceAuthError& error) { + signin::SetAccountsInCookieResult result) { VLOG(1) << "AccountReconcilor::OnSetAccountsInCookieCompleted: " - << "Error was " << error.ToString(); + << "Error was " << static_cast<int>(result); if (is_reconcile_started_) { - if (error.state() != GoogleServiceAuthError::State::NONE && + if (result != signin::SetAccountsInCookieResult::kSuccess && !error_during_last_reconcile_.IsPersistentError()) { - error_during_last_reconcile_ = error; + error_during_last_reconcile_ = + result == signin::SetAccountsInCookieResult::kTransientError + ? GoogleServiceAuthError( + GoogleServiceAuthError::CONNECTION_FAILED) + : GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR); delegate_->OnReconcileError(error_during_last_reconcile_); } diff --git a/chromium/components/signin/core/browser/account_reconcilor.h b/chromium/components/signin/core/browser/account_reconcilor.h index adaad558514..9801137824e 100644 --- a/chromium/components/signin/core/browser/account_reconcilor.h +++ b/chromium/components/signin/core/browser/account_reconcilor.h @@ -35,6 +35,7 @@ extern const base::Feature kUseMultiloginEndpoint; namespace signin { class AccountReconcilorDelegate; class ConsistencyCookieManagerBase; +enum class SetAccountsInCookieResult; } class SigninClient; @@ -302,7 +303,7 @@ class AccountReconcilor : public KeyedService, void OnAddAccountToCookieCompleted(const std::string& account_id, const GoogleServiceAuthError& error); - void OnSetAccountsInCookieCompleted(const GoogleServiceAuthError& error); + void OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult result); // Lock related methods. void IncrementLockCount(); diff --git a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc index a7f1b917e46..a1d22a5f74e 100644 --- a/chromium/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/chromium/components/signin/core/browser/account_reconcilor_unittest.cc @@ -26,6 +26,7 @@ #include "components/signin/core/browser/list_accounts_test_utils.h" #include "components/signin/core/browser/mice_account_reconcilor_delegate.h" #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h" +#include "components/signin/core/browser/set_accounts_in_cookie_result.h" #include "components/signin/core/browser/signin_buildflags.h" #include "components/signin/core/browser/signin_metrics.h" #include "components/signin/core/browser/signin_pref_names.h" @@ -251,7 +252,7 @@ class AccountReconcilorTest : public ::testing::Test { void SimulateSetAccountsInCookieCompleted( AccountReconcilor* reconcilor, - const GoogleServiceAuthError& error); + signin::SetAccountsInCookieResult result); void SetAccountConsistency(signin::AccountConsistencyMethod method); @@ -327,8 +328,8 @@ INSTANTIATE_TEST_SUITE_P(Dice_Mirror, AccountReconcilorTest::AccountReconcilorTest() : account_consistency_(signin::AccountConsistencyMethod::kDisabled), - test_signin_client_(&pref_service_), - identity_test_env_(&test_url_loader_factory_, + test_signin_client_(&pref_service_, &test_url_loader_factory_), + identity_test_env_(/*test_url_loader_factory=*/nullptr, &pref_service_, account_consistency_, &test_signin_client_) { @@ -390,8 +391,8 @@ void AccountReconcilorTest::SimulateAddAccountToCookieCompleted( void AccountReconcilorTest::SimulateSetAccountsInCookieCompleted( AccountReconcilor* reconcilor, - const GoogleServiceAuthError& error) { - reconcilor->OnSetAccountsInCookieCompleted(error); + signin::SetAccountsInCookieResult result) { + reconcilor->OnSetAccountsInCookieCompleted(result); } void AccountReconcilorTest::SimulateCookieContentSettingsChanged( @@ -1040,8 +1041,8 @@ TEST_P(AccountReconcilorTestDiceMultilogin, TableRowTest) { GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; reconcilor->StartReconcile(); - SimulateSetAccountsInCookieCompleted(reconcilor, - GoogleServiceAuthError::AuthErrorNone()); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); ASSERT_FALSE(reconcilor->is_reconcile_started_); if (GetParam().tokens == GetParam().tokens_after_reconcile_multilogin) { @@ -1138,7 +1139,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceReconcileWithoutSignin) { reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1174,7 +1175,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, const std::string account_id_2 = account_info_2.account_id; auto* identity_manager = identity_test_env()->identity_manager(); - std::vector<AccountInfo> accounts = + std::vector<CoreAccountInfo> accounts = identity_manager->GetAccountsWithRefreshTokens(); ASSERT_EQ(2u, accounts.size()); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); @@ -1212,7 +1213,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, reconcilor, account_id_2, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1233,7 +1234,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { account_info_1.gaia, &test_url_loader_factory_); auto* identity_manager = identity_test_env()->identity_manager(); - std::vector<AccountInfo> accounts = + std::vector<CoreAccountInfo> accounts = identity_manager->GetAccountsWithRefreshTokens(); ASSERT_EQ(2u, accounts.size()); ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id_1)); @@ -1291,7 +1292,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceLastKnownFirstAccount) { reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1358,7 +1359,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, UnverifiedAccountMerge) { reconcilor, chrome_account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1454,7 +1455,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, DiceNoMigrationAfterReconcile) { reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1501,7 +1502,7 @@ TEST_P(AccountReconcilorDiceEndpointParamTest, MigrationClearSecondaryTokens) { reconcilor, account_id_1, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_SCHEDULED, @@ -1734,8 +1735,8 @@ TEST_P(AccountReconcilorTestMirrorMultilogin, TableRowTest) { GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; reconcilor->StartReconcile(); - SimulateSetAccountsInCookieCompleted(reconcilor, - GoogleServiceAuthError::AuthErrorNone()); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1851,8 +1852,8 @@ TEST_P(AccountReconcilorTestMiceMultilogin, TableRowTest) { GetParam().is_first_reconcile == IsFirstReconcile::kFirst ? true : false; reconcilor->StartReconcile(); - SimulateSetAccountsInCookieCompleted(reconcilor, - GoogleServiceAuthError::AuthErrorNone()); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -1933,8 +1934,8 @@ TEST_F(AccountReconcilorMiceTest, AccountReconcilorStateScheduled) { account_info.account_id); // Unblock the first reconcile. - SimulateSetAccountsInCookieCompleted(reconcilor, - GoogleServiceAuthError::AuthErrorNone()); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kSuccess); // Wait until the first reconcile finishes, and a second reconcile is done. // The second reconcile will be a no-op. base::RunLoop().RunUntilIdle(); @@ -1980,7 +1981,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, TokensNotLoaded) { reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -2088,7 +2089,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); @@ -2293,7 +2294,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileAddToCookie) { reconcilor, account_id2, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2398,12 +2399,14 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, base::RunLoop().RunUntilIdle(); ASSERT_TRUE(reconcilor->is_reconcile_started_); - GoogleServiceAuthError error( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); if (!IsMultiloginEnabled()) { - SimulateAddAccountToCookieCompleted(reconcilor, account_id2, error); + SimulateAddAccountToCookieCompleted( + reconcilor, account_id2, + GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); } else { - SimulateSetAccountsInCookieCompleted(reconcilor, error); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kPersistentError); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2452,7 +2455,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2521,7 +2524,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, reconcilor, account_id2, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2559,7 +2562,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, reconcilor, account_id3, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2617,7 +2620,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, StartReconcileBadPrimary) { reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2866,7 +2869,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, reconcilor, account_id, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } ASSERT_FALSE(reconcilor->is_reconcile_started_); } @@ -2912,7 +2915,8 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, NoLoopWithBadPrimary) { SimulateAddAccountToCookieCompleted( reconcilor, account_id2, GoogleServiceAuthError::AuthErrorNone()); } else { - SimulateSetAccountsInCookieCompleted(reconcilor, error); + SimulateSetAccountsInCookieCompleted( + reconcilor, signin::SetAccountsInCookieResult::kPersistentError); } base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); @@ -2972,7 +2976,7 @@ TEST_P(AccountReconcilorMirrorEndpointParamTest, WontMergeAccountsWithError) { reconcilor, account_id1, GoogleServiceAuthError::AuthErrorNone()); } else { SimulateSetAccountsInCookieCompleted( - reconcilor, GoogleServiceAuthError::AuthErrorNone()); + reconcilor, signin::SetAccountsInCookieResult::kSuccess); } base::RunLoop().RunUntilIdle(); ASSERT_FALSE(reconcilor->is_reconcile_started_); diff --git a/chromium/components/signin/core/browser/account_tracker_service.cc b/chromium/components/signin/core/browser/account_tracker_service.cc index 05be0c6698f..c30ba074019 100644 --- a/chromium/components/signin/core/browser/account_tracker_service.cc +++ b/chromium/components/signin/core/browser/account_tracker_service.cc @@ -46,28 +46,12 @@ const char kAccountChildAccountStatusPath[] = "is_child_account"; const char kAdvancedProtectionAccountStatusPath[] = "is_under_advanced_protection"; -// TODO(knn): Remove once deprecated service flags have been migrated from -// preferences. -const char kChildAccountServiceFlag[] = "uca"; - // Account folders used for storing account related data at disk. const base::FilePath::CharType kAccountsFolder[] = FILE_PATH_LITERAL("Accounts"); const base::FilePath::CharType kAvatarImagesFolder[] = FILE_PATH_LITERAL("Avatar Images"); -// TODO(M48): Remove deprecated preference migration. -const char kAccountServiceFlagsPath[] = "service_flags"; - -void RemoveDeprecatedServiceFlags(PrefService* pref_service) { - ListPrefUpdate update(pref_service, prefs::kAccountInfo); - for (size_t i = 0; i < update->GetSize(); ++i) { - base::DictionaryValue* dict = nullptr; - if (update->GetDictionary(i, &dict)) - dict->RemoveWithoutPathExpansion(kAccountServiceFlagsPath, nullptr); - } -} - // Reads a PNG image from disk and decodes it. If the reading/decoding attempt // was unsuccessful, an empty image is returned. gfx::Image ReadImage(const base::FilePath& image_path) { @@ -169,7 +153,7 @@ std::vector<AccountInfo> AccountTrackerService::GetAccounts() const { } AccountInfo AccountTrackerService::GetAccountInfo( - const std::string& account_id) const { + const CoreAccountId& account_id) const { const auto iterator = accounts_.find(account_id); if (iterator != accounts_.end()) return iterator->second; @@ -229,12 +213,6 @@ void AccountTrackerService::NotifyAccountUpdated( observer.OnAccountUpdated(account_info); } -void AccountTrackerService::NotifyAccountUpdateFailed( - const std::string& account_id) { - for (auto& observer : observer_list_) - observer.OnAccountUpdateFailed(account_id); -} - void AccountTrackerService::NotifyAccountRemoved( const AccountInfo& account_info) { DCHECK(!account_info.gaia.empty()); @@ -243,17 +221,18 @@ void AccountTrackerService::NotifyAccountRemoved( } void AccountTrackerService::StartTrackingAccount( - const std::string& account_id) { + const CoreAccountId& account_id) { if (!base::ContainsKey(accounts_, account_id)) { DVLOG(1) << "StartTracking " << account_id; AccountInfo account_info; account_info.account_id = account_id; account_info.is_child_account = false; - accounts_.insert(make_pair(account_id, account_info)); + accounts_.insert(std::make_pair(account_id, account_info)); } } -void AccountTrackerService::StopTrackingAccount(const std::string& account_id) { +void AccountTrackerService::StopTrackingAccount( + const CoreAccountId& account_id) { DVLOG(1) << "StopTracking " << account_id; if (base::ContainsKey(accounts_, account_id)) { AccountInfo account_info = std::move(accounts_[account_id]); @@ -267,7 +246,7 @@ void AccountTrackerService::StopTrackingAccount(const std::string& account_id) { } void AccountTrackerService::SetAccountInfoFromUserInfo( - const std::string& account_id, + const CoreAccountId& account_id, const base::DictionaryValue* user_info) { DCHECK(base::ContainsKey(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; @@ -290,7 +269,7 @@ void AccountTrackerService::SetAccountInfoFromUserInfo( SaveToPrefs(account_info); } -void AccountTrackerService::SetAccountImage(const std::string& account_id, +void AccountTrackerService::SetAccountImage(const CoreAccountId& account_id, const gfx::Image& image) { if (!base::ContainsKey(accounts_, account_id)) return; @@ -300,7 +279,7 @@ void AccountTrackerService::SetAccountImage(const std::string& account_id, NotifyAccountUpdated(account_info); } -void AccountTrackerService::SetIsChildAccount(const std::string& account_id, +void AccountTrackerService::SetIsChildAccount(const CoreAccountId& account_id, bool is_child_account) { DCHECK(base::ContainsKey(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; @@ -313,7 +292,7 @@ void AccountTrackerService::SetIsChildAccount(const std::string& account_id, } void AccountTrackerService::SetIsAdvancedProtectionAccount( - const std::string& account_id, + const CoreAccountId& account_id, bool is_under_advanced_protection) { DCHECK(base::ContainsKey(accounts_, account_id)); AccountInfo& account_info = accounts_[account_id]; @@ -328,10 +307,10 @@ void AccountTrackerService::SetIsAdvancedProtectionAccount( void AccountTrackerService::MigrateToGaiaId() { DCHECK_EQ(GetMigrationState(), MIGRATION_IN_PROGRESS); - std::vector<std::string> to_remove; + std::vector<CoreAccountId> to_remove; std::vector<AccountInfo> migrated_accounts; for (const auto& pair : accounts_) { - const std::string& new_account_id = pair.second.gaia; + const CoreAccountId new_account_id(pair.second.gaia); if (pair.first == new_account_id) continue; @@ -353,7 +332,7 @@ void AccountTrackerService::MigrateToGaiaId() { for (AccountInfo& new_account_info : migrated_accounts) { // Copy the AccountInfo |gaia| member field so that it is not left in // an undeterminate state in the structure after std::map::emplace call. - std::string account_id = new_account_info.gaia; + CoreAccountId account_id = new_account_info.account_id; SaveToPrefs(new_account_info); accounts_.emplace(std::move(account_id), std::move(new_account_info)); @@ -414,14 +393,15 @@ AccountTrackerService::GetMigrationState(const PrefService* pref_service) { } base::FilePath AccountTrackerService::GetImagePathFor( - const std::string& account_id) { + const CoreAccountId& account_id) { return user_data_dir_.Append(kAccountsFolder) .Append(kAvatarImagesFolder) - .AppendASCII(account_id); + .AppendASCII(account_id.id); } -void AccountTrackerService::OnAccountImageLoaded(const std::string& account_id, - gfx::Image image) { +void AccountTrackerService::OnAccountImageLoaded( + const CoreAccountId& account_id, + gfx::Image image) { if (base::ContainsKey(accounts_, account_id) && accounts_[account_id].account_image.IsEmpty()) { AccountInfo& account_info = accounts_[account_id]; @@ -434,7 +414,7 @@ void AccountTrackerService::LoadAccountImagesFromDisk() { if (!image_storage_task_runner_) return; for (const auto& pair : accounts_) { - const std::string& account_id = pair.second.account_id; + const CoreAccountId& account_id = pair.second.account_id; PostTaskAndReplyWithResult( image_storage_task_runner_.get(), FROM_HERE, base::BindOnce(&ReadImage, GetImagePathFor(account_id)), @@ -444,7 +424,7 @@ void AccountTrackerService::LoadAccountImagesFromDisk() { } void AccountTrackerService::SaveAccountImageToDisk( - const std::string& account_id, + const CoreAccountId& account_id, const gfx::Image& image) { if (!image_storage_task_runner_) return; @@ -454,7 +434,7 @@ void AccountTrackerService::SaveAccountImageToDisk( } void AccountTrackerService::RemoveAccountImageFromDisk( - const std::string& account_id) { + const CoreAccountId& account_id) { if (!image_storage_task_runner_) return; image_storage_task_runner_->PostTask( @@ -463,55 +443,39 @@ void AccountTrackerService::RemoveAccountImageFromDisk( void AccountTrackerService::LoadFromPrefs() { const base::ListValue* list = pref_service_->GetList(prefs::kAccountInfo); - std::set<std::string> to_remove; - bool contains_deprecated_service_flags = false; + std::set<CoreAccountId> to_remove; for (size_t i = 0; i < list->GetSize(); ++i) { const base::DictionaryValue* dict; if (list->GetDictionary(i, &dict)) { - base::string16 value; + std::string value; if (dict->GetString(kAccountKeyPath, &value)) { - std::string account_id = base::UTF16ToUTF8(value); - // Ignore incorrectly persisted non-canonical account ids. - if (account_id.find('@') != std::string::npos && - account_id != gaia::CanonicalizeEmail(account_id)) { - to_remove.insert(account_id); + if (value.find('@') != std::string::npos && + value != gaia::CanonicalizeEmail(value)) { + to_remove.insert(CoreAccountId(value)); continue; } + CoreAccountId account_id(value); StartTrackingAccount(account_id); AccountInfo& account_info = accounts_[account_id]; if (dict->GetString(kAccountGaiaPath, &value)) - account_info.gaia = base::UTF16ToUTF8(value); + account_info.gaia = value; if (dict->GetString(kAccountEmailPath, &value)) - account_info.email = base::UTF16ToUTF8(value); + account_info.email = value; if (dict->GetString(kAccountHostedDomainPath, &value)) - account_info.hosted_domain = base::UTF16ToUTF8(value); + account_info.hosted_domain = value; if (dict->GetString(kAccountFullNamePath, &value)) - account_info.full_name = base::UTF16ToUTF8(value); + account_info.full_name = value; if (dict->GetString(kAccountGivenNamePath, &value)) - account_info.given_name = base::UTF16ToUTF8(value); + account_info.given_name = value; if (dict->GetString(kAccountLocalePath, &value)) - account_info.locale = base::UTF16ToUTF8(value); + account_info.locale = value; if (dict->GetString(kAccountPictureURLPath, &value)) - account_info.picture_url = base::UTF16ToUTF8(value); + account_info.picture_url = value; bool is_child_account = false; - // Migrate deprecated service flag preference. - const base::ListValue* service_flags_list; - if (dict->GetList(kAccountServiceFlagsPath, &service_flags_list)) { - contains_deprecated_service_flags = true; - std::string flag_string; - for (const auto& flag : *service_flags_list) { - if (flag.GetAsString(&flag_string) && - flag_string == kChildAccountServiceFlag) { - is_child_account = true; - break; - } - } - account_info.is_child_account = is_child_account; - } if (dict->GetBoolean(kAccountChildAccountStatusPath, &is_child_account)) account_info.is_child_account = is_child_account; @@ -528,12 +492,6 @@ void AccountTrackerService::LoadFromPrefs() { } } - UMA_HISTOGRAM_BOOLEAN("Signin.AccountTracker.DeprecatedServiceFlagDeleted", - contains_deprecated_service_flags); - - if (contains_deprecated_service_flags) - RemoveDeprecatedServiceFlags(pref_service_); - // Remove any obsolete prefs. for (auto account_id : to_remove) { AccountInfo account_info; @@ -572,12 +530,12 @@ void AccountTrackerService::SaveToPrefs(const AccountInfo& account_info) { return; base::DictionaryValue* dict = nullptr; - base::string16 account_id_16 = base::UTF8ToUTF16(account_info.account_id); ListPrefUpdate update(pref_service_, prefs::kAccountInfo); for (size_t i = 0; i < update->GetSize(); ++i, dict = nullptr) { if (update->GetDictionary(i, &dict)) { - base::string16 value; - if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) + std::string value; + if (dict->GetString(kAccountKeyPath, &value) && + value == account_info.account_id.id) break; } } @@ -587,7 +545,7 @@ void AccountTrackerService::SaveToPrefs(const AccountInfo& account_info) { update->Append(base::WrapUnique(dict)); // |dict| is invalidated at this point, so it needs to be reset. update->GetDictionary(update->GetSize() - 1, &dict); - dict->SetString(kAccountKeyPath, account_id_16); + dict->SetString(kAccountKeyPath, account_info.account_id.id); } dict->SetString(kAccountEmailPath, account_info.email); @@ -607,13 +565,13 @@ void AccountTrackerService::RemoveFromPrefs(const AccountInfo& account_info) { if (!pref_service_) return; - base::string16 account_id_16 = base::UTF8ToUTF16(account_info.account_id); ListPrefUpdate update(pref_service_, prefs::kAccountInfo); for (size_t i = 0; i < update->GetSize(); ++i) { base::DictionaryValue* dict = nullptr; if (update->GetDictionary(i, &dict)) { - base::string16 value; - if (dict->GetString(kAccountKeyPath, &value) && value == account_id_16) { + std::string value; + if (dict->GetString(kAccountKeyPath, &value) && + value == account_info.account_id.id) { update->Remove(i, nullptr); break; } @@ -621,14 +579,14 @@ void AccountTrackerService::RemoveFromPrefs(const AccountInfo& account_info) { } } -std::string AccountTrackerService::PickAccountIdForAccount( +CoreAccountId AccountTrackerService::PickAccountIdForAccount( const std::string& gaia, const std::string& email) const { return PickAccountIdForAccount(pref_service_, gaia, email); } // static -std::string AccountTrackerService::PickAccountIdForAccount( +CoreAccountId AccountTrackerService::PickAccountIdForAccount( const PrefService* pref_service, const std::string& gaia, const std::string& email) { @@ -639,29 +597,24 @@ std::string AccountTrackerService::PickAccountIdForAccount( case MIGRATION_NOT_STARTED: // Some tests don't use a real email address. To support these cases, // don't try to canonicalize these strings. - return (email.find('@') == std::string::npos) - ? email - : gaia::CanonicalizeEmail(email); + return CoreAccountId(email.find('@') == std::string::npos + ? email + : gaia::CanonicalizeEmail(email)); case MIGRATION_IN_PROGRESS: case MIGRATION_DONE: - return gaia; + return CoreAccountId(gaia); default: NOTREACHED(); - return email; + return CoreAccountId(email); } } -std::string AccountTrackerService::SeedAccountInfo(const std::string& gaia, - const std::string& email) { - const std::string account_id = PickAccountIdForAccount(gaia, email); - const bool already_exists = base::ContainsKey(accounts_, account_id); - StartTrackingAccount(account_id); - AccountInfo& account_info = accounts_[account_id]; - DCHECK(!already_exists || account_info.gaia.empty() || - account_info.gaia == gaia); +CoreAccountId AccountTrackerService::SeedAccountInfo(const std::string& gaia, + const std::string& email) { + AccountInfo account_info; account_info.gaia = gaia; account_info.email = email; - SaveToPrefs(account_info); + CoreAccountId account_id = SeedAccountInfo(account_info); DVLOG(1) << "AccountTrackerService::SeedAccountInfo" << " account_id=" << account_id << " gaia_id=" << gaia @@ -670,14 +623,15 @@ std::string AccountTrackerService::SeedAccountInfo(const std::string& gaia, return account_id; } -std::string AccountTrackerService::SeedAccountInfo(AccountInfo info) { +CoreAccountId AccountTrackerService::SeedAccountInfo(AccountInfo info) { info.account_id = PickAccountIdForAccount(info.gaia, info.email); - if (!base::ContainsKey(accounts_, info.account_id)) { - StartTrackingAccount(info.account_id); - } - + const bool already_exists = base::ContainsKey(accounts_, info.account_id); + StartTrackingAccount(info.account_id); AccountInfo& account_info = accounts_[info.account_id]; + DCHECK(!already_exists || account_info.gaia.empty() || + account_info.gaia == info.gaia); + // Update the missing fields in |account_info| with |info|. if (account_info.UpdateWith(info)) { if (!account_info.gaia.empty()) @@ -688,7 +642,7 @@ std::string AccountTrackerService::SeedAccountInfo(AccountInfo info) { return info.account_id; } -void AccountTrackerService::RemoveAccount(const std::string& account_id) { +void AccountTrackerService::RemoveAccount(const CoreAccountId& account_id) { StopTrackingAccount(account_id); } diff --git a/chromium/components/signin/core/browser/account_tracker_service.h b/chromium/components/signin/core/browser/account_tracker_service.h index 5bf928b49e5..e0511f891da 100644 --- a/chromium/components/signin/core/browser/account_tracker_service.h +++ b/chromium/components/signin/core/browser/account_tracker_service.h @@ -19,6 +19,7 @@ #include "build/build_config.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/core/browser/account_info.h" +#include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_auth_util.h" #include "ui/gfx/image/image.h" @@ -46,9 +47,8 @@ void SimulateSuccessfulFetchOfAccountInfo(IdentityManager*, const std::string&); } -// AccountTrackerService is a KeyedService that retrieves and caches GAIA -// information about Google Accounts. -class AccountTrackerService : public KeyedService { +// Retrieves and caches GAIA information about Google Accounts. +class AccountTrackerService { public: // Clients of AccountTrackerService can implement this interface and register // with AddObserver() to learn about account information changes. @@ -56,7 +56,6 @@ class AccountTrackerService : public KeyedService { public: virtual ~Observer() {} virtual void OnAccountUpdated(const AccountInfo& info) {} - virtual void OnAccountUpdateFailed(const std::string& account_id) {} virtual void OnAccountRemoved(const AccountInfo& info) {} }; @@ -72,13 +71,12 @@ class AccountTrackerService : public KeyedService { }; AccountTrackerService(); - ~AccountTrackerService() override; + ~AccountTrackerService(); // Registers the preferences used by AccountTrackerService. static void RegisterPrefs(PrefRegistrySimple* registry); - // KeyedService implementation. - void Shutdown() override; + void Shutdown(); void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); @@ -91,37 +89,38 @@ class AccountTrackerService : public KeyedService { // Returns the list of known accounts and for which gaia IDs // have been fetched. std::vector<AccountInfo> GetAccounts() const; - AccountInfo GetAccountInfo(const std::string& account_id) const; + AccountInfo GetAccountInfo(const CoreAccountId& account_id) const; AccountInfo FindAccountInfoByGaiaId(const std::string& gaia_id) const; AccountInfo FindAccountInfoByEmail(const std::string& email) const; // Picks the correct account_id for the specified account depending on the // migration state. - std::string PickAccountIdForAccount(const std::string& gaia, - const std::string& email) const; - static std::string PickAccountIdForAccount(const PrefService* pref_service, - const std::string& gaia, - const std::string& email); + CoreAccountId PickAccountIdForAccount(const std::string& gaia, + const std::string& email) const; + static CoreAccountId PickAccountIdForAccount(const PrefService* pref_service, + const std::string& gaia, + const std::string& email); // Seeds the account whose account_id is given by PickAccountIdForAccount() // with its corresponding gaia id and email address. Returns the same // value PickAccountIdForAccount() when given the same arguments. - std::string SeedAccountInfo(const std::string& gaia, - const std::string& email); + CoreAccountId SeedAccountInfo(const std::string& gaia, + const std::string& email); // Seeds the account represented by |info|. If the account is already tracked // and compatible, the empty fields will be updated with values from |info|. // If after the update IsValid() is true, OnAccountUpdated will be fired. - std::string SeedAccountInfo(AccountInfo info); + CoreAccountId SeedAccountInfo(AccountInfo info); // Sets whether the account is a Unicorn account. - void SetIsChildAccount(const std::string& account_id, bool is_child_account); + void SetIsChildAccount(const CoreAccountId& account_id, + bool is_child_account); // Sets whether the account is under advanced protection. - void SetIsAdvancedProtectionAccount(const std::string& account_id, + void SetIsAdvancedProtectionAccount(const CoreAccountId& account_id, bool is_under_advanced_protection); - void RemoveAccount(const std::string& account_id); + void RemoveAccount(const CoreAccountId& account_id); // Is migration of the account id from normalized email to gaia id supported // on the current platform? @@ -137,12 +136,13 @@ class AccountTrackerService : public KeyedService { protected: // Available to be called in tests. - void SetAccountInfoFromUserInfo(const std::string& account_id, + void SetAccountInfoFromUserInfo(const CoreAccountId& account_id, const base::DictionaryValue* user_info); // Updates the account image. Does nothing if |account_id| does not exist in // |accounts_|. - void SetAccountImage(const std::string& account_id, const gfx::Image& image); + void SetAccountImage(const CoreAccountId& account_id, + const gfx::Image& image); private: friend class AccountFetcherService; @@ -158,11 +158,10 @@ class AccountTrackerService : public KeyedService { const std::string&); void NotifyAccountUpdated(const AccountInfo& account_info); - void NotifyAccountUpdateFailed(const std::string& account_id); - void NotifyAccountRemoved(const AccountInfo& accoint_info); + void NotifyAccountRemoved(const AccountInfo& account_info); - void StartTrackingAccount(const std::string& account_id); - void StopTrackingAccount(const std::string& account_id); + void StartTrackingAccount(const CoreAccountId& account_id); + void StopTrackingAccount(const CoreAccountId& account_id); // Load the current state of the account info from the preferences file. void LoadFromPrefs(); @@ -170,12 +169,12 @@ class AccountTrackerService : public KeyedService { void RemoveFromPrefs(const AccountInfo& account); // Used to load/save account images from/to disc. - base::FilePath GetImagePathFor(const std::string& account_id); - void OnAccountImageLoaded(const std::string& account_id, gfx::Image image); + base::FilePath GetImagePathFor(const CoreAccountId& account_id); + void OnAccountImageLoaded(const CoreAccountId& account_id, gfx::Image image); void LoadAccountImagesFromDisk(); - void SaveAccountImageToDisk(const std::string& account_id, + void SaveAccountImageToDisk(const CoreAccountId& account_id, const gfx::Image& image); - void RemoveAccountImageFromDisk(const std::string& account_id); + void RemoveAccountImageFromDisk(const CoreAccountId& account_id); // Migrate accounts to be keyed by gaia id instead of normalized email. // Requires that the migration state is set to MIGRATION_IN_PROGRESS. @@ -198,7 +197,7 @@ class AccountTrackerService : public KeyedService { const PrefService* pref_service); PrefService* pref_service_ = nullptr; // Not owned. - std::map<std::string, AccountInfo> accounts_; + std::map<CoreAccountId, AccountInfo> accounts_; base::ObserverList<Observer>::Unchecked observer_list_; base::FilePath user_data_dir_; diff --git a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc index 7b0aae13a82..46eac21d21b 100644 --- a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc +++ b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc @@ -120,12 +120,16 @@ class TrackingEvent { public: TrackingEvent(TrackingEventType type, const std::string& account_id, - const std::string& gaia_id) - : type_(type), account_id_(account_id), gaia_id_(gaia_id) {} + const std::string& gaia_id, + const std::string& email) + : type_(type), + account_id_(account_id), + gaia_id_(gaia_id), + email_(email) {} bool operator==(const TrackingEvent& event) const { return type_ == event.type_ && account_id_ == event.account_id_ && - (gaia_id_.empty() || gaia_id_ == event.gaia_id_); + gaia_id_ == event.gaia_id_ && email_ == event.email_; } std::string ToString() const { @@ -138,8 +142,9 @@ class TrackingEvent { typestr = "REM"; break; } - return base::StringPrintf("{ type: %s, account_id: %s, gaia: %s }", typestr, - account_id_.c_str(), gaia_id_.c_str()); + return base::StringPrintf( + "{ type: %s, account_id: %s, gaia: %s, email: %s }", typestr, + account_id_.c_str(), gaia_id_.c_str(), email_.c_str()); } private: @@ -148,6 +153,7 @@ class TrackingEvent { TrackingEventType type_; std::string account_id_; std::string gaia_id_; + std::string email_; }; bool CompareByUser(TrackingEvent a, TrackingEvent b) { @@ -187,11 +193,13 @@ class AccountTrackerObserver : public AccountTrackerService::Observer { }; void AccountTrackerObserver::OnAccountUpdated(const AccountInfo& ids) { - events_.push_back(TrackingEvent(UPDATED, ids.account_id, ids.gaia)); + events_.push_back( + TrackingEvent(UPDATED, ids.account_id, ids.gaia, ids.email)); } void AccountTrackerObserver::OnAccountRemoved(const AccountInfo& ids) { - events_.push_back(TrackingEvent(REMOVED, ids.account_id, ids.gaia)); + events_.push_back( + TrackingEvent(REMOVED, ids.account_id, ids.gaia, ids.email)); } void AccountTrackerObserver::Clear() { @@ -331,8 +339,8 @@ class AccountTrackerServiceTest : public testing::Test { PrefService* prefs() { return &pref_service_; } AccountTrackerObserver* observer() { return &observer_; } - network::TestURLLoaderFactory* test_url_loader_factory() { - return signin_client_.test_url_loader_factory(); + network::TestURLLoaderFactory* GetTestURLLoaderFactory() { + return signin_client_.GetTestURLLoaderFactory(); } bool* force_account_id_to_email_for_legacy_tests_pointer() { @@ -398,11 +406,11 @@ void AccountTrackerServiceTest::ReturnFetchResults( net::HttpStatusCode response_code, const std::string& response_string) { GURL url = GaiaUrls::GetInstance()->oauth_user_info_url(); - EXPECT_TRUE(test_url_loader_factory()->IsPending(url.spec())); + EXPECT_TRUE(GetTestURLLoaderFactory()->IsPending(url.spec())); // It's possible for multiple requests to be pending. Respond to all of them. - while (test_url_loader_factory()->IsPending(url.spec())) { - test_url_loader_factory()->SimulateResponseForPendingRequest( + while (GetTestURLLoaderFactory()->IsPending(url.spec())) { + GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( url, network::URLLoaderCompletionStatus(net::OK), network::CreateResourceResponseHead(response_code), response_string, network::TestURLLoaderFactory::kMostRecentMatch); @@ -430,14 +438,14 @@ void AccountTrackerServiceTest::ReturnAccountInfoFetchFailure( void AccountTrackerServiceTest::ReturnAccountImageFetchSuccess( AccountKey account_key) { - test_url_loader_factory()->AddResponse( + GetTestURLLoaderFactory()->AddResponse( AccountKeyToPictureURLWithSize(account_key), "image data"); scoped_task_environment_.RunUntilIdle(); } void AccountTrackerServiceTest::ReturnAccountImageFetchFailure( AccountKey account_key) { - test_url_loader_factory()->AddResponse( + GetTestURLLoaderFactory()->AddResponse( AccountKeyToPictureURLWithSize(account_key), std::string(), net::HTTP_BAD_REQUEST); scoped_task_environment_.RunUntilIdle(); @@ -464,7 +472,8 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageSuccess) { EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); EXPECT_TRUE(account_tracker() @@ -473,7 +482,8 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageSuccess) { ReturnAccountImageFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); EXPECT_FALSE(account_tracker() ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha)) @@ -486,7 +496,8 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_ImageFailure) { EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); EXPECT_TRUE(account_tracker() @@ -504,12 +515,14 @@ TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_Revoked) { EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); SimulateTokenRevoked(kAccountKeyAlpha); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); } @@ -526,7 +539,8 @@ TEST_F(AccountTrackerServiceTest, TokenAvailableTwice_UserInfoOnce) { EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); SimulateTokenAvailable(kAccountKeyAlpha); @@ -549,9 +563,11 @@ TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_TwoUserInfo) { EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta), - AccountKeyToGaiaId(kAccountKeyBeta)), + AccountKeyToGaiaId(kAccountKeyBeta), + AccountKeyToEmail(kAccountKeyBeta)), })); } @@ -562,13 +578,15 @@ TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_OneUserInfo) { EXPECT_FALSE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta), - AccountKeyToGaiaId(kAccountKeyBeta)), + AccountKeyToGaiaId(kAccountKeyBeta), + AccountKeyToEmail(kAccountKeyBeta)), })); ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(account_fetcher()->IsAllUserInfoFetched()); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); } @@ -619,7 +637,7 @@ TEST_F(AccountTrackerServiceTest, GetAccountInfo_TokenAvailable_EnableNetwork) { SimulateTokenAvailable(kAccountKeyAlpha); IssueAccessToken(kAccountKeyAlpha); // No fetcher has been created yet. - EXPECT_EQ(0, test_url_loader_factory()->NumPending()); + EXPECT_EQ(0, GetTestURLLoaderFactory()->NumPending()); // Enable the network to create the fetcher then issue the access token. account_fetcher()->EnableNetworkFetchesForTest(); @@ -689,17 +707,21 @@ TEST_F(AccountTrackerServiceTest, Persistence) { EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta), - AccountKeyToGaiaId(kAccountKeyBeta)), + AccountKeyToGaiaId(kAccountKeyBeta), + AccountKeyToEmail(kAccountKeyBeta)), })); // Wait until all account images are loaded. scoped_task_environment_.RunUntilIdle(); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyBeta), - AccountKeyToGaiaId(kAccountKeyBeta)), + AccountKeyToGaiaId(kAccountKeyBeta), + AccountKeyToEmail(kAccountKeyBeta)), })); std::vector<AccountInfo> infos = account_tracker()->GetAccounts(); @@ -740,20 +762,36 @@ TEST_F(AccountTrackerServiceTest, Persistence) { } TEST_F(AccountTrackerServiceTest, SeedAccountInfo) { - std::vector<AccountInfo> infos = account_tracker()->GetAccounts(); - EXPECT_EQ(0u, infos.size()); + EXPECT_TRUE(account_tracker()->GetAccounts().empty()); - const std::string gaia_id = AccountKeyToGaiaId(kAccountKeyAlpha); - const std::string email = AccountKeyToEmail(kAccountKeyAlpha); + const std::string gaia_id = AccountKeyToGaiaId(kAccountKeyFooBar); + const std::string email = AccountKeyToEmail(kAccountKeyFooBar); + const std::string email_dotted = AccountKeyToEmail(kAccountKeyFooDotBar); const std::string account_id = account_tracker()->PickAccountIdForAccount(gaia_id, email); - account_tracker()->SeedAccountInfo(gaia_id, email); - infos = account_tracker()->GetAccounts(); + account_tracker()->SeedAccountInfo(gaia_id, email); + auto infos = account_tracker()->GetAccounts(); ASSERT_EQ(1u, infos.size()); EXPECT_EQ(account_id, infos[0].account_id); EXPECT_EQ(gaia_id, infos[0].gaia); EXPECT_EQ(email, infos[0].email); + EXPECT_TRUE(observer()->CheckEvents({ + TrackingEvent(UPDATED, account_id, gaia_id, email), + })); + + account_tracker()->SeedAccountInfo(gaia_id, email_dotted); + infos = account_tracker()->GetAccounts(); + ASSERT_EQ(1u, infos.size()) << "Seeding information to an existing account " + "should not add a new account"; + EXPECT_EQ(account_id, infos[0].account_id) + << "Account id is either the canonicalized email or gaia, it should " + "remain the same"; + EXPECT_EQ(gaia_id, infos[0].gaia); + EXPECT_EQ(email_dotted, infos[0].email) << "Email should be changed"; + EXPECT_TRUE(observer()->CheckEvents({ + TrackingEvent(UPDATED, account_id, gaia_id, email_dotted), + })); } TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { @@ -770,7 +808,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { EXPECT_EQ(info.email, stored_info.email); EXPECT_EQ(info.full_name, stored_info.full_name); EXPECT_TRUE(observer()->CheckEvents({ - TrackingEvent(UPDATED, info.account_id, info.gaia), + TrackingEvent(UPDATED, info.account_id, info.gaia, info.email), })); // Validate that seeding new full informations to an existing account works @@ -785,7 +823,7 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { EXPECT_EQ(info.email, stored_info.email); EXPECT_EQ(info.given_name, stored_info.given_name); EXPECT_TRUE(observer()->CheckEvents({ - TrackingEvent(UPDATED, info.account_id, info.gaia), + TrackingEvent(UPDATED, info.account_id, info.gaia, info.email), })); // Validate that seeding invalid information to an existing account doesn't @@ -827,7 +865,8 @@ TEST_F(AccountTrackerServiceTest, UpgradeToFullAccountInfo) { EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyIncomplete), - AccountKeyToGaiaId(kAccountKeyIncomplete)), + AccountKeyToGaiaId(kAccountKeyIncomplete), + AccountKeyToEmail(kAccountKeyIncomplete)), })); // Enabling network fetches shouldn't cause any actual fetch since the @@ -923,48 +962,6 @@ TEST_F(AccountTrackerServiceTest, LegacyDottedAccountIds) { EXPECT_EQ(AccountKeyToEmail(kAccountKeyFooBar), infos[0].email); } -TEST_F(AccountTrackerServiceTest, NoDeprecatedServiceFlags) { - const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha); - const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha); - - ListPrefUpdate update(prefs(), prefs::kAccountInfo); - - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); - dict->SetString("account_id", email_alpha); - dict->SetString("email", email_alpha); - dict->SetString("gaia", gaia_alpha); - update->Append(std::move(dict)); - - base::HistogramTester tester; - - ResetAccountTracker(); - tester.ExpectBucketCount("Signin.AccountTracker.DeprecatedServiceFlagDeleted", - false, 1); -} - -TEST_F(AccountTrackerServiceTest, MigrateDeprecatedServiceFlags) { - const std::string email_alpha = AccountKeyToEmail(kAccountKeyAlpha); - const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha); - - ListPrefUpdate update(prefs(), prefs::kAccountInfo); - - std::unique_ptr<base::ListValue> service_flags(new base::ListValue()); - service_flags->Append(std::make_unique<base::Value>("uca")); - - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); - dict->SetString("account_id", email_alpha); - dict->SetString("email", email_alpha); - dict->SetString("gaia", gaia_alpha); - dict->SetList("service_flags", std::move(service_flags)); - update->Append(std::move(dict)); - - base::HistogramTester tester; - - ResetAccountTracker(); - tester.ExpectBucketCount("Signin.AccountTracker.DeprecatedServiceFlagDeleted", - true, 1); -} - TEST_F(AccountTrackerServiceTest, MigrateAccountIdToGaiaId) { if (!AccountTrackerService::IsMigrationSupported()) return; @@ -1158,7 +1155,8 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevoked) { GenerateValidTokenInfoResponse(kAccountKeyChild)); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); AccountInfo info = account_tracker()->GetAccountInfo( AccountKeyToAccountId(kAccountKeyChild)); @@ -1166,7 +1164,8 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevoked) { SimulateTokenRevoked(kAccountKeyChild); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); } @@ -1184,7 +1183,8 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevokedWithUpdate) { GenerateValidTokenInfoResponse(kAccountKeyChild)); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); AccountInfo info = account_tracker()->GetAccountInfo( AccountKeyToAccountId(kAccountKeyChild)); @@ -1194,14 +1194,17 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedAndRevokedWithUpdate) { // On Android, is_child_account is set to false before removing it. EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); #else EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); #endif } @@ -1221,23 +1224,28 @@ TEST_F(AccountTrackerServiceTest, ChildAccountUpdatedTwiceThenRevoked) { #endif EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); SimulateTokenRevoked(kAccountKeyChild); #if defined(OS_ANDROID) // On Android, is_child_account is set to false before removing it. EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); #else EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); #endif } @@ -1261,7 +1269,8 @@ TEST_F(AccountTrackerServiceTest, ChildAccountGraduation) { GenerateValidTokenInfoResponse(kAccountKeyChild)); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); // Now simulate child account graduation. @@ -1277,13 +1286,15 @@ TEST_F(AccountTrackerServiceTest, ChildAccountGraduation) { EXPECT_FALSE(info.is_child_account); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); SimulateTokenRevoked(kAccountKeyChild); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyChild), - AccountKeyToGaiaId(kAccountKeyChild)), + AccountKeyToGaiaId(kAccountKeyChild), + AccountKeyToEmail(kAccountKeyChild)), })); } @@ -1293,14 +1304,16 @@ TEST_F(AccountTrackerServiceTest, RemoveAccountBeforeImageFetchDone) { ReturnAccountInfoFetchSuccess(kAccountKeyAlpha); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(UPDATED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); SimulateTokenRevoked(kAccountKeyAlpha); ReturnAccountImageFetchFailure(kAccountKeyAlpha); EXPECT_TRUE(observer()->CheckEvents({ TrackingEvent(REMOVED, AccountKeyToAccountId(kAccountKeyAlpha), - AccountKeyToGaiaId(kAccountKeyAlpha)), + AccountKeyToGaiaId(kAccountKeyAlpha), + AccountKeyToEmail(kAccountKeyAlpha)), })); } diff --git a/chromium/components/signin/core/browser/android/OWNERS b/chromium/components/signin/core/browser/android/OWNERS deleted file mode 100644 index e50e23c175d..00000000000 --- a/chromium/components/signin/core/browser/android/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -bsazonov@chromium.org - -# COMPONENT: Services>SignIn diff --git a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java index 2158328dbbb..c05c650293f 100644 --- a/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java +++ b/chromium/components/signin/core/browser/android/java/src/org/chromium/components/signin/OAuth2TokenService.java @@ -23,11 +23,8 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; /** * Java instance for the native OAuth2TokenService. @@ -257,49 +254,6 @@ public final class OAuth2TokenService } /** - * Call this method to retrieve an OAuth2 access token for the given account and scope. This - * method times out after the specified timeout, and will return null if that happens. - * - * Given that this is a blocking method call, this should never be called from the UI thread. - * - * @param account the account to get the access token for. - * @param scope The scope to get an auth token for (without Android-style 'oauth2:' prefix). - * @param timeout the timeout. - * @param unit the unit for |timeout|. - */ - @VisibleForTesting - public static String getAccessTokenWithTimeout( - Account account, String scope, long timeout, TimeUnit unit) { - assert !ThreadUtils.runningOnUiThread(); - final AtomicReference<String> result = new AtomicReference<>(); - final Semaphore semaphore = new Semaphore(0); - getAccessToken(account, scope, new GetAccessTokenCallback() { - @Override - public void onGetTokenSuccess(String token) { - result.set(token); - semaphore.release(); - } - - @Override - public void onGetTokenFailure(boolean isTransientError) { - result.set(null); - semaphore.release(); - } - }); - try { - if (semaphore.tryAcquire(timeout, unit)) { - return result.get(); - } else { - Log.d(TAG, "Failed to retrieve auth token within timeout (%s %s)", timeout, unit); - return null; - } - } catch (InterruptedException e) { - Log.w(TAG, "Got interrupted while waiting for auth token"); - return null; - } - } - - /** * Called by native to check whether the account has an OAuth2 refresh token. */ @CalledByNative @@ -394,7 +348,12 @@ public final class OAuth2TokenService } @CalledByNative - private static void saveStoredAccounts(String[] accounts) { + /** + * Called by native to save the account IDs that have associated OAuth2 refresh tokens. + * This is called during updateAccountList to sync with getSystemAccountNames. + * @param accounts IDs to save. + */ + private static void setAccounts(String[] accounts) { Set<String> set = new HashSet<>(Arrays.asList(accounts)); ContextUtils.getAppSharedPreferences() .edit() diff --git a/chromium/components/signin/core/browser/avatar_icon_util.cc b/chromium/components/signin/core/browser/avatar_icon_util.cc index 5e613d24b9c..2a74566b464 100644 --- a/chromium/components/signin/core/browser/avatar_icon_util.cc +++ b/chromium/components/signin/core/browser/avatar_icon_util.cc @@ -5,6 +5,7 @@ #include "components/signin/core/browser/avatar_icon_util.h" #include <string> +#include <vector> #include "base/stl_util.h" #include "base/strings/string_split.h" @@ -16,20 +17,25 @@ namespace { // Separator of URL path components. -const char kURLPathSeparator[] = "/"; +constexpr char kURLPathSeparator[] = "/"; -// Constants describing image URL format. +// Constants describing legacy image URL format. // See https://crbug.com/733306#c3 for details. -const size_t kImageURLPathComponentsCount = 6; -const size_t kImageURLPathComponentsCountWithOptions = 7; -const size_t kImageURLPathOptionsComponentPosition = 5; +constexpr size_t kLegacyURLPathComponentsCount = 6; +constexpr size_t kLegacyURLPathComponentsCountWithOptions = 7; +constexpr size_t kLegacyURLPathOptionsComponentPosition = 5; +// Constants describing content image URL format. +// See https://crbug.com/911332#c15 for details. +constexpr size_t kContentURLPathMinComponentsCount = 2; +constexpr size_t kContentURLPathMaxComponentsCount = 3; +constexpr char kContentURLOptionsStartChar = '='; // Various options that can be embedded in image URL. -const char kImageURLOptionSeparator[] = "-"; -const char kImageURLOptionSizePattern[] = R"(s\d+)"; -const char kImageURLOptionSizeFormat[] = "s%d"; -const char kImageURLOptionSquareCrop[] = "c"; +constexpr char kImageURLOptionSeparator[] = "-"; +constexpr char kImageURLOptionSizePattern[] = R"(s\d+)"; +constexpr char kImageURLOptionSizeFormat[] = "s%d"; +constexpr char kImageURLOptionSquareCrop[] = "c"; // Option to disable default avatar if user doesn't have a custom one. -const char kImageURLOptionNoSilhouette[] = "ns"; +constexpr char kImageURLOptionNoSilhouette[] = "ns"; std::string BuildImageURLOptionsString(int image_size, bool no_silhouette, @@ -53,6 +59,62 @@ std::string BuildImageURLOptionsString(int image_size, return base::JoinString(url_options, kImageURLOptionSeparator); } +// Returns an empty vector if |url_components| couldn't be processed as a legacy +// image URL. +std::vector<std::string> TryProcessAsLegacyImageURL( + std::vector<std::string> url_components, + int image_size, + bool no_silhouette) { + if (url_components.back().empty()) + return {}; + + if (url_components.size() == kLegacyURLPathComponentsCount) { + url_components.insert( + url_components.begin() + kLegacyURLPathOptionsComponentPosition, + BuildImageURLOptionsString(image_size, no_silhouette, std::string())); + return url_components; + } + + if (url_components.size() == kLegacyURLPathComponentsCountWithOptions) { + std::string options = + url_components.at(kLegacyURLPathOptionsComponentPosition); + url_components[kLegacyURLPathOptionsComponentPosition] = + BuildImageURLOptionsString(image_size, no_silhouette, options); + return url_components; + } + + return {}; +} + +// Returns an empty vector if |url_components| couldn't be processed as a +// content image URL. +std::vector<std::string> TryProcessAsContentImageURL( + std::vector<std::string> url_components, + int image_size, + bool no_silhouette) { + if (url_components.size() < kContentURLPathMinComponentsCount || + url_components.size() > kContentURLPathMaxComponentsCount || + url_components.back().empty()) { + return {}; + } + + std::string* options_component = &url_components.back(); + // Extract existing options from |options_component|. + const size_t options_pos = + options_component->find(kContentURLOptionsStartChar); + std::string component_without_options = + options_component->substr(0, options_pos); + std::string existing_options = + options_pos == std::string::npos + ? "" + : options_component->substr(options_pos + 1); + // Update options in |options_component|. + *options_component = + component_without_options + kContentURLOptionsStartChar + + BuildImageURLOptionsString(image_size, no_silhouette, existing_options); + return url_components; +} + } // namespace namespace signin { @@ -66,24 +128,20 @@ GURL GetAvatarImageURLWithOptions(const GURL& old_url, base::SplitString(old_url.path(), kURLPathSeparator, base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - if (components.size() < kImageURLPathComponentsCount || - components.size() > kImageURLPathComponentsCountWithOptions || - components.back().empty()) { - return old_url; + auto new_components = + TryProcessAsContentImageURL(components, image_size, no_silhouette); + + if (new_components.empty()) { + new_components = + TryProcessAsLegacyImageURL(components, image_size, no_silhouette); } - if (components.size() == kImageURLPathComponentsCount) { - components.insert( - components.begin() + kImageURLPathOptionsComponentPosition, - BuildImageURLOptionsString(image_size, no_silhouette, std::string())); - } else { - DCHECK_EQ(kImageURLPathComponentsCountWithOptions, components.size()); - std::string options = components.at(kImageURLPathOptionsComponentPosition); - components[kImageURLPathOptionsComponentPosition] = - BuildImageURLOptionsString(image_size, no_silhouette, options); + if (new_components.empty()) { + // URL doesn't match any known patterns, so return unchanged. + return old_url; } - std::string new_path = base::JoinString(components, kURLPathSeparator); + std::string new_path = base::JoinString(new_components, kURLPathSeparator); GURL::Replacements replacement; replacement.SetPathStr(new_path); return old_url.ReplaceComponents(replacement); diff --git a/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc b/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc index 7f592916406..f3686240080 100644 --- a/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc +++ b/chromium/components/signin/core/browser/avatar_icon_util_unittest.cc @@ -9,7 +9,7 @@ namespace { -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoInitialSize) { +TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoInitialSize_LegacyURL) { GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/photo.jpg"); GURL result = signin::GetAvatarImageURLWithOptions(in, 128, false /* no_silhouette */); @@ -18,7 +18,8 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoInitialSize) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeAlreadySpecified) { +TEST(AvatarIconUtilTest, + GetAvatarImageURLWithOptionsSizeAlreadySpecified_LegacyURL) { // If there's already a size specified in the URL, it should be changed to the // specified size in the resulting URL. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-c/photo.jpg"); @@ -29,7 +30,8 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeAlreadySpecified) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsOtherSizeSpecified) { +TEST(AvatarIconUtilTest, + GetAvatarImageURLWithOptionsOtherSizeSpecified_LegacyURL) { // If there's already a size specified in the URL, it should be changed to the // specified size in the resulting URL. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s128-c/photo.jpg"); @@ -40,7 +42,7 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsOtherSizeSpecified) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSameSize) { +TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSameSize_LegacyURL) { // If there's already a size specified in the URL, and it's already the // requested size, true should be returned and the URL should remain // unchanged. @@ -52,7 +54,8 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSameSize) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoFileNameInPath) { +TEST(AvatarIconUtilTest, + GetAvatarImageURLWithOptionsNoFileNameInPath_LegacyURL) { // If there is no file path component in the URL path, we should return // the input URL. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/"); @@ -61,7 +64,7 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoFileNameInPath) { EXPECT_EQ(result, in); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoSilhouette) { +TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoSilhouette_LegacyURL) { // If there's already a size specified in the URL, it should be changed to the // specified size in the resulting URL. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/photo.jpg"); @@ -72,7 +75,8 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsNoSilhouette) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeReplaceNoSilhouette) { +TEST(AvatarIconUtilTest, + GetAvatarImageURLWithOptionsSizeReplaceNoSilhouette_LegacyURL) { // If there's already a no_silhouette option specified in the URL, it should // be removed if necessary. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-c-ns/photo.jpg"); @@ -83,7 +87,8 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsSizeReplaceNoSilhouette) { EXPECT_EQ(result, expected); } -TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsUnknownShouldBePreserved) { +TEST(AvatarIconUtilTest, + GetAvatarImageURLWithOptionsUnknownShouldBePreserved_LegacyURL) { // If there are any unknown options encoded in URL, // GetAvatarImageURLWithOptions should preserve them. GURL in("http://example.com/-A/AAAAAAAAAAI/AAAAAAAAACQ/B/s64-mo-k/photo.jpg"); @@ -94,4 +99,20 @@ TEST(AvatarIconUtilTest, GetAvatarImageURLWithOptionsUnknownShouldBePreserved) { EXPECT_EQ(result, expected); } +TEST(AvatarIconUtilTest, GetAvatarImageURLWithExistingOptions_ContentURL) { + GURL in("http://example.com/-A/ABcdefghijk1l2mN3=s256"); + GURL result = + signin::GetAvatarImageURLWithOptions(in, 128, false /* no_silhouette */); + GURL expected("http://example.com/-A/ABcdefghijk1l2mN3=s128-c"); + EXPECT_EQ(result, expected); +} + +TEST(AvatarIconUtilTest, GetAvatarImageURLNoExistingOptions_ContentURL) { + GURL in("http://example.com/-A/ABcdefghijk1l2mN3"); + GURL result = + signin::GetAvatarImageURLWithOptions(in, 128, true /* no_silhouette */); + GURL expected("http://example.com/-A/ABcdefghijk1l2mN3=s128-c-ns"); + EXPECT_EQ(result, expected); +} + } // namespace diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc index 7a576c1c6cb..7f95e128b2f 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc +++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc @@ -10,6 +10,8 @@ #include <set> #include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" #include "base/json/json_reader.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -20,14 +22,13 @@ #include "base/time/time.h" #include "base/values.h" #include "build/build_config.h" -#include "components/data_use_measurement/core/data_use_user_data.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/set_accounts_in_cookie_result.h" #include "components/signin/core/browser/signin_metrics.h" #include "components/signin/core/browser/ubertoken_fetcher_impl.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/oauth2_token_service.h" -#include "mojo/public/cpp/bindings/callback_helpers.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/cookies/cookie_change_dispatcher.h" @@ -37,6 +38,10 @@ #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/simple_url_loader.h" +#if defined(OS_CHROMEOS) +#include "chromeos/constants/chromeos_switches.h" +#endif + namespace signin { MultiloginParameters::MultiloginParameters( const gaia::MultiloginMode mode, @@ -108,20 +113,10 @@ void RecordListAccountsFailure(GoogleServiceAuthError::State error_state) { GoogleServiceAuthError::NUM_STATES); } -void RecordGetAccessTokenFinished(GoogleServiceAuthError error) { - UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenFinished", error.state(), - GoogleServiceAuthError::NUM_STATES); -} - void RecordLogoutRequestState(LogoutRequestState logout_state) { UMA_HISTOGRAM_ENUMERATION("Signin.GaiaCookieManager.Logout", logout_state); } -void RecordMultiloginFinished(GoogleServiceAuthError error) { - UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginFinished", error.state(), - GoogleServiceAuthError::NUM_STATES); -} - // Record ListAccounts errors for individual retries. void RecordListAccountsRetryResult(GoogleServiceAuthError error, int retry_attempt_number) { @@ -191,9 +186,9 @@ const std::string GaiaCookieManagerService::GaiaCookieRequest::GetAccountID() { void GaiaCookieManagerService::GaiaCookieRequest:: RunSetAccountsInCookieCompletedCallback( - const GoogleServiceAuthError& error) { + signin::SetAccountsInCookieResult result) { if (set_accounts_in_cookie_completed_callback_) - std::move(set_accounts_in_cookie_completed_callback_).Run(error); + std::move(set_accounts_in_cookie_completed_callback_).Run(result); } void GaiaCookieManagerService::GaiaCookieRequest:: @@ -270,7 +265,7 @@ void GaiaCookieManagerService::ExternalCcResultFetcher::Start( CleanupTransientState(); results_.clear(); helper_->gaia_auth_fetcher_ = helper_->signin_client_->CreateGaiaAuthFetcher( - this, gaia::GaiaSource::kChrome, helper_->GetURLLoaderFactory()); + this, gaia::GaiaSource::kChrome); helper_->gaia_auth_fetcher_->StartGetCheckConnectionInfo(); // Some fetches may timeout. Start a timer to decide when the result fetcher @@ -373,10 +368,7 @@ GaiaCookieManagerService::ExternalCcResultFetcher::CreateAndStartLoader( auto request = std::make_unique<network::ResourceRequest>(); request->url = url; - request->load_flags = - net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES; - // TODO(https://crbug.com/808498) re-add data use measurement once - // SimpleURLLoader supports it: data_use_measurement::DataUseUserData::SIGNIN + request->allow_credentials = false; std::unique_ptr<network::SimpleURLLoader> loader = network::SimpleURLLoader::Create(std::move(request), traffic_annotation); @@ -461,21 +453,8 @@ void GaiaCookieManagerService::ExternalCcResultFetcher:: GaiaCookieManagerService::GaiaCookieManagerService( OAuth2TokenService* token_service, SigninClient* signin_client) - : GaiaCookieManagerService( - token_service, - signin_client, - base::BindRepeating(&SigninClient::GetURLLoaderFactory, - base::Unretained(signin_client))) {} - -GaiaCookieManagerService::GaiaCookieManagerService( - OAuth2TokenService* token_service, - SigninClient* signin_client, - base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()> - shared_url_loader_factory_getter) - : OAuth2TokenService::Consumer("gaia_cookie_manager"), - token_service_(token_service), + : token_service_(token_service), signin_client_(signin_client), - shared_url_loader_factory_getter_(shared_url_loader_factory_getter), external_cc_result_fetcher_(this), fetcher_backoff_(&kBackoffPolicy), fetcher_retries_(0), @@ -524,39 +503,15 @@ void GaiaCookieManagerService::SetAccountsInCookie( account_ids, source, std::move(set_accounts_in_cookies_completed_callback))); if (!signin_client_->AreSigninCookiesAllowed()) { - OnSetAccountsFinished( - GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + OnSetAccountsFinished(signin::SetAccountsInCookieResult::kPersistentError); return; } if (requests_.size() == 1) { fetcher_retries_ = 0; - signin_client_->DelayNetworkCall(base::BindOnce( - &GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin, - base::Unretained(this))); + StartSetAccounts(); } } -void GaiaCookieManagerService::SetAccountsInCookieWithTokens() { -#ifndef NDEBUG - // Check that there is no duplicate accounts. - std::set<std::string> accounts_no_duplicates( - requests_.front().account_ids().begin(), - requests_.front().account_ids().end()); - DCHECK_EQ(requests_.front().account_ids().size(), - accounts_no_duplicates.size()); -#endif - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.reserve(requests_.front().account_ids().size()); - int i = 0; - for (const std::string& account_id : requests_.front().account_ids()) { - accounts.emplace_back(account_id, access_tokens_[account_id]); - ++i; - } - StartFetchingMultiLogin(accounts); -} - void GaiaCookieManagerService::AddAccountToCookieInternal( const std::string& account_id, gaia::GaiaSource source, @@ -575,7 +530,7 @@ void GaiaCookieManagerService::AddAccountToCookieInternal( if (requests_.size() == 1) { signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingUbertoken, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } } @@ -627,7 +582,7 @@ void GaiaCookieManagerService::TriggerListAccounts() { requests_.push_back(GaiaCookieRequest::CreateListAccountsRequest()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } else if (std::find_if(requests_.begin(), requests_.end(), [](const GaiaCookieRequest& request) { return request.request_type() == LIST_ACCOUNTS; @@ -689,8 +644,9 @@ void GaiaCookieManagerService::LogOutAllAccounts(gaia::GaiaSource source) { requests_.push_back(GaiaCookieRequest::CreateLogOutRequest(source)); if (requests_.size() == 1) { fetcher_retries_ = 0; - signin_client_->DelayNetworkCall(base::BindOnce( - &GaiaCookieManagerService::StartGaiaLogOut, base::Unretained(this))); + signin_client_->DelayNetworkCall( + base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut, + weak_ptr_factory_.GetWeakPtr())); } } } @@ -707,13 +663,14 @@ void GaiaCookieManagerService::CancelAll() { VLOG(1) << "GaiaCookieManagerService::CancelAll"; gaia_auth_fetcher_.reset(); uber_token_fetcher_.reset(); + oauth_multilogin_helper_.reset(); requests_.clear(); fetcher_timer_.Stop(); } scoped_refptr<network::SharedURLLoaderFactory> GaiaCookieManagerService::GetURLLoaderFactory() { - return shared_url_loader_factory_getter_.Run(); + return signin_client_->GetURLLoaderFactory(); } void GaiaCookieManagerService::MarkListAccountsStale() { @@ -750,7 +707,7 @@ void GaiaCookieManagerService::OnCookieChange( fetcher_retries_ = 0; signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } } @@ -778,10 +735,10 @@ void GaiaCookieManagerService::SignalAddToCookieComplete( } void GaiaCookieManagerService::SignalSetAccountsComplete( - const GoogleServiceAuthError& error) { + signin::SetAccountsInCookieResult result) { DCHECK(requests_.front().request_type() == GaiaCookieRequestType::SET_ACCOUNTS); - requests_.front().RunSetAccountsInCookieCompletedCallback(error); + requests_.front().RunSetAccountsInCookieCompletedCallback(result); } void GaiaCookieManagerService::OnUbertokenFetchComplete( @@ -814,54 +771,7 @@ void GaiaCookieManagerService::OnUbertokenFetchComplete( signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingMergeSession, - base::Unretained(this))); -} - -void GaiaCookieManagerService::OnTokenFetched(const std::string& account_id, - const std::string& token) { - access_tokens_.insert(std::make_pair(account_id, token)); - if (access_tokens_.size() == requests_.front().account_ids().size()) { - fetcher_retries_ = 0; - token_requests_.clear(); - signin_client_->DelayNetworkCall( - base::BindOnce(&GaiaCookieManagerService::SetAccountsInCookieWithTokens, - base::Unretained(this))); - } -} - -void GaiaCookieManagerService::OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) { - DCHECK(requests_.front().request_type() == - GaiaCookieRequestType::SET_ACCOUNTS); - fetcher_backoff_.InformOfRequest(true); - OnTokenFetched(request->GetAccountId(), token_response.access_token); -} - -void GaiaCookieManagerService::OnGetTokenFailure( - const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) { - VLOG(1) << "Failed to retrieve accesstoken" - << " account=" << request->GetAccountId() - << " error=" << error.ToString(); - if (++fetcher_retries_ < signin::kMaxFetcherRetries && - error.IsTransientError()) { - fetcher_backoff_.InformOfRequest(false); - UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenRetry", error.state(), - GoogleServiceAuthError::NUM_STATES); - OAuth2TokenService::ScopeSet scopes; - scopes.insert(GaiaConstants::kOAuth1LoginScope); - fetcher_timer_.Start( - FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), - base::BindOnce( - &SigninClient::DelayNetworkCall, base::Unretained(signin_client_), - base::BindOnce(&GaiaCookieManagerService:: - StartFetchingAccessTokenForMultilogin, - base::Unretained(this), request->GetAccountId()))); - return; - } - RecordGetAccessTokenFinished(error); - OnSetAccountsFinished(error); + weak_ptr_factory_.GetWeakPtr())); } void GaiaCookieManagerService::OnMergeSessionSuccess(const std::string& data) { @@ -896,7 +806,7 @@ void GaiaCookieManagerService::OnMergeSessionFailure( base::BindOnce( &SigninClient::DelayNetworkCall, base::Unretained(signin_client_), base::BindOnce(&GaiaCookieManagerService::StartFetchingMergeSession, - base::Unretained(this)))); + weak_ptr_factory_.GetWeakPtr()))); return; } @@ -908,53 +818,6 @@ void GaiaCookieManagerService::OnMergeSessionFailure( HandleNextRequest(); } -void GaiaCookieManagerService::OnOAuthMultiloginFinished( - const OAuthMultiloginResult& result) { - DCHECK(requests_.front().request_type() == - GaiaCookieRequestType::SET_ACCOUNTS); - RecordMultiloginFinished(result.error()); - if (result.error().state() == GoogleServiceAuthError::NONE) { - VLOG(1) << "Multilogin successful accounts=" - << base::JoinString(requests_.front().account_ids(), " "); - std::vector<std::string> account_ids = requests_.front().account_ids(); - access_tokens_.clear(); - fetcher_backoff_.InformOfRequest(true); - StartSettingCookies(result); - return; - } - if (++fetcher_retries_ < signin::kMaxFetcherRetries && - result.error().IsTransientError()) { - UMA_HISTOGRAM_ENUMERATION("Signin.MultiloginRetry", result.error().state(), - GoogleServiceAuthError::NUM_STATES); - fetcher_backoff_.InformOfRequest(false); - fetcher_timer_.Start( - FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), - base::BindOnce( - &SigninClient::DelayNetworkCall, base::Unretained(signin_client_), - base::BindOnce( - &GaiaCookieManagerService::SetAccountsInCookieWithTokens, - base::Unretained(this)))); - return; - } - // If Gaia responded with status: "INVALID_TOKENS", we have to mark tokens as - // invalid. - if (result.error().state() == - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) { - for (const std::string& account_id : result.failed_accounts()) { - DCHECK(base::ContainsKey(access_tokens_, account_id)); - token_service_->InvalidateTokenForMultilogin(account_id, - access_tokens_[account_id]); - access_tokens_.erase(account_id); - } - for (const std::string& account_id : result.failed_accounts()) { - // Maybe the access token was expired, try to get a new one. - StartFetchingAccessTokenForMultilogin(account_id); - } - return; - } - OnSetAccountsFinished(result.error()); -} - void GaiaCookieManagerService::OnListAccountsSuccess(const std::string& data) { VLOG(1) << "ListAccounts successful"; DCHECK(requests_.front().request_type() == @@ -1012,7 +875,7 @@ void GaiaCookieManagerService::OnListAccountsFailure( base::BindOnce( &SigninClient::DelayNetworkCall, base::Unretained(signin_client_), base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, - base::Unretained(this)))); + weak_ptr_factory_.GetWeakPtr()))); return; } @@ -1044,42 +907,16 @@ void GaiaCookieManagerService::OnLogOutFailure( fetcher_backoff_.InformOfRequest(false); fetcher_timer_.Start( FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), - base::BindOnce(&SigninClient::DelayNetworkCall, - base::Unretained(signin_client_), - base::Bind(&GaiaCookieManagerService::StartGaiaLogOut, - base::Unretained(this)))); + base::BindOnce( + &SigninClient::DelayNetworkCall, base::Unretained(signin_client_), + base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut, + weak_ptr_factory_.GetWeakPtr()))); return; } HandleNextRequest(); } -void GaiaCookieManagerService::StartFetchingAccessTokenForMultilogin( - const std::string& account_id) { - token_requests_.push_back( - token_service_->StartRequestForMultilogin(account_id, this)); -} - -void GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin() { - DCHECK_EQ(SET_ACCOUNTS, requests_.front().request_type()); - VLOG(1) << "GaiaCookieManagerService::StartFetchingAccessToken account_id =" - << base::JoinString(requests_.front().account_ids(), " "); - - if (!external_cc_result_fetched_ && - !external_cc_result_fetcher_.IsRunning()) { - external_cc_result_fetcher_.Start(base::BindOnce( - &GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin, - weak_ptr_factory_.GetWeakPtr())); - return; - } - - token_requests_.clear(); - access_tokens_.clear(); - for (const std::string& account_id : requests_.front().account_ids()) { - StartFetchingAccessTokenForMultilogin(account_id); - } -} - void GaiaCookieManagerService::StartFetchingUbertoken() { const std::string account_id = requests_.front().GetAccountID(); VLOG(1) << "GaiaCookieManagerService::StartFetchingUbertoken account_id=" @@ -1090,28 +927,17 @@ void GaiaCookieManagerService::StartFetchingUbertoken() { base::Unretained(this)), base::BindRepeating( [](SigninClient* client, - scoped_refptr<network::SharedURLLoaderFactory> url_loader, GaiaAuthConsumer* consumer) -> std::unique_ptr<GaiaAuthFetcher> { - return client->CreateGaiaAuthFetcher( - consumer, gaia::GaiaSource::kChrome, url_loader); + return client->CreateGaiaAuthFetcher(consumer, + gaia::GaiaSource::kChrome); }, - base::Unretained(signin_client_), GetURLLoaderFactory())); -} - -void GaiaCookieManagerService::StartFetchingMultiLogin( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& accounts) { - DCHECK(external_cc_result_fetched_); - gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher( - this, requests_.front().source(), GetURLLoaderFactory()); - - gaia_auth_fetcher_->StartOAuthMultilogin( - accounts, external_cc_result_fetcher_.GetExternalCcResult()); + base::Unretained(signin_client_))); } void GaiaCookieManagerService::StartFetchingMergeSession() { DCHECK(!uber_token_.empty()); - gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher( - this, requests_.front().source(), GetURLLoaderFactory()); + gaia_auth_fetcher_ = + signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source()); gaia_auth_fetcher_->StartMergeSession( uber_token_, external_cc_result_fetcher_.GetExternalCcResult()); @@ -1121,17 +947,20 @@ void GaiaCookieManagerService::StartGaiaLogOut() { DCHECK(requests_.front().request_type() == GaiaCookieRequestType::LOG_OUT); VLOG(1) << "GaiaCookieManagerService::StartGaiaLogOut"; - signin_client_->PreGaiaLogout(base::BindOnce( - &GaiaCookieManagerService::StartFetchingLogOut, base::Unretained(this))); + signin_client_->PreGaiaLogout( + base::BindOnce(&GaiaCookieManagerService::StartFetchingLogOut, + weak_ptr_factory_.GetWeakPtr())); } void GaiaCookieManagerService::StartFetchingLogOut() { RecordLogoutRequestState(LogoutRequestState::kStarted); - gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher( - this, requests_.front().source(), GetURLLoaderFactory()); + gaia_auth_fetcher_ = + signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source()); bool use_continue_url = false; #if defined(OS_ANDROID) use_continue_url = base::FeatureList::IsEnabled(signin::kMiceFeature); +#elif defined(OS_CHROMEOS) + use_continue_url = chromeos::switches::IsAccountManagerEnabled(); #endif if (use_continue_url) { gaia_auth_fetcher_->StartLogOutWithBlankContinueURL(); @@ -1142,71 +971,37 @@ void GaiaCookieManagerService::StartFetchingLogOut() { void GaiaCookieManagerService::StartFetchingListAccounts() { VLOG(1) << "GaiaCookieManagerService::ListAccounts"; - gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher( - this, requests_.front().source(), GetURLLoaderFactory()); + gaia_auth_fetcher_ = + signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source()); gaia_auth_fetcher_->StartListAccounts(); } -void GaiaCookieManagerService::OnSetAccountsFinished( - const GoogleServiceAuthError& error) { - MarkListAccountsStale(); - access_tokens_.clear(); - token_requests_.clear(); - cookies_to_set_.clear(); - SignalSetAccountsComplete(error); - HandleNextRequest(); -} +void GaiaCookieManagerService::StartSetAccounts() { + DCHECK(!requests_.empty()); + DCHECK_EQ(GaiaCookieRequestType::SET_ACCOUNTS, + requests_.front().request_type()); + DCHECK(!requests_.front().account_ids().empty()); -void GaiaCookieManagerService::OnCookieSet( - const std::string& cookie_name, - const std::string& cookie_domain, - net::CanonicalCookie::CookieInclusionStatus status) { - cookies_to_set_.erase(std::make_pair(cookie_name, cookie_domain)); - bool success = - (status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE); - if (!success) { - VLOG(1) << "Failed to set cookie " << cookie_name - << " for domain=" << cookie_domain << "."; + if (!external_cc_result_fetched_ && + !external_cc_result_fetcher_.IsRunning()) { + external_cc_result_fetcher_.Start( + base::BindOnce(&GaiaCookieManagerService::StartSetAccounts, + weak_ptr_factory_.GetWeakPtr())); + return; } - UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success); - if (cookies_to_set_.empty()) - OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone()); -} - -void GaiaCookieManagerService::StartSettingCookies( - const OAuthMultiloginResult& result) { - network::mojom::CookieManager* cookie_manager = - signin_client_->GetCookieManager(); - DCHECK(cookies_to_set_.empty()); - - const std::vector<net::CanonicalCookie>& cookies = result.cookies(); + oauth_multilogin_helper_ = std::make_unique<signin::OAuthMultiloginHelper>( + signin_client_, token_service_, requests_.front().account_ids(), + external_cc_result_fetcher_.GetExternalCcResult(), + base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished, + weak_ptr_factory_.GetWeakPtr())); +} - for (const net::CanonicalCookie& cookie : cookies) { - cookies_to_set_.insert(std::make_pair(cookie.Name(), cookie.Domain())); - } - for (const net::CanonicalCookie& cookie : cookies) { - if (cookies_to_set_.find(std::make_pair(cookie.Name(), cookie.Domain())) != - cookies_to_set_.end()) { - base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)> - callback = base::BindOnce(&GaiaCookieManagerService::OnCookieSet, - weak_ptr_factory_.GetWeakPtr(), - cookie.Name(), cookie.Domain()); - net::CookieOptions options; - options.set_include_httponly(); - // Permit it to set a SameSite cookie if it wants to. - options.set_same_site_cookie_context( - net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); - cookie_manager->SetCanonicalCookie( - cookie, "https", options, - mojo::WrapCallbackWithDefaultInvokeIfNotRun( - std::move(callback), net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_UNKNOWN_ERROR)); - } else { - LOG(ERROR) << "Duplicate cookie found: " << cookie.Name() << " " - << cookie.Domain(); - } - } +void GaiaCookieManagerService::OnSetAccountsFinished( + signin::SetAccountsInCookieResult result) { + MarkListAccountsStale(); + SignalSetAccountsComplete(result); + HandleNextRequest(); } void GaiaCookieManagerService::HandleNextRequest() { @@ -1235,26 +1030,23 @@ void GaiaCookieManagerService::HandleNextRequest() { DCHECK_EQ(1u, requests_.front().account_ids().size()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingUbertoken, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); break; case GaiaCookieRequestType::SET_ACCOUNTS: - DCHECK(!requests_.front().account_ids().empty()); - signin_client_->DelayNetworkCall(base::BindOnce( - &GaiaCookieManagerService::StartFetchingAccessTokensForMultilogin, - base::Unretained(this))); + StartSetAccounts(); break; case GaiaCookieRequestType::LOG_OUT: DCHECK(requests_.front().account_ids().empty()); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartGaiaLogOut, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); break; case GaiaCookieRequestType::LIST_ACCOUNTS: DCHECK(requests_.front().account_ids().empty()); uber_token_fetcher_.reset(); signin_client_->DelayNetworkCall( base::BindOnce(&GaiaCookieManagerService::StartFetchingListAccounts, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); break; } } diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h index 8073b55d947..94901f4e0a5 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.h +++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.h @@ -12,18 +12,18 @@ #include <utility> #include <vector> -#include "base/callback.h" +#include "base/callback_forward.h" #include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/timer/timer.h" +#include "components/signin/core/browser/oauth_multilogin_helper.h" #include "components/signin/core/browser/signin_client.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/oauth2_token_service.h" -#include "google_apis/gaia/oauth_multilogin_result.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/base/backoff_entry.h" #include "services/network/public/mojom/cookie_manager.mojom.h" @@ -40,6 +40,7 @@ class SimpleURLLoader; namespace signin { class UbertokenFetcherImpl; +enum class SetAccountsInCookieResult; // The maximum number of retries for a fetcher used in this class. constexpr int kMaxFetcherRetries = 8; @@ -70,8 +71,7 @@ struct MultiloginParameters { // GAIA cookie are blocked (such as youtube). This is executed once for the // lifetime of this object, when the first call is made to AddAccountToCookie. class GaiaCookieManagerService : public GaiaAuthConsumer, - public network::mojom::CookieChangeListener, - public OAuth2TokenService::Consumer { + public network::mojom::CookieChangeListener { public: enum GaiaCookieRequestType { ADD_ACCOUNT, @@ -80,10 +80,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, SET_ACCOUNTS }; - typedef base::OnceCallback<void(const GoogleServiceAuthError& error)> + typedef base::OnceCallback<void(signin::SetAccountsInCookieResult)> SetAccountsInCookieCompletedCallback; - typedef base::OnceCallback<void(const std::string& account_id, - const GoogleServiceAuthError& error)> + typedef base::OnceCallback<void(const std::string&, + const GoogleServiceAuthError&)> AddAccountToCookieCompletedCallback; // Contains the information and parameters for any request. @@ -102,7 +102,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, gaia::GaiaSource source() const { return source_; } void RunSetAccountsInCookieCompletedCallback( - const GoogleServiceAuthError& error); + signin::SetAccountsInCookieResult result); void RunAddAccountToCookieCompletedCallback( const std::string& account_id, const GoogleServiceAuthError& error); @@ -229,19 +229,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, GaiaCookieManagerService(OAuth2TokenService* token_service, SigninClient* signin_client); - // Creates a GaiaCookieManagerService that uses the provided - // |shared_url_loader_factory_getter| to determine the SharedUrlLoaderFactory - // used for cookie-related requests. - // Note: SharedUrlLoaderFactory is passed via callback, so that if the - // callback has side-effects (e.g. network initialization), they do not occur - // until the first time GaiaCookieManagerService::GetSharedUrlLoaderFactory is - // called. - GaiaCookieManagerService( - OAuth2TokenService* token_service, - SigninClient* signin_client, - base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()> - shared_url_loader_factory_getter); - ~GaiaCookieManagerService() override; void InitCookieListener(); @@ -265,11 +252,6 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, SetAccountsInCookieCompletedCallback set_accounts_in_cookies_completed_callback); - // Takes list of account_ids from the front request, matches them with a - // corresponding stored access_token and calls StartMultilogin. - // Virtual for testing purposes. - virtual void SetAccountsInCookieWithTokens(); - // Returns if the listed accounts are up to date or not. The out parameter // will be assigned the current cached accounts (whether they are not up to // date or not). If the accounts are not up to date, a ListAccounts fetch is @@ -299,7 +281,7 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void LogOutAllAccounts(gaia::GaiaSource source); // Call observers when setting accounts in cookie completes. - void SignalSetAccountsComplete(const GoogleServiceAuthError& error); + void SignalSetAccountsComplete(signin::SetAccountsInCookieResult result); // Returns true of there are pending log ins or outs. bool is_running() const { return requests_.size() > 0; } @@ -320,20 +302,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, void OnUbertokenFetchComplete(GoogleServiceAuthError error, const std::string& uber_token); - private: - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - MultiloginSuccessAndCookiesSet); - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - MultiloginFailurePersistentError); - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - MultiloginFailureMaxRetriesReached); - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - FetcherRetriesZeroedBetweenCalls); - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - MultiloginFailureInvalidGaiaCredentialsMobile); - FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest, - MultiloginFailureInvalidGaiaCredentialsDesktop); + // Final call in the Setting accounts in cookie procedure. Public for testing. + void OnSetAccountsFinished(signin::SetAccountsInCookieResult result); + private: scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory(); // Calls the AddAccountToCookie completion callback. @@ -352,57 +324,24 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, network::mojom::CookieChangeCause cause) override; void OnCookieListenerConnectionError(); - // Overridden from OAuth2TokenService::Consumer. - void OnGetTokenSuccess( - const OAuth2TokenService::Request* request, - const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; - void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) override; - // Called when either refresh or access token becomes available. - void OnTokenFetched(const std::string& account_id, const std::string& token); - // Overridden from GaiaAuthConsumer. void OnMergeSessionSuccess(const std::string& data) override; void OnMergeSessionFailure(const GoogleServiceAuthError& error) override; - void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override; void OnListAccountsSuccess(const std::string& data) override; void OnListAccountsFailure(const GoogleServiceAuthError& error) override; void OnLogOutSuccess() override; void OnLogOutFailure(const GoogleServiceAuthError& error) override; - // Callback for CookieManager::SetCanonicalCookie. - void OnCookieSet(const std::string& cookie_name, - const std::string& cookie_domain, - net::CanonicalCookie::CookieInclusionStatus status); - - // Final call in the Setting accounts in cookie procedure. Virtual for testing - // purposes. - virtual void OnSetAccountsFinished(const GoogleServiceAuthError& error); - // Helper method for AddAccountToCookie* methods. void AddAccountToCookieInternal( const std::string& account_id, gaia::GaiaSource source, AddAccountToCookieCompletedCallback completion_callback); - // Helper function to trigger fetching retry in case of failure for only - // failed account id. Virtual for testing purposes. - virtual void StartFetchingAccessTokenForMultilogin( - const std::string& account_id); - - // Starts the process of fetching the access token with OauthLogin scope and - // performing SetAccountsInCookie on success. Virtual so that it can be - // overridden in tests. - virtual void StartFetchingAccessTokensForMultilogin(); - // Starts the proess of fetching the uber token and performing a merge session // for the next account. Virtual so that it can be overriden in tests. virtual void StartFetchingUbertoken(); - // Starts the process of setting accounts in cookie. - void StartFetchingMultiLogin( - const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& accounts); - // Virtual for testing purposes. virtual void StartFetchingMergeSession(); @@ -416,8 +355,8 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // Virtual for testing purpose. virtual void StartFetchingLogOut(); - // Starts setting parsed cookies in browser. - void StartSettingCookies(const OAuthMultiloginResult& result); + // Starts setting account using multilogin endpoint. + void StartSetAccounts(); // Start the next request, if needed. void HandleNextRequest(); @@ -425,11 +364,10 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, OAuth2TokenService* token_service_; SigninClient* signin_client_; - base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()> - shared_url_loader_factory_getter_; std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_; ExternalCcResultFetcher external_cc_result_fetcher_; + std::unique_ptr<signin::OAuthMultiloginHelper> oauth_multilogin_helper_; // If the GaiaAuthFetcher or SimpleURLLoader fails, retry with exponential // backoff and network delay. @@ -440,21 +378,9 @@ class GaiaCookieManagerService : public GaiaAuthConsumer, // The last fetched ubertoken, for use in MergeSession retries. std::string uber_token_; - // Access tokens for use inside SetAccountsToCookie. - // TODO (valeriyas): make FetchUberToken use those instead of a separate - // access_token. - std::unordered_map<std::string, std::string> access_tokens_; - - // Current list of processed token requests; - std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_; - // The access token that can be used to prime the UberToken fetch. std::string access_token_; - // List of pairs (cookie name and cookie domain) that have to be set in - // cookie jar. - std::set<std::pair<std::string, std::string>> cookies_to_set_; - // Connection to the CookieManager that signals when the GAIA cookies change. mojo::Binding<network::mojom::CookieChangeListener> cookie_listener_binding_; diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc index 2b71d6111db..85a5c442b00 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc +++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc @@ -96,11 +96,7 @@ class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { public: InstrumentedGaiaCookieManagerService(OAuth2TokenService* token_service, SigninClient* signin_client) - : GaiaCookieManagerService( - token_service, - signin_client, - base::BindRepeating(&SigninClient::GetURLLoaderFactory, - base::Unretained(signin_client))) { + : GaiaCookieManagerService(token_service, signin_client) { total++; } @@ -110,11 +106,6 @@ class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { MOCK_METHOD0(StartFetchingListAccounts, void()); MOCK_METHOD0(StartFetchingLogOut, void()); MOCK_METHOD0(StartFetchingMergeSession, void()); - MOCK_METHOD1(StartFetchingAccessTokenForMultilogin, - void(const std::string& account_id)); - MOCK_METHOD0(SetAccountsInCookieWithTokens, void()); - MOCK_METHOD1(OnSetAccountsFinished, - void(const GoogleServiceAuthError& error)); private: DISALLOW_COPY_AND_ASSIGN(InstrumentedGaiaCookieManagerService); @@ -125,18 +116,7 @@ class GaiaCookieManagerServiceTest : public testing::Test { GaiaCookieManagerServiceTest() : no_error_(GoogleServiceAuthError::NONE), error_(GoogleServiceAuthError::SERVICE_ERROR), - canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {} - - class RequestMockImpl : public OAuth2TokenService::Request { - public: - RequestMockImpl(std::string account_id) { account_id_ = account_id; } - std::string GetAccountId() const override { return account_id_; } - - private: - std::string account_id_; - }; - - void SetUp() override { + canceled_(GoogleServiceAuthError::REQUEST_CANCELED) { AccountTrackerService::RegisterPrefs(pref_service_.registry()); signin_client_.reset(new TestSigninClient(&pref_service_)); } @@ -199,7 +179,7 @@ class GaiaCookieManagerServiceTest : public testing::Test { } void SimulateGetCheckConnectionInfoSuccess(const std::string& data) { - signin_client_->test_url_loader_factory()->AddResponse( + signin_client_->GetTestURLLoaderFactory()->AddResponse( GaiaUrls::GetInstance() ->GetCheckConnectionInfoURLWithSource(GaiaConstants::kChromeSource) .spec(), @@ -209,7 +189,7 @@ class GaiaCookieManagerServiceTest : public testing::Test { void SimulateGetCheckConnectionInfoResult(const std::string& url, const std::string& result) { - signin_client_->test_url_loader_factory()->AddResponse(url, result); + signin_client_->GetTestURLLoaderFactory()->AddResponse(url, result); base::RunLoop().RunUntilIdle(); } @@ -221,12 +201,12 @@ class GaiaCookieManagerServiceTest : public testing::Test { } bool IsLoadPending(const std::string& url) { - return signin_client_->test_url_loader_factory()->IsPending( + return signin_client_->GetTestURLLoaderFactory()->IsPending( GURL(url).spec()); } bool IsLoadPending() { - return signin_client_->test_url_loader_factory()->NumPending() > 0; + return signin_client_->GetTestURLLoaderFactory()->NumPending() > 0; } const GoogleServiceAuthError& no_error() { return no_error_; } @@ -366,549 +346,6 @@ TEST_F(GaiaCookieManagerServiceTest, FailedUbertoken) { SimulateUbertokenFailure(&helper, error()); } -TEST_F(GaiaCookieManagerServiceTest, AccessTokenSuccess) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - base::ScopedClosureRunner task_runner_ = - base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()); - - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - - SimulateAccessTokenSuccess(&helper, &request2); - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - - // Transient error, retry. - SimulateAccessTokenFailure(&helper, &request1, error); - - DCHECK(helper.is_running()); - Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); - - SimulateAccessTokenSuccess(&helper, &request1); -} - -TEST_F(GaiaCookieManagerServiceTest, - AccessTokenFailureTransientErrorMaxRetriesReached) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - base::ScopedClosureRunner task_runner_ = - base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(signin::kMaxFetcherRetries - 1); - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); - - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - - EXPECT_LT(helper.GetBackoffEntry()->GetTimeUntilRelease(), - base::TimeDelta::FromMilliseconds(1100)); - - // Transient error, retry, fail when maximum number of retries is reached. - for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { - SimulateAccessTokenFailure(&helper, &request1, error); - Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); - } - SimulateAccessTokenFailure(&helper, &request1, error); - // Check that no Multilogin is triggered. - SimulateAccessTokenSuccess(&helper, &request2); -} - -TEST_F(GaiaCookieManagerServiceTest, AccessTokenFailurePersistentError) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - - GoogleServiceAuthError error( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); - - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - - SimulateAccessTokenFailure(&helper, &request1, error); - - // Check that no Multilogin is triggered. - SimulateAccessTokenSuccess(&helper, &request2); -} - -TEST_F(GaiaCookieManagerServiceTest, FetcherRetriesZeroedBetweenCalls) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - base::ScopedClosureRunner task_runner_ = - base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - - std::string data = - R"()]}' - { - "status": "OK", - "cookies":[ - { - "name":"SID", - "value":"vAlUe1", - "domain":".google.ru", - "path":"/", - "isSecure":true, - "isHttpOnly":false, - "priority":"HIGH", - "maxAge":63070000 - } - ] - } - )"; - OAuthMultiloginResult result(data); - ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - // retry call - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(signin::kMaxFetcherRetries - 1); - // retry call - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); - EXPECT_CALL(helper, - OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) - .Times(1); - - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - // Transient error, retry. - // Succeed when only one retry is left. Simulate Multilogin failure. Check - // that it retries. - for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { - SimulateAccessTokenFailure(&helper, &request1, error); - Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); - } - SimulateAccessTokenSuccess(&helper, &request1); - SimulateAccessTokenSuccess(&helper, &request2); - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - helper.StartFetchingMultiLogin(accounts); - SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); - SimulateMultiloginFinished(&helper, result); -} - -TEST_F(GaiaCookieManagerServiceTest, MultiloginSuccessAndCookiesSet) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - base::ScopedClosureRunner task_runner_ = - base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - - std::string data = - R"()]}' - { - "status": "OK", - "cookies":[ - { - "name":"SID", - "value":"vAlUe1", - "domain":".google.ru", - "path":"/", - "isSecure":true, - "isHttpOnly":false, - "priority":"HIGH", - "maxAge":63070000 - }, - { - "name":"SID", - "value":"vAlUe1", - "domain":".google.ru", - "path":"/", - "isSecure":true, - "isHttpOnly":false, - "priority":"HIGH", - "maxAge":63070000 - }, - { - "name":"HSID", - "value":"vAlUe4", - "host":"google.fr", - "path":"/", - "isSecure":true, - "isHttpOnly":false, - "priority":"HIGH", - "maxAge":0 - } - ] - } - )"; - OAuthMultiloginResult result(data); - ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); - EXPECT_CALL(helper, - OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) - .Times(1); - - // Needed to insert request in the queue. - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - helper.StartFetchingMultiLogin(accounts); - - SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); - - DCHECK(helper.is_running()); - Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); - - SimulateMultiloginFinished(&helper, result); -} - -TEST_F(GaiaCookieManagerServiceTest, MultiloginFailurePersistentError) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); - - // Needed to insert request in the queue. - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - helper.StartFetchingMultiLogin(accounts); - - SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); -} - -TEST_F(GaiaCookieManagerServiceTest, MultiloginFailureMaxRetriesReached) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - base::ScopedClosureRunner task_runner_ = - base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - // This is the retry call, the first call is skipped as we call - // StartFetchingMultiLogim explicitly instead. - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()) - .Times(signin::kMaxFetcherRetries - 1); - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); - - // Needed to insert request in the queue. - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - helper.StartFetchingMultiLogin(accounts); - - // Transient error, retry, fail when maximum number of retries is reached. - for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { - SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); - Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); - } - SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); -} - -TEST_F(GaiaCookieManagerServiceTest, - MultiloginFailureInvalidGaiaCredentialsMobile) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - - GoogleServiceAuthError error( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); - - std::string data_ok = - R"()]}' - { - "status": "OK", - "cookies":[ - { - "name":"SID", - "value":"vAlUe1", - "domain":".google.ru", - "path":"/", - "isSecure":true, - "isHttpOnly":false, - "priority":"HIGH", - "maxAge":63070000 - } - ] - } - )"; - OAuthMultiloginResult result_ok(data_ok); - ASSERT_EQ(result_ok.error().state(), GoogleServiceAuthError::State::NONE); - - std::string data_failed = - R"()]}' - { - "status": "INVALID_TOKENS", - "failed_accounts": [ - { - "obfuscated_id": "12345", "status": "RECOVERABLE" - }, - { - "obfuscated_id": "23456", "status": "OK" - } - ] - } - )"; - OAuthMultiloginResult result_failed(data_failed); - ASSERT_EQ(result_failed.error().state(), - GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); - - // On mobile token_service is expected to retry getting access tokens first. - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(0); - - // Expect to try to refetch failed account one more time. - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - - // This time access tokens are supposed to be correct. - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); - EXPECT_CALL(helper, - OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) - .Times(1); - - // Needed to insert request in the queue. - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - // Both requests for access tokens are successful but they could be returned - // from cache and be stale. - SimulateAccessTokenSuccess(&helper, &request1); - SimulateAccessTokenSuccess(&helper, &request2); - // Both tokens are inserted in the map. - EXPECT_EQ(2u, helper.access_tokens_.size()); - - helper.StartFetchingMultiLogin(accounts); - // Access tokens were stale, Multilogin failed. - SimulateMultiloginFinished(&helper, result_failed); - - // GaiaCookieManagerService should retry fetching access token again, - // it should be removed from the token map. - EXPECT_EQ(1u, helper.access_tokens_.size()); - EXPECT_EQ(helper.access_tokens_.begin()->first, account_id2); - - // This time access token is fresh. - SimulateAccessTokenSuccess(&helper, &request1); - - EXPECT_EQ(2u, helper.access_tokens_.size()); - - // And Multilogin is successful. - SimulateMultiloginFinished(&helper, result_ok); - - // The end. -} - -TEST_F(GaiaCookieManagerServiceTest, - MultiloginFailureInvalidGaiaCredentialsDesktop) { - InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); - MockObserver observer(&helper); - - const std::string account_id1 = "12345"; - const std::string account_id2 = "23456"; - const std::vector<std::string> account_ids = {account_id1, account_id2}; - - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> accounts = - std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>(); - accounts.emplace_back(account_id1, "AccessToken"); - accounts.emplace_back(account_id2, "AccessToken"); - - RequestMockImpl request1(account_id1); - RequestMockImpl request2(account_id2); - - GoogleServiceAuthError error( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); - - std::string data_failed = - R"()]}' - { - "status": "INVALID_TOKENS", - "failed_accounts": [ - { - "obfuscated_id": "12345", "status": "RECOVERABLE" - }, - { - "obfuscated_id": "23456", "status": "OK" - } - ] - } - )"; - OAuthMultiloginResult result_failed(data_failed); - ASSERT_EQ(result_failed.error().state(), - GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); - - testing::InSequence mock_sequence; - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) - .Times(1); - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); - // Expect to try to refetch failed account one more time. - EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) - .Times(1); - // And fail right away. - EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); - EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); - - // Needed to insert request in the queue. - helper.SetAccountsInCookie( - account_ids, gaia::GaiaSource::kChrome, - GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); - - // Both requests for access tokens are successful but they could be returned - // from cache and be stale. - SimulateAccessTokenSuccess(&helper, &request1); - SimulateAccessTokenSuccess(&helper, &request2); - // Both tokens are inserted in the map. - EXPECT_EQ(2u, helper.access_tokens_.size()); - - helper.StartFetchingMultiLogin(accounts); - - // On desktop refresh tokens were used and failed. Token service will retry - // fetching access token but refresh token is supposed to be set in error and - // request will return with immediate error. - SimulateMultiloginFinished(&helper, result_failed); - - SimulateAccessTokenFailure(&helper, &request1, error); -} - TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); diff --git a/chromium/components/signin/core/browser/identity_manager_wrapper.cc b/chromium/components/signin/core/browser/identity_manager_wrapper.cc index 9281edbb567..2025ebcd649 100644 --- a/chromium/components/signin/core/browser/identity_manager_wrapper.cc +++ b/chromium/components/signin/core/browser/identity_manager_wrapper.cc @@ -11,7 +11,7 @@ #include "services/identity/public/cpp/primary_account_mutator.h" IdentityManagerWrapper::IdentityManagerWrapper( - AccountTrackerService* account_tracker_service, + std::unique_ptr<AccountTrackerService> account_tracker_service, std::unique_ptr<ProfileOAuth2TokenService> token_service, std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, std::unique_ptr<SigninManagerBase> signin_manager, @@ -20,16 +20,12 @@ IdentityManagerWrapper::IdentityManagerWrapper( std::unique_ptr<identity::AccountsMutator> accounts_mutator, std::unique_ptr<identity::AccountsCookieMutator> accounts_cookie_mutator, std::unique_ptr<identity::DiagnosticsProvider> diagnostics_provider) - : identity::IdentityManager(std::move(token_service), + : identity::IdentityManager(std::move(account_tracker_service), + std::move(token_service), std::move(gaia_cookie_manager_service), std::move(signin_manager), std::move(account_fetcher_service), - account_tracker_service, std::move(primary_account_mutator), std::move(accounts_mutator), std::move(accounts_cookie_mutator), std::move(diagnostics_provider)) {} - -void IdentityManagerWrapper::Shutdown() { - IdentityManager::Shutdown(); -} diff --git a/chromium/components/signin/core/browser/identity_manager_wrapper.h b/chromium/components/signin/core/browser/identity_manager_wrapper.h index 916d1961d4d..b1c03249a61 100644 --- a/chromium/components/signin/core/browser/identity_manager_wrapper.h +++ b/chromium/components/signin/core/browser/identity_manager_wrapper.h @@ -28,7 +28,7 @@ class IdentityManagerWrapper : public KeyedService, public identity::IdentityManager { public: IdentityManagerWrapper( - AccountTrackerService* account_tracker_service, + std::unique_ptr<AccountTrackerService> account_tracker_service, std::unique_ptr<ProfileOAuth2TokenService> token_service, std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service, std::unique_ptr<SigninManagerBase> signin_manager, @@ -37,9 +37,6 @@ class IdentityManagerWrapper : public KeyedService, std::unique_ptr<identity::AccountsMutator> accounts_mutator, std::unique_ptr<identity::AccountsCookieMutator> accounts_cookie_mutator, std::unique_ptr<identity::DiagnosticsProvider> diagnostics_provider); - - // KeyedService overrides. - void Shutdown() override; }; #endif // COMPONENTS_SIGNIN_CORE_BROWSER_IDENTITY_MANAGER_WRAPPER_H_ diff --git a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc b/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc index df7b61bc268..7a42f2e4c1d 100644 --- a/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/chromium/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc @@ -88,7 +88,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest MutableProfileOAuth2TokenServiceDelegateTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI, - base::test::ScopedTaskEnvironment::ExecutionMode::ASYNC), + base::test::ScopedTaskEnvironment::ThreadPoolExecutionMode::ASYNC), access_token_success_count_(0), access_token_failure_count_(0), access_token_failure_(GoogleServiceAuthError::NONE), @@ -107,7 +107,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest AccountTrackerService::RegisterPrefs(pref_service_.registry()); SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); client_.reset(new TestSigninClient(&pref_service_)); - client_->test_url_loader_factory()->AddResponse( + client_->GetTestURLLoaderFactory()->AddResponse( GaiaUrls::GetInstance()->oauth2_revoke_url().spec(), ""); LoadTokenDatabase(); account_tracker_service_.Initialize(&pref_service_, base::FilePath()); @@ -138,7 +138,7 @@ class MutableProfileOAuth2TokenServiceDelegateTest } void AddSuccessfulOAuhTokenResponse() { - client_->test_url_loader_factory()->AddResponse( + client_->GetTestURLLoaderFactory()->AddResponse( GaiaUrls::GetInstance()->oauth2_token_url().spec(), GetValidTokenResponse("token", 3600)); } @@ -204,12 +204,12 @@ class MutableProfileOAuth2TokenServiceDelegateTest } // OAuth2TokenService::DiagnosticsObserver implementation - void OnRefreshTokenAvailableFromSource(const std::string& account_id, + void OnRefreshTokenAvailableFromSource(const CoreAccountId& account_id, bool is_refresh_token_valid, const std::string& source) override { source_for_refresh_token_available_ = source; } - void OnRefreshTokenRevokedFromSource(const std::string& account_id, + void OnRefreshTokenRevokedFromSource(const CoreAccountId& account_id, const std::string& source) override { source_for_refresh_token_revoked_ = source; } @@ -541,9 +541,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -582,9 +582,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -623,9 +623,9 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, account_tracker_service_.SeedAccountInfo(secondary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); - AddAuthTokenManually("AccountId-" + secondary_account.account_id, + AddAuthTokenManually("AccountId-" + secondary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -659,7 +659,7 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, account_tracker_service_.SeedAccountInfo(primary_account); ResetObserverCounts(); - AddAuthTokenManually("AccountId-" + primary_account.account_id, + AddAuthTokenManually("AccountId-" + primary_account.account_id.id, "refresh_token"); oauth2_service_delegate_->LoadCredentials(primary_account.account_id); base::RunLoop().RunUntilIdle(); @@ -792,39 +792,39 @@ TEST_F(MutableProfileOAuth2TokenServiceDelegateTest, RevokeRetries) { InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled); const std::string url = GaiaUrls::GetInstance()->oauth2_revoke_url().spec(); // Revokes will remain in "pending" state. - client_->test_url_loader_factory()->ClearResponses(); + client_->GetTestURLLoaderFactory()->ClearResponses(); oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); - EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url)); oauth2_service_delegate_->RevokeCredentials("account_id"); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); - EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); // Fail and retry. - client_->test_url_loader_factory()->SimulateResponseForPendingRequest( + client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR); - EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); // Fail and retry. - client_->test_url_loader_factory()->SimulateResponseForPendingRequest( + client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR); - EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); // Do not retry after third attempt. - client_->test_url_loader_factory()->SimulateResponseForPendingRequest( + client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR); - EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url)); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); // No retry after success. oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token"); oauth2_service_delegate_->RevokeCredentials("account_id"); EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size()); - EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url)); - client_->test_url_loader_factory()->SimulateResponseForPendingRequest( + EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url)); + client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest( url, std::string(), net::HTTP_OK); - EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url)); + EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url)); EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty()); } diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc index 575b6123c44..386c6dc93e7 100644 --- a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc +++ b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.cc @@ -150,16 +150,14 @@ OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid( if (account_tracker_service_->GetMigrationState() == AccountTrackerService::MIGRATION_IN_PROGRESS) { std::vector<std::string> accounts = GetAccounts(); - std::vector<std::string> accounts_id; + std::vector<CoreAccountId> accounts_id; for (auto account_name : accounts) { AccountInfo account_info = account_tracker_service_->FindAccountInfoByEmail(account_name); DCHECK(!account_info.gaia.empty()); accounts_id.push_back(account_info.gaia); } - ScopedJavaLocalRef<jobjectArray> java_accounts( - base::android::ToJavaArrayOfStrings(env, accounts_id)); - Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts); + SetAccounts(accounts_id); } if (!disable_interaction_with_system_accounts_) { @@ -244,6 +242,36 @@ OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() { return account_names; } +std::vector<CoreAccountId> +OAuth2TokenServiceDelegateAndroid::GetSystemAccounts() { + std::vector<CoreAccountId> ids; + for (const std::string& name : GetSystemAccountNames()) { + CoreAccountId id(MapAccountNameToAccountId(name)); + if (!id.empty()) + ids.push_back(std::move(id)); + } + return ids; +} + +std::vector<CoreAccountId> +OAuth2TokenServiceDelegateAndroid::GetValidAccounts() { + std::vector<CoreAccountId> ids; + for (const std::string& id : GetAccounts()) { + if (ValidateAccountId(id)) + ids.emplace_back(id); + } + return ids; +} + +void OAuth2TokenServiceDelegateAndroid::SetAccounts( + const std::vector<CoreAccountId>& accounts) { + JNIEnv* env = AttachCurrentThread(); + std::vector<std::string> str_ids(accounts.begin(), accounts.end()); + ScopedJavaLocalRef<jobjectArray> java_accounts( + base::android::ToJavaArrayOfStrings(env, str_ids)); + Java_OAuth2TokenService_setAccounts(env, java_accounts); +} + OAuth2AccessTokenFetcher* OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher( const std::string& account_id, @@ -284,52 +312,33 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( // Clear any auth errors so that client can retry to get access tokens. errors_.clear(); - UpdateAccountList(MapAccountNameToAccountId(signed_in_account_name)); + UpdateAccountList(MapAccountNameToAccountId(signed_in_account_name), + GetValidAccounts(), GetSystemAccounts()); } void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( - const std::string& signed_in_account_id) { - std::vector<std::string> curr_ids; - for (const std::string& curr_name : GetSystemAccountNames()) { - std::string curr_id(MapAccountNameToAccountId(curr_name)); - if (!curr_id.empty()) - curr_ids.push_back(curr_id); - } - - std::vector<std::string> prev_ids; - for (const std::string& prev_id : GetAccounts()) { - if (ValidateAccountId(prev_id)) - prev_ids.push_back(prev_id); - } - + const CoreAccountId& signed_in_account_id, + const std::vector<CoreAccountId>& prev_ids, + const std::vector<CoreAccountId>& curr_ids) { DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" << " sigined_in_account_id=" << signed_in_account_id << " prev_ids=" << prev_ids.size() << " curr_ids=" << curr_ids.size(); - std::vector<std::string> refreshed_ids; - std::vector<std::string> revoked_ids; + std::vector<CoreAccountId> refreshed_ids; + std::vector<CoreAccountId> revoked_ids; bool keep_accounts = UpdateAccountList( signed_in_account_id, prev_ids, curr_ids, &refreshed_ids, &revoked_ids); ScopedBatchChange batch(this); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobjectArray> java_accounts; - if (keep_accounts) { - java_accounts = base::android::ToJavaArrayOfStrings(env, curr_ids); - } else { - DCHECK(!base::FeatureList::IsEnabled(signin::kMiceFeature)); - java_accounts = - base::android::ToJavaArrayOfStrings(env, std::vector<std::string>()); - } // Save the current accounts in the token service before calling // FireRefreshToken* methods. - Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts); + SetAccounts(keep_accounts ? curr_ids : std::vector<CoreAccountId>()); - for (const std::string& refreshed_id : refreshed_ids) + for (const CoreAccountId& refreshed_id : refreshed_ids) FireRefreshTokenAvailable(refreshed_id); - for (const std::string& revoked_id : revoked_ids) + for (const CoreAccountId& revoked_id : revoked_ids) FireRefreshTokenRevoked(revoked_id); if (fire_refresh_token_loaded_ == RT_WAIT_FOR_VALIDATION) { fire_refresh_token_loaded_ = RT_LOADED; @@ -338,7 +347,7 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( fire_refresh_token_loaded_ = RT_HAS_BEEN_VALIDATED; } - // Clear accounts no longer exist on device from AccountTrackerService. + // Clear accounts that no longer exist on device from AccountTrackerService. std::vector<AccountInfo> accounts_info = account_tracker_service_->GetAccounts(); for (const AccountInfo& info : accounts_info) { @@ -361,16 +370,16 @@ void OAuth2TokenServiceDelegateAndroid::UpdateAccountList( } bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( - const std::string& signed_in_id, - const std::vector<std::string>& prev_ids, - const std::vector<std::string>& curr_ids, - std::vector<std::string>* refreshed_ids, - std::vector<std::string>* revoked_ids) { + const CoreAccountId& signed_in_id, + const std::vector<CoreAccountId>& prev_ids, + const std::vector<CoreAccountId>& curr_ids, + std::vector<CoreAccountId>* refreshed_ids, + std::vector<CoreAccountId>* revoked_ids) { bool keep_accounts = base::FeatureList::IsEnabled(signin::kMiceFeature) || base::ContainsValue(curr_ids, signed_in_id); if (keep_accounts) { // Revoke token for ids that have been removed from the device. - for (const std::string& prev_id : prev_ids) { + for (const CoreAccountId& prev_id : prev_ids) { if (prev_id == signed_in_id) continue; if (!base::ContainsValue(curr_ids, prev_id)) { @@ -386,7 +395,7 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( << "refreshed=" << signed_in_id; refreshed_ids->push_back(signed_in_id); } - for (const std::string& curr_id : curr_ids) { + for (const CoreAccountId& curr_id : curr_ids) { if (curr_id == signed_in_id) continue; DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" @@ -400,7 +409,7 @@ bool OAuth2TokenServiceDelegateAndroid::UpdateAccountList( << "revoked=" << signed_in_id; revoked_ids->push_back(signed_in_id); } - for (const std::string& prev_id : prev_ids) { + for (const CoreAccountId& prev_id : prev_ids) { if (prev_id == signed_in_id) continue; DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAccountList:" @@ -469,10 +478,7 @@ void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() { // Clear accounts in the token service before calling // |FireRefreshTokenRevoked|. - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobjectArray> java_accounts( - base::android::ToJavaArrayOfStrings(env, std::vector<std::string>())); - Java_OAuth2TokenService_saveStoredAccounts(env, java_accounts); + SetAccounts(std::vector<CoreAccountId>()); for (const std::string& account : accounts_to_revoke) FireRefreshTokenRevoked(account); @@ -499,17 +505,18 @@ void OAuth2TokenServiceDelegateAndroid::ReloadAccountsFromSystem( const std::string& primary_account_id) { // UpdateAccountList() effectively synchronizes the accounts in the Token // Service with those present at the system level. - UpdateAccountList(primary_account_id); + UpdateAccountList(primary_account_id, GetValidAccounts(), + GetSystemAccounts()); } std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName( - const std::string& account_id) const { + const CoreAccountId& account_id) const { return account_tracker_service_->GetAccountInfo(account_id).email; } -std::string OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( +CoreAccountId OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId( const std::string& account_name) const { - std::string account_id = + CoreAccountId account_id = account_tracker_service_->FindAccountInfoByEmail(account_name).account_id; DCHECK(!account_id.empty() || account_name.empty()) << "Can't find account id, account_name=" << account_name; diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h index 8808eeac0e5..d3603245d49 100644 --- a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h +++ b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android.h @@ -55,9 +55,6 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { const GoogleServiceAuthError& error) override; std::vector<std::string> GetAccounts() override; - // Lists account names at the OS level. - std::vector<std::string> GetSystemAccountNames(); - void UpdateAccountList( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, @@ -67,7 +64,9 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // android account ids and check the token status of each. // 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 std::string& signed_in_account_id); + void UpdateAccountList(const CoreAccountId& signed_in_account_id, + const std::vector<CoreAccountId>& prev_ids, + const std::vector<CoreAccountId>& curr_ids); // Overridden from OAuth2TokenService to complete signout of all // OA2TService aware accounts. @@ -98,8 +97,9 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { void FireRefreshTokensLoaded() override; private: - std::string MapAccountIdToAccountName(const std::string& account_id) const; - std::string MapAccountNameToAccountId(const std::string& account_name) const; + std::string MapAccountIdToAccountName(const CoreAccountId& account_id) const; + CoreAccountId MapAccountNameToAccountId( + const std::string& account_name) const; enum RefreshTokenLoadStatus { RT_LOAD_NOT_START, @@ -110,11 +110,20 @@ class OAuth2TokenServiceDelegateAndroid : public OAuth2TokenServiceDelegate { // Return whether accounts are valid and we have access to all the tokens in // |curr_ids|. - bool UpdateAccountList(const std::string& signed_in_id, - const std::vector<std::string>& prev_ids, - const std::vector<std::string>& curr_ids, - std::vector<std::string>* refreshed_ids, - std::vector<std::string>* revoked_ids); + bool UpdateAccountList(const CoreAccountId& signed_in_id, + const std::vector<CoreAccountId>& prev_ids, + const std::vector<CoreAccountId>& curr_ids, + std::vector<CoreAccountId>* refreshed_ids, + std::vector<CoreAccountId>* revoked_ids); + + // Lists account names at the OS level. + std::vector<std::string> GetSystemAccountNames(); + // As |GetSystemAccountNames| but returning account IDs. + std::vector<CoreAccountId> GetSystemAccounts(); + // As |GetAccounts| but with only validated account IDs. + std::vector<CoreAccountId> GetValidAccounts(); + // Set accounts using Java's Oauth2TokenService.setAccounts. + virtual void SetAccounts(const std::vector<CoreAccountId>& accounts); base::android::ScopedJavaGlobalRef<jobject> java_ref_; diff --git a/chromium/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc new file mode 100644 index 00000000000..6fe207ad174 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth2_token_service_delegate_android_unittest.cc @@ -0,0 +1,294 @@ +// Copyright (c) 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. + +#include "components/signin/core/browser/oauth2_token_service_delegate_android.h" + +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::StrictMock; + +namespace signin { + +namespace { +const std::vector<CoreAccountId> kEmptyVector; +class OAuth2TokenServiceDelegateAndroidForTest + : public OAuth2TokenServiceDelegateAndroid { + public: + OAuth2TokenServiceDelegateAndroidForTest( + AccountTrackerService* account_tracker_service) + : OAuth2TokenServiceDelegateAndroid(account_tracker_service) {} + MOCK_METHOD1(SetAccounts, void(const std::vector<CoreAccountId>&)); +}; + +class TestObserver : public OAuth2TokenService::Observer { + public: + MOCK_METHOD1(OnRefreshTokenAvailable, void(const std::string&)); + MOCK_METHOD1(OnRefreshTokenRevoked, void(const std::string&)); + MOCK_METHOD0(OnRefreshTokensLoaded, void()); +}; +} // namespace + +class OAuth2TokenServiceDelegateAndroidTest : public testing::Test { + public: + OAuth2TokenServiceDelegateAndroidTest() {} + ~OAuth2TokenServiceDelegateAndroidTest() override = default; + + protected: + void SetUp() override { + testing::Test::SetUp(); + AccountTrackerService::RegisterPrefs(pref_service_.registry()); + account_tracker_service_.Initialize(&pref_service_, base::FilePath()); + OAuth2TokenServiceDelegateAndroid:: + set_disable_interaction_with_system_accounts(); + delegate_ = std::make_unique<OAuth2TokenServiceDelegateAndroidForTest>( + &account_tracker_service_); + delegate_->AddObserver(&observer_); + CreateAndSeedAccounts(); + } + + void TearDown() override { + delegate_->RemoveObserver(&observer_); + testing::Test::TearDown(); + } + + AccountInfo CreateAccountInfo(const std::string& gaia_id, + const std::string& email) { + AccountInfo account_info; + + account_info.gaia = gaia_id; + account_info.email = email; + account_info.full_name = "fullname"; + account_info.given_name = "givenname"; + account_info.hosted_domain = "example.com"; + account_info.locale = "en"; + account_info.picture_url = "https://example.com"; + account_info.is_child_account = false; + account_info.account_id = account_tracker_service_.PickAccountIdForAccount( + account_info.gaia, account_info.email); + + DCHECK(account_info.IsValid()); + + return account_info; + } + + void CreateAndSeedAccounts() { + account1_ = CreateAccountInfo("gaia-id-user-1", "user-1@example.com"); + account2_ = CreateAccountInfo("gaia-id-user-2", "user-2@example.com"); + // SeedAccountInfo is required for + // OAuth2TokenServiceDelegateAndrod::MapAccountNameToAccountId + account_tracker_service_.SeedAccountInfo(account1_); + account_tracker_service_.SeedAccountInfo(account2_); + } + + AccountTrackerService account_tracker_service_; + sync_preferences::TestingPrefServiceSyncable pref_service_; + std::unique_ptr<OAuth2TokenServiceDelegateAndroidForTest> delegate_; + StrictMock<TestObserver> observer_; + + AccountInfo account1_; + AccountInfo account2_; +}; + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith0SystemAccount0AccountAndNotSignedIn) { + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return()); + // No observer call expected + delegate_->UpdateAccountList(std::string(), {}, {}); + EXPECT_TRUE(account_tracker_service_.GetAccounts().empty()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount0AccountAndNotSignedIn) { + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return()); + // No observer call expected + delegate_->UpdateAccountList(std::string(), {}, {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount1AccountAndNotSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)) + .InSequence(seq) + .WillOnce(Return()); + // Stored account from |GetAccounts| must fire a revoked event + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(std::string(), {account1_.account_id}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount0AccountAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, + SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account1_.account_id, {}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount1AccountAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, + SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account1_.account_id, {account1_.account_id}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount1AccountDifferentAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, + SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + // Previously stored account is removed, new account is available + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account1_.account_id, {account2_.account_id}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith0SystemAccount1AccountSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account1_.account_id, {account1_.account_id}, + {}); + EXPECT_TRUE(account_tracker_service_.GetAccounts().empty()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount0AccountAndSignedInDifferent) { + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)).WillOnce(Return()); + + delegate_->UpdateAccountList(account2_.account_id, {}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +// Test Getsysaccounts return a user != from signed user while GetAccounts not +// empty +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount1AccountAndSignedInDifferent) { + Sequence seq; + EXPECT_CALL(*delegate_, SetAccounts(kEmptyVector)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account2_.account_id, {account1_.account_id}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>{account1_}, + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith2SystemAccount0AccountAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, SetAccounts(std::vector<CoreAccountId>( + {account1_.account_id, account2_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + // OnRefreshTokenAvailable fired, signed in account should go first. + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account2_.account_id, {}, + {account1_.account_id, account2_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>({account1_, account2_}), + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith2SystemAccount1AccountAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, SetAccounts(std::vector<CoreAccountId>( + {account1_.account_id, account2_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + // OnRefreshTokenAvailable fired, signed in account should go first. + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account2_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + delegate_->UpdateAccountList(account1_.account_id, {account2_.account_id}, + {account1_.account_id, account2_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>({account1_, account2_}), + account_tracker_service_.GetAccounts()); +} + +TEST_F(OAuth2TokenServiceDelegateAndroidTest, + UpdateAccountListWith1SystemAccount2AccountAndSignedIn) { + Sequence seq; + EXPECT_CALL(*delegate_, + SetAccounts(std::vector<CoreAccountId>({account1_.account_id}))) + .InSequence(seq) + .WillOnce(Return()); + // OnRefreshTokenAvailable fired, signed in account should go first. + EXPECT_CALL(observer_, OnRefreshTokenAvailable(account1_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + EXPECT_CALL(observer_, OnRefreshTokenRevoked(account2_.account_id.id)) + .InSequence(seq) + .WillOnce(Return()); + + delegate_->UpdateAccountList(account1_.account_id, + {account1_.account_id, account2_.account_id}, + {account1_.account_id}); + EXPECT_EQ(std::vector<AccountInfo>({account1_}), + account_tracker_service_.GetAccounts()); +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper.cc b/chromium/components/signin/core/browser/oauth_multilogin_helper.cc new file mode 100644 index 00000000000..ab38dc09254 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_helper.cc @@ -0,0 +1,202 @@ +// 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. + +#include "components/signin/core/browser/oauth_multilogin_helper.h" + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h" +#include "components/signin/core/browser/set_accounts_in_cookie_result.h" +#include "components/signin/core/browser/signin_client.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth_multilogin_result.h" +#include "mojo/public/cpp/bindings/callback_helpers.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +namespace { + +constexpr int kMaxFetcherRetries = 3; + +std::string FindTokenForAccount( + const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs, + const std::string& account_id) { + for (auto it = token_id_pairs.cbegin(); it != token_id_pairs.cend(); ++it) { + if (account_id == it->gaia_id_) + return it->token_; + } + return std::string(); +} + +} // namespace + +namespace signin { + +OAuthMultiloginHelper::OAuthMultiloginHelper( + SigninClient* signin_client, + OAuth2TokenService* token_service, + const std::vector<std::string>& account_ids, + const std::string& external_cc_result, + base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback) + : signin_client_(signin_client), + token_service_(token_service), + account_ids_(account_ids), + external_cc_result_(external_cc_result), + callback_(std::move(callback)), + weak_ptr_factory_(this) { + DCHECK(signin_client_); + DCHECK(token_service_); + DCHECK(!account_ids_.empty()); + DCHECK(callback_); + +#ifndef NDEBUG + // Check that there is no duplicate accounts. + std::set<std::string> accounts_no_duplicates(account_ids_.begin(), + account_ids_.end()); + DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size()); +#endif + + StartFetchingTokens(); +} + +OAuthMultiloginHelper::~OAuthMultiloginHelper() = default; + +void OAuthMultiloginHelper::StartFetchingTokens() { + DCHECK(!token_fetcher_); + DCHECK(token_id_pairs_.empty()); + token_fetcher_ = std::make_unique<signin::OAuthMultiloginTokenFetcher>( + signin_client_, token_service_, account_ids_, + base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensSuccess, + base::Unretained(this)), + base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensFailure, + base::Unretained(this))); +} + +void OAuthMultiloginHelper::OnAccessTokensSuccess( + const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs) { + DCHECK(token_id_pairs_.empty()); + token_id_pairs_ = token_id_pairs; + DCHECK_EQ(token_id_pairs_.size(), account_ids_.size()); + token_fetcher_.reset(); + + signin_client_->DelayNetworkCall( + base::BindOnce(&OAuthMultiloginHelper::StartFetchingMultiLogin, + weak_ptr_factory_.GetWeakPtr())); +} + +void OAuthMultiloginHelper::OnAccessTokensFailure( + const GoogleServiceAuthError& error) { + token_fetcher_.reset(); + std::move(callback_).Run( + error.IsTransientError() + ? signin::SetAccountsInCookieResult::kTransientError + : signin::SetAccountsInCookieResult::kPersistentError); + // Do not add anything below this line, because this may be deleted. +} + +void OAuthMultiloginHelper::StartFetchingMultiLogin() { + DCHECK_EQ(token_id_pairs_.size(), account_ids_.size()); + gaia_auth_fetcher_ = + signin_client_->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome); + gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_, + external_cc_result_); +} + +void OAuthMultiloginHelper::OnOAuthMultiloginFinished( + const OAuthMultiloginResult& result) { + if (result.status() == OAuthMultiloginResponseStatus::kOk) { + VLOG(1) << "Multilogin successful accounts=" + << base::JoinString(account_ids_, " "); + StartSettingCookies(result); + return; + } + + // If Gaia responded with kInvalidTokens, we have to mark tokens as invalid. + if (result.status() == OAuthMultiloginResponseStatus::kInvalidTokens) { + for (const std::string& failed_account_id : result.failed_accounts()) { + std::string failed_token = + FindTokenForAccount(token_id_pairs_, failed_account_id); + if (failed_token.empty()) { + LOG(ERROR) + << "Unexpected failed token for account not present in request: " + << failed_account_id; + continue; + } + token_service_->InvalidateTokenForMultilogin(failed_account_id, + failed_token); + } + } + + bool is_transient_error = + result.status() == OAuthMultiloginResponseStatus::kInvalidTokens || + result.status() == OAuthMultiloginResponseStatus::kRetry; + + if (is_transient_error && ++fetcher_retries_ < kMaxFetcherRetries) { + token_id_pairs_.clear(); + StartFetchingTokens(); + return; + } + std::move(callback_).Run( + is_transient_error ? signin::SetAccountsInCookieResult::kTransientError + : signin::SetAccountsInCookieResult::kPersistentError); + // Do not add anything below this line, because this may be deleted. +} + +void OAuthMultiloginHelper::StartSettingCookies( + const OAuthMultiloginResult& result) { + DCHECK(cookies_to_set_.empty()); + network::mojom::CookieManager* cookie_manager = + signin_client_->GetCookieManager(); + const std::vector<net::CanonicalCookie>& cookies = result.cookies(); + + for (const net::CanonicalCookie& cookie : cookies) { + cookies_to_set_.insert(std::make_pair(cookie.Name(), cookie.Domain())); + } + for (const net::CanonicalCookie& cookie : cookies) { + if (cookies_to_set_.find(std::make_pair(cookie.Name(), cookie.Domain())) != + cookies_to_set_.end()) { + base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)> + callback = base::BindOnce(&OAuthMultiloginHelper::OnCookieSet, + weak_ptr_factory_.GetWeakPtr(), + cookie.Name(), cookie.Domain()); + net::CookieOptions options; + options.set_include_httponly(); + // Permit it to set a SameSite cookie if it wants to. + options.set_same_site_cookie_context( + net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); + cookie_manager->SetCanonicalCookie( + cookie, "https", options, + mojo::WrapCallbackWithDefaultInvokeIfNotRun( + std::move(callback), net::CanonicalCookie::CookieInclusionStatus:: + EXCLUDE_UNKNOWN_ERROR)); + } else { + LOG(ERROR) << "Duplicate cookie found: " << cookie.Name() << " " + << cookie.Domain(); + } + } +} + +void OAuthMultiloginHelper::OnCookieSet( + const std::string& cookie_name, + const std::string& cookie_domain, + net::CanonicalCookie::CookieInclusionStatus status) { + cookies_to_set_.erase(std::make_pair(cookie_name, cookie_domain)); + bool success = + (status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE); + if (!success) { + LOG(ERROR) << "Failed to set cookie " << cookie_name + << " for domain=" << cookie_domain << "."; + } + UMA_HISTOGRAM_BOOLEAN("Signin.SetCookieSuccess", success); + if (cookies_to_set_.empty()) + std::move(callback_).Run(signin::SetAccountsInCookieResult::kSuccess); + // Do not add anything below this line, because this may be deleted. +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper.h b/chromium/components/signin/core/browser/oauth_multilogin_helper.h new file mode 100644 index 00000000000..69064a9f0b0 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_helper.h @@ -0,0 +1,98 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ + +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "google_apis/gaia/gaia_auth_consumer.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" + +class GaiaAuthFetcher; +class GoogleServiceAuthError; +class OAuth2TokenService; +class SigninClient; + +namespace signin { + +class OAuthMultiloginTokenFetcher; +enum class SetAccountsInCookieResult; + +// This is a helper class that drives the OAuth multilogin process. +// The main steps are: +// - fetch access tokens with login scope, +// - call the oauth multilogin endpoint, +// - get the cookies from the response body, and set them in the cookie manager. +// It is safe to delete this object from within the callbacks. +class OAuthMultiloginHelper : public GaiaAuthConsumer { + public: + OAuthMultiloginHelper( + SigninClient* signin_client, + OAuth2TokenService* token_service, + const std::vector<std::string>& account_ids, + const std::string& external_cc_result, + base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback); + + ~OAuthMultiloginHelper() override; + + private: + // Starts fetching tokens with OAuthMultiloginTokenFetcher. + void StartFetchingTokens(); + + // Callbacks for OAuthMultiloginTokenFetcher. + void OnAccessTokensSuccess( + const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& + token_id_pairs); + void OnAccessTokensFailure(const GoogleServiceAuthError& error); + + // Actual call to the multilogin endpoint. + void StartFetchingMultiLogin(); + + // Overridden from GaiaAuthConsumer. + void OnOAuthMultiloginFinished(const OAuthMultiloginResult& result) override; + + // Starts setting parsed cookies in browser. + void StartSettingCookies(const OAuthMultiloginResult& result); + + // Callback for CookieManager::SetCanonicalCookie. + void OnCookieSet(const std::string& cookie_name, + const std::string& cookie_domain, + net::CanonicalCookie::CookieInclusionStatus status); + + SigninClient* signin_client_; + OAuth2TokenService* token_service_; + + int fetcher_retries_ = 0; + + // Account ids to set in the cookie. + const std::vector<std::string> account_ids_; + // See GaiaCookieManagerService::ExternalCcResultFetcher for details. + const std::string external_cc_result_; + // Access tokens, in the same order as the account ids. + std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_; + + base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback_; + std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; + std::unique_ptr<OAuthMultiloginTokenFetcher> token_fetcher_; + + // List of pairs (cookie name and cookie domain) that have to be set in + // cookie jar. + std::set<std::pair<std::string, std::string>> cookies_to_set_; + + base::WeakPtrFactory<OAuthMultiloginHelper> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginHelper); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_HELPER_H_ diff --git a/chromium/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/chromium/components/signin/core/browser/oauth_multilogin_helper_unittest.cc new file mode 100644 index 00000000000..8c19a4b7009 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_helper_unittest.cc @@ -0,0 +1,495 @@ +// 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. + +#include "components/signin/core/browser/oauth_multilogin_helper.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/core/browser/set_accounts_in_cookie_result.h" +#include "components/signin/core/browser/test_signin_client.h" +#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "google_apis/gaia/gaia_urls.h" +#include "services/network/test/test_cookie_manager.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace signin { + +namespace { + +const char kAccountId[] = "account_id_1"; +const char kAccountId2[] = "account_id_2"; +const char kAccessToken[] = "access_token_1"; +const char kAccessToken2[] = "access_token_2"; + +const char kExternalCcResult[] = "youtube:OK"; + +constexpr int kMaxFetcherRetries = 3; + +const char kMultiloginSuccessResponse[] = + R"()]}' + { + "status": "OK", + "cookies":[ + { + "name":"SID", + "value":"SID_value", + "domain":".google.fr", + "path":"/", + "isSecure":true, + "isHttpOnly":false, + "priority":"HIGH", + "maxAge":63070000 + } + ] + } + )"; + +const char kMultiloginSuccessResponseTwoCookies[] = + R"()]}' + { + "status": "OK", + "cookies":[ + { + "name":"SID", + "value":"SID_value", + "domain":".google.fr", + "path":"/", + "isSecure":true, + "isHttpOnly":false, + "priority":"HIGH", + "maxAge":63070000 + }, + { + "name":"FOO", + "value":"FOO_value", + "domain":".google.com", + "path":"/", + "isSecure":true, + "isHttpOnly":false, + "priority":"HIGH", + "maxAge":63070000 + } + ] + } + )"; + +const char kMultiloginSuccessResponseWithSecondaryDomain[] = + R"()]}' + { + "status": "OK", + "cookies":[ + { + "name":"SID", + "value":"SID_value", + "domain":".youtube.com", + "path":"/", + "isSecure":true, + "isHttpOnly":false, + "priority":"HIGH", + "maxAge":63070000 + }, + { + "name":"FOO", + "value":"FOO_value", + "domain":".google.com", + "path":"/", + "isSecure":true, + "isHttpOnly":false, + "priority":"HIGH", + "maxAge":63070000 + } + ] + } + )"; + +const char kMultiloginRetryResponse[] = + R"()]}' + { + "status": "RETRY" + } + )"; + +const char kMultiloginInvalidTokenResponse[] = + R"()]}' + { + "status": "INVALID_TOKENS", + "failed_accounts": [ + { "obfuscated_id": "account_id_1", "status": "RECOVERABLE" }, + { "obfuscated_id": "account_id_2", "status": "OK" } + ] + } + )"; + +// GMock matcher that checks that the cookie has the expected parameters. +MATCHER_P3(CookieMatcher, name, value, domain, "") { + return arg.Name() == name && arg.Value() == value && arg.Domain() == domain && + arg.Path() == "/" && arg.IsSecure() && !arg.IsHttpOnly(); +} + +void RunSetCookieCallbackWithSuccess( + const net::CanonicalCookie&, + const std::string&, + const net::CookieOptions&, + network::mojom::CookieManager::SetCanonicalCookieCallback callback) { + std::move(callback).Run(net::CanonicalCookie::CookieInclusionStatus::INCLUDE); +} + +class MockCookieManager + : public ::testing::StrictMock<network::TestCookieManager> { + public: + MOCK_METHOD4(SetCanonicalCookie, + void(const net::CanonicalCookie& cookie, + const std::string& source_scheme, + const net::CookieOptions& cookie_options, + SetCanonicalCookieCallback callback)); +}; + +class MockTokenService : public FakeOAuth2TokenService { + public: + MOCK_METHOD2(InvalidateTokenForMultilogin, + void(const std::string& account_id, const std::string& token)); +}; + +} // namespace + +class OAuthMultiloginHelperTest : public testing::Test { + public: + OAuthMultiloginHelperTest() : test_signin_client_(/*prefs=*/nullptr) { + std::unique_ptr<MockCookieManager> cookie_manager = + std::make_unique<MockCookieManager>(); + mock_cookie_manager_ = cookie_manager.get(); + test_signin_client_.set_cookie_manager(std::move(cookie_manager)); + } + + ~OAuthMultiloginHelperTest() override = default; + + std::unique_ptr<OAuthMultiloginHelper> CreateHelper( + const std::vector<std::string> account_ids) { + return std::make_unique<OAuthMultiloginHelper>( + &test_signin_client_, token_service(), account_ids, std::string(), + base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, + base::Unretained(this))); + } + + std::unique_ptr<OAuthMultiloginHelper> CreateHelperWithExternalCcResult( + const std::vector<std::string> account_ids) { + return std::make_unique<OAuthMultiloginHelper>( + &test_signin_client_, token_service(), account_ids, kExternalCcResult, + base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished, + base::Unretained(this))); + } + + network::TestURLLoaderFactory* url_loader() { + return test_signin_client_.GetTestURLLoaderFactory(); + } + + std::string multilogin_url() const { + return GaiaUrls::GetInstance()->oauth_multilogin_url().spec() + + "?source=ChromiumBrowser"; + } + + std::string multilogin_url_with_external_cc_result() const { + return GaiaUrls::GetInstance()->oauth_multilogin_url().spec() + + "?source=ChromiumBrowser&externalCcResult=" + kExternalCcResult; + } + + MockCookieManager* cookie_manager() { return mock_cookie_manager_; } + MockTokenService* token_service() { return &mock_token_service_; } + + protected: + void OnOAuthMultiloginFinished(signin::SetAccountsInCookieResult result) { + DCHECK(!callback_called_); + callback_called_ = true; + result_ = result; + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + + bool callback_called_ = false; + signin::SetAccountsInCookieResult result_; + + MockCookieManager* mock_cookie_manager_; // Owned by test_signin_client_ + TestSigninClient test_signin_client_; + MockTokenService mock_token_service_; +}; + +// Everything succeeds. +TEST_F(OAuthMultiloginHelperTest, Success) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + // Configure mock cookie manager: + // - check that the cookie is the expected one + // - immediately invoke the callback + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + + // Multilogin call. + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); +} + +// Multiple cookies in the multilogin response. +TEST_F(OAuthMultiloginHelperTest, MultipleCookies) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + // Configure mock cookie manager: + // - check that the cookie is the expected one + // - immediately invoke the callback + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("FOO", "FOO_value", ".google.com"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + + // Multilogin call. + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->AddResponse(multilogin_url(), + kMultiloginSuccessResponseTwoCookies); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); +} + +// Multiple cookies in the multilogin response. +TEST_F(OAuthMultiloginHelperTest, SuccessWithExternalCcResult) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelperWithExternalCcResult({kAccountId}); + + // Configure mock cookie manager: + // - check that the cookie is the expected one + // - immediately invoke the callback + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".youtube.com"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("FOO", "FOO_value", ".google.com"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + + // Multilogin call. + EXPECT_FALSE(callback_called_); + EXPECT_TRUE( + url_loader()->IsPending(multilogin_url_with_external_cc_result())); + url_loader()->AddResponse(multilogin_url_with_external_cc_result(), + kMultiloginSuccessResponseWithSecondaryDomain); + EXPECT_FALSE( + url_loader()->IsPending(multilogin_url_with_external_cc_result())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); +} + +// Failure to get the access token. +TEST_F(OAuthMultiloginHelperTest, OneAccountAccessTokenFailure) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + token_service()->IssueErrorForAllPendingRequestsForAccount( + kAccountId, + GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kPersistentError, result_); +} + +// Retry on transient errors in the multilogin call. +TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + // Configure mock cookie manager: + // - check that the cookie is the expected one + // - immediately invoke the callback + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + + // Multilogin call fails with transient error. + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->SimulateResponseForPendingRequest(multilogin_url(), + kMultiloginRetryResponse); + + // Call is retried and succeeds. + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); +} + +// Stop retrying after too many transient errors in the multilogin call. +TEST_F(OAuthMultiloginHelperTest, + OneAccountTransientMultiloginErrorMaxRetries) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + + // Multilogin call fails with transient error, retry many times. + for (int i = 0; i < kMaxFetcherRetries; ++i) { + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + EXPECT_FALSE(callback_called_); + url_loader()->SimulateResponseForPendingRequest(multilogin_url(), + kMultiloginRetryResponse); + } + + // Failure after exceeding the maximum number of retries. + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kTransientError, result_); +} + +// Persistent error in the multilogin call. +TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) { + token_service()->AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId}); + + // Issue access token. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + + // Multilogin call fails with persistent error. + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->AddResponse(multilogin_url(), "blah"); // Unexpected response. + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kPersistentError, result_); +} + +// Retry on "invalid token" in the multilogin response. +TEST_F(OAuthMultiloginHelperTest, InvalidTokenError) { + token_service()->AddAccount(kAccountId); + token_service()->AddAccount(kAccountId2); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({kAccountId, kAccountId2}); + + // Configure mock cookie manager: + // - check that the cookie is the expected one + // - immediately invoke the callback + EXPECT_CALL( + *cookie_manager(), + SetCanonicalCookie(CookieMatcher("SID", "SID_value", ".google.fr"), + "https", testing::_, testing::_)) + .WillOnce(::testing::Invoke(RunSetCookieCallbackWithSuccess)); + + // The failed access token should be invalidated. + EXPECT_CALL(*token_service(), + InvalidateTokenForMultilogin(kAccountId, kAccessToken)); + + // Issue access tokens. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + OAuth2AccessTokenConsumer::TokenResponse success_response_2; + success_response_2.access_token = kAccessToken2; + token_service()->IssueAllTokensForAccount(kAccountId2, success_response_2); + + // Multilogin call fails with invalid token for kAccountId. + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->SimulateResponseForPendingRequest( + multilogin_url(), kMultiloginInvalidTokenResponse); + + // Both tokens are retried. + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_FALSE(callback_called_); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + token_service()->IssueAllTokensForAccount(kAccountId2, success_response); + + // Multilogin succeeds the second time. + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + url_loader()->AddResponse(multilogin_url(), kMultiloginSuccessResponse); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kSuccess, result_); +} + +// Retry on "invalid token" in the multilogin response. +TEST_F(OAuthMultiloginHelperTest, InvalidTokenErrorMaxRetries) { + token_service()->AddAccount(kAccountId); + token_service()->AddAccount(kAccountId2); + std::unique_ptr<OAuthMultiloginHelper> helper = + CreateHelper({kAccountId, kAccountId2}); + + // The failed access token should be invalidated. + EXPECT_CALL(*token_service(), + InvalidateTokenForMultilogin(kAccountId, kAccessToken)) + .Times(kMaxFetcherRetries); + + // Issue access tokens. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + OAuth2AccessTokenConsumer::TokenResponse success_response_2; + success_response_2.access_token = kAccessToken2; + + // Multilogin call fails with invalid token for kAccountId. Retry many times. + for (int i = 0; i < kMaxFetcherRetries; ++i) { + token_service()->IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + token_service()->IssueAllTokensForAccount(kAccountId2, success_response_2); + + EXPECT_FALSE(callback_called_); + EXPECT_TRUE(url_loader()->IsPending(multilogin_url())); + + url_loader()->SimulateResponseForPendingRequest( + multilogin_url(), kMultiloginInvalidTokenResponse); + } + + // The maximum number of retries is reached, fail. + EXPECT_FALSE(url_loader()->IsPending(multilogin_url())); + EXPECT_TRUE(callback_called_); + EXPECT_EQ(signin::SetAccountsInCookieResult::kTransientError, result_); +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.cc b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.cc new file mode 100644 index 00000000000..d01a50aa468 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.cc @@ -0,0 +1,136 @@ +// 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. + +#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h" + +#include <algorithm> +#include <set> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "components/signin/core/browser/signin_client.h" + +namespace { + +void RecordGetAccessTokenFinished(GoogleServiceAuthError error) { + UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenFinished", error.state(), + GoogleServiceAuthError::NUM_STATES); +} + +} // namespace + +namespace signin { + +OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher( + SigninClient* signin_client, + OAuth2TokenService* token_service, + const std::vector<std::string>& account_ids, + SuccessCallback success_callback, + FailureCallback failure_callback) + : OAuth2TokenService::Consumer("oauth_multilogin_token_fetcher"), + signin_client_(signin_client), + token_service_(token_service), + account_ids_(account_ids), + success_callback_(std::move(success_callback)), + failure_callback_(std::move(failure_callback)), + weak_ptr_factory_(this) { + DCHECK(signin_client_); + DCHECK(token_service_); + DCHECK(!account_ids_.empty()); + DCHECK(success_callback_); + DCHECK(failure_callback_); + +#ifndef NDEBUG + // Check that there is no duplicate accounts. + std::set<std::string> accounts_no_duplicates(account_ids_.begin(), + account_ids_.end()); + DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size()); +#endif + + for (const std::string& account_id : account_ids_) + StartFetchingToken(account_id); +} + +OAuthMultiloginTokenFetcher::~OAuthMultiloginTokenFetcher() = default; + +void OAuthMultiloginTokenFetcher::StartFetchingToken( + const std::string& account_id) { + DCHECK(!account_id.empty()); + token_requests_.push_back( + token_service_->StartRequestForMultilogin(account_id, this)); +} + +void OAuthMultiloginTokenFetcher::OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { + std::string account_id = request->GetAccountId(); + DCHECK(account_ids_.cend() != + std::find(account_ids_.cbegin(), account_ids_.cend(), account_id)); + + const std::string token = token_response.access_token; + DCHECK(!token.empty()); + // Do not use |request| and |token_response| below this line, as they are + // deleted. + EraseRequest(request); + + const auto& inserted = + access_tokens_.insert(std::make_pair(account_id, token)); + DCHECK(inserted.second); // If this fires, we have a duplicate account. + + if (access_tokens_.size() == account_ids_.size()) { + std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs; + for (const auto& id : account_ids_) { + const auto& it = access_tokens_.find(id); + DCHECK(!it->second.empty()); + // TODO(https://crbug.com/956503): Don't assume that the account ID is the + // Gaia ID. + token_id_pairs.emplace_back(id, it->second); + } + RecordGetAccessTokenFinished(GoogleServiceAuthError::AuthErrorNone()); + std::move(success_callback_).Run(token_id_pairs); + // Do not add anything below this line, as this may be deleted. + } +} + +void OAuthMultiloginTokenFetcher::OnGetTokenFailure( + const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) { + std::string account_id = request->GetAccountId(); + VLOG(1) << "Failed to retrieve accesstoken account=" << account_id + << " error=" << error.ToString(); + if (error.IsTransientError() && + retried_requests_.find(account_id) == retried_requests_.end()) { + retried_requests_.insert(account_id); + UMA_HISTOGRAM_ENUMERATION("Signin.GetAccessTokenRetry", error.state(), + GoogleServiceAuthError::NUM_STATES); + EraseRequest(request); + // Fetching fresh access tokens requires network. + signin_client_->DelayNetworkCall( + base::BindOnce(&OAuthMultiloginTokenFetcher::StartFetchingToken, + weak_ptr_factory_.GetWeakPtr(), account_id)); + return; + } + RecordGetAccessTokenFinished(error); + // Copy the error because it is a reference owned by token_requests_. + GoogleServiceAuthError error_copy = error; + token_requests_.clear(); // Cancel pending requests. + std::move(failure_callback_).Run(error_copy); + // Do not add anything below this line, as this may be deleted. +} + +void OAuthMultiloginTokenFetcher::EraseRequest( + const OAuth2TokenService::Request* request) { + for (auto it = token_requests_.begin(); it != token_requests_.end(); ++it) { + if (it->get() == request) { + token_requests_.erase(it); + return; + } + } + NOTREACHED() << "Request not found"; +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h new file mode 100644 index 00000000000..168ba881513 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher.h @@ -0,0 +1,74 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ + +#include <map> +#include <memory> +#include <set> +#include <string> +#include <vector> + +#include "base/bind_helpers.h" +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_token_service.h" + +class SigninClient; + +namespace signin { + +// Fetches multilogin access tokens in parallel for multiple accounts. +// It is safe to delete this object from within the callbacks. +class OAuthMultiloginTokenFetcher : public OAuth2TokenService::Consumer { + public: + using SuccessCallback = base::OnceCallback<void( + const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&)>; + using FailureCallback = + base::OnceCallback<void(const GoogleServiceAuthError&)>; + + OAuthMultiloginTokenFetcher(SigninClient* signin_client, + OAuth2TokenService* token_service, + const std::vector<std::string>& account_ids, + SuccessCallback success_callback, + FailureCallback failure_callback); + + ~OAuthMultiloginTokenFetcher() override; + + private: + void StartFetchingToken(const std::string& account_id); + + // Overridden from OAuth2TokenService::Consumer. + void OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; + void OnGetTokenFailure(const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) override; + + // Helper function to remove a request from token_requests_. + void EraseRequest(const OAuth2TokenService::Request* request); + + SigninClient* signin_client_; + OAuth2TokenService* token_service_; + const std::vector<std::string> account_ids_; + + SuccessCallback success_callback_; + FailureCallback failure_callback_; + + std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_; + std::map<std::string, std::string> access_tokens_; + std::set<std::string> retried_requests_; // Requests are retried once. + + base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(OAuthMultiloginTokenFetcher); +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_OAUTH_MULTILOGIN_TOKEN_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc new file mode 100644 index 00000000000..a40df52a3d6 --- /dev/null +++ b/chromium/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc @@ -0,0 +1,226 @@ +// 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. + +#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h" + +#include <map> +#include <memory> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/test/scoped_task_environment.h" +#include "components/signin/core/browser/test_signin_client.h" +#include "google_apis/gaia/fake_oauth2_token_service.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace signin { + +namespace { + +const char kAccountId[] = "account_id"; +const char kAccessToken[] = "access_token"; + +// Status of the token fetch. +enum class FetchStatus { kSuccess, kFailure, kPending }; + +} // namespace + +class OAuthMultiloginTokenFetcherTest : public testing::Test { + public: + OAuthMultiloginTokenFetcherTest() : test_signin_client_(/*prefs=*/nullptr) {} + + ~OAuthMultiloginTokenFetcherTest() override = default; + + std::unique_ptr<OAuthMultiloginTokenFetcher> CreateFetcher( + const std::vector<std::string> account_ids) { + return std::make_unique<OAuthMultiloginTokenFetcher>( + &test_signin_client_, &token_service_, account_ids, + base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnSuccess, + base::Unretained(this)), + base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnFailure, + base::Unretained(this))); + } + + // Returns the status of the token fetch. + FetchStatus GetFetchStatus() const { + if (success_callback_called_) + return FetchStatus::kSuccess; + return failure_callback_called_ ? FetchStatus::kFailure + : FetchStatus::kPending; + } + + protected: + // Success callback for OAuthMultiloginTokenFetcher. + void OnSuccess(const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& + token_id_pairs) { + DCHECK(!success_callback_called_); + DCHECK(token_id_pairs_.empty()); + success_callback_called_ = true; + token_id_pairs_ = token_id_pairs; + } + + // Failure callback for OAuthMultiloginTokenFetcher. + void OnFailure(const GoogleServiceAuthError& error) { + DCHECK(!failure_callback_called_); + failure_callback_called_ = true; + error_ = error; + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + + bool success_callback_called_ = false; + bool failure_callback_called_ = false; + GoogleServiceAuthError error_; + std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_; + + TestSigninClient test_signin_client_; + FakeOAuth2TokenService token_service_; +}; + +TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountSuccess) { + token_service_.AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({kAccountId}); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service_.IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); + // Check result. + EXPECT_EQ(1u, token_id_pairs_.size()); + EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_); + EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_); +} + +TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) { + token_service_.AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({kAccountId}); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + token_service_.IssueErrorForAllPendingRequestsForAccount( + kAccountId, + GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus()); + EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state()); +} + +TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientError) { + token_service_.AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({kAccountId}); + // Connection failure will be retried. + token_service_.IssueErrorForAllPendingRequestsForAccount( + kAccountId, + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + // Success on retry. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + token_service_.IssueAllTokensForAccount(kAccountId, success_response); + EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); + // Check result. + EXPECT_EQ(1u, token_id_pairs_.size()); + EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_); + EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_); +} + +TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) { + token_service_.AddAccount(kAccountId); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({kAccountId}); + // Repeated connection failures. + token_service_.IssueErrorForAllPendingRequestsForAccount( + kAccountId, + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + token_service_.IssueErrorForAllPendingRequestsForAccount( + kAccountId, + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + // Stop retrying, and fail. + EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus()); + EXPECT_EQ(GoogleServiceAuthError::CONNECTION_FAILED, error_.state()); +} + +// The flow succeeds even if requests are received out of order. +TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsSuccess) { + token_service_.AddAccount("account_1"); + token_service_.AddAccount("account_2"); + token_service_.AddAccount("account_3"); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({"account_1", "account_2", "account_3"}); + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = "token_3"; + token_service_.IssueAllTokensForAccount("account_3", success_response); + success_response.access_token = "token_1"; + token_service_.IssueAllTokensForAccount("account_1", success_response); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + success_response.access_token = "token_2"; + token_service_.IssueAllTokensForAccount("account_2", success_response); + EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); + // Check result. + EXPECT_EQ(3u, token_id_pairs_.size()); + EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_); + EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_); + EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_); + EXPECT_EQ("token_1", token_id_pairs_[0].token_); + EXPECT_EQ("token_2", token_id_pairs_[1].token_); + EXPECT_EQ("token_3", token_id_pairs_[2].token_); +} + +TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsTransientError) { + token_service_.AddAccount("account_1"); + token_service_.AddAccount("account_2"); + token_service_.AddAccount("account_3"); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({"account_1", "account_2", "account_3"}); + // Connection failures will be retried. + token_service_.IssueErrorForAllPendingRequestsForAccount( + "account_1", + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + token_service_.IssueErrorForAllPendingRequestsForAccount( + "account_2", + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + token_service_.IssueErrorForAllPendingRequestsForAccount( + "account_3", + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); + // Success on retry. + OAuth2AccessTokenConsumer::TokenResponse success_response; + success_response.access_token = kAccessToken; + success_response.access_token = "token_1"; + token_service_.IssueAllTokensForAccount("account_1", success_response); + success_response.access_token = "token_2"; + token_service_.IssueAllTokensForAccount("account_2", success_response); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + success_response.access_token = "token_3"; + token_service_.IssueAllTokensForAccount("account_3", success_response); + EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus()); + // Check result. + EXPECT_EQ(3u, token_id_pairs_.size()); + EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_); + EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_); + EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_); + EXPECT_EQ("token_1", token_id_pairs_[0].token_); + EXPECT_EQ("token_2", token_id_pairs_[1].token_); + EXPECT_EQ("token_3", token_id_pairs_[2].token_); +} + +TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsPersistentError) { + token_service_.AddAccount("account_1"); + token_service_.AddAccount("account_2"); + token_service_.AddAccount("account_3"); + std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher = + CreateFetcher({"account_1", "account_2", "account_3"}); + EXPECT_EQ(FetchStatus::kPending, GetFetchStatus()); + token_service_.IssueErrorForAllPendingRequestsForAccount( + "account_2", + GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + // Fail as soon as one of the accounts is in error. + EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus()); + EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, error_.state()); +} + +} // namespace signin diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc b/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc index a32eeff5a5f..088a552ca34 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc +++ b/chromium/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc @@ -193,7 +193,7 @@ class CrOSOAuthDelegateTest : public testing::Test { } void AddSuccessfulOAuthTokenResponse() { - client_->test_url_loader_factory()->AddResponse( + client_->GetTestURLLoaderFactory()->AddResponse( GaiaUrls::GetInstance()->oauth2_token_url().spec(), GetValidTokenResponse("token", 3600)); } diff --git a/chromium/components/signin/core/browser/resources/signin_index.html b/chromium/components/signin/core/browser/resources/signin_index.html index 835d2d2c565..64d66020dcb 100644 --- a/chromium/components/signin/core/browser/resources/signin_index.html +++ b/chromium/components/signin/core/browser/resources/signin_index.html @@ -77,7 +77,7 @@ <div class="account-section"> <table class="signin-details"> <tr class="header"> - <td>Accound Id</td> + <td>Account Id</td> <td>Has refresh token</td> <td>Has persistent auth error</td> </tr> diff --git a/chromium/components/signin/core/browser/set_accounts_in_cookie_result.h b/chromium/components/signin/core/browser/set_accounts_in_cookie_result.h new file mode 100644 index 00000000000..6adba69ab8f --- /dev/null +++ b/chromium/components/signin/core/browser/set_accounts_in_cookie_result.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ + +namespace signin { + +// Result of a "Set Accounts" cookie operation. +enum class SetAccountsInCookieResult { + // The request succeeded. + kSuccess, + // The request failed, and can be retried. + kTransientError, + // The request failed and should not be retried. + kPersistentError, +}; + +} // namespace signin + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_SET_ACCOUNTS_IN_COOKIE_RESULT_H_ diff --git a/chromium/components/signin/core/browser/signin_client.h b/chromium/components/signin/core/browser/signin_client.h index 0b288a6213c..8587e23de6c 100644 --- a/chromium/components/signin/core/browser/signin_client.h +++ b/chromium/components/signin/core/browser/signin_client.h @@ -79,6 +79,9 @@ class SigninClient : public KeyedService { // Returns true if GAIA cookies are allowed in the content area. virtual bool AreSigninCookiesAllowed() = 0; + // Returns true if signin cookies are cleared on exit. + virtual bool AreSigninCookiesDeletedOnExit() = 0; + // Adds an observer to listen for changes to the state of sign in cookie // settings. virtual void AddContentSettingsObserver( @@ -92,8 +95,7 @@ class SigninClient : public KeyedService { // Creates a new platform-specific GaiaAuthFetcher. virtual std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher( GaiaAuthConsumer* consumer, - gaia::GaiaSource source, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) = 0; + gaia::GaiaSource source) = 0; // Schedules migration to happen at next startup. virtual void SetReadyForDiceMigration(bool is_ready) {} diff --git a/chromium/components/signin/core/browser/signin_error_controller.cc b/chromium/components/signin/core/browser/signin_error_controller.cc index 9b2e0b98035..e3e4668f03e 100644 --- a/chromium/components/signin/core/browser/signin_error_controller.cc +++ b/chromium/components/signin/core/browser/signin_error_controller.cc @@ -82,7 +82,7 @@ bool SigninErrorController::UpdateSecondaryAccountErrors( // account id, use that error. Otherwise, just take the first actionable // error we find. bool error_changed = false; - for (const AccountInfo& account_info : + for (const CoreAccountInfo& account_info : identity_manager_->GetAccountsWithRefreshTokens()) { std::string account_id = account_info.account_id; 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 a8935aac517..61487c836db 100644 --- a/chromium/components/signin/core/browser/signin_header_helper_unittest.cc +++ b/chromium/components/signin/core/browser/signin_header_helper_unittest.cc @@ -41,8 +41,7 @@ class SigninHeaderHelperTest : public testing::Test { HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); settings_map_ = new HostContentSettingsMap( - &prefs_, false /* incognito_profile */, false /* guest_profile */, - false /* store_last_modified */, + &prefs_, false /* is_off_the_record */, false /* store_last_modified */, false /* migrate_requesting_and_top_level_origin_settings */); cookie_settings_ = new content_settings::CookieSettings(settings_map_.get(), &prefs_, ""); diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc index 09759330bcf..d97a3e1c54a 100644 --- a/chromium/components/signin/core/browser/signin_manager.cc +++ b/chromium/components/signin/core/browser/signin_manager.cc @@ -8,14 +8,12 @@ #include <vector> #include "base/bind.h" -#include "base/metrics/histogram_macros.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/identity_utils.h" #include "components/signin/core/browser/signin_metrics.h" #include "components/signin/core/browser/signin_pref_names.h" @@ -31,9 +29,11 @@ SigninManager::SigninManager( AccountTrackerService* account_tracker_service, GaiaCookieManagerService* cookie_manager_service, signin::AccountConsistencyMethod account_consistency) - : SigninManagerBase(client, token_service, account_tracker_service), - cookie_manager_service_(cookie_manager_service), - account_consistency_(account_consistency), + : SigninManagerBase(client, + token_service, + account_tracker_service, + cookie_manager_service, + account_consistency), weak_pointer_factory_(this) {} SigninManager::~SigninManager() { @@ -41,104 +41,6 @@ SigninManager::~SigninManager() { local_state_pref_registrar_.RemoveAll(); } -void SigninManager::SignOut( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric) { - RemoveAccountsOption remove_option = - (account_consistency_ == signin::AccountConsistencyMethod::kDice) - ? RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError - : RemoveAccountsOption::kRemoveAllAccounts; - StartSignOut(signout_source_metric, signout_delete_metric, remove_option); -} - -void SigninManager::SignOutAndRemoveAllAccounts( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric) { - StartSignOut(signout_source_metric, signout_delete_metric, - RemoveAccountsOption::kRemoveAllAccounts); -} - -void SigninManager::SignOutAndKeepAllAccounts( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric) { - StartSignOut(signout_source_metric, signout_delete_metric, - RemoveAccountsOption::kKeepAllAccounts); -} - -void SigninManager::StartSignOut( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option) { - signin_client()->PreSignOut( - base::BindOnce(&SigninManager::OnSignoutDecisionReached, - base::Unretained(this), signout_source_metric, - signout_delete_metric, remove_option), - signout_source_metric); -} - -void SigninManager::OnSignoutDecisionReached( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option, - SigninClient::SignoutDecision signout_decision) { - DCHECK(IsInitialized()); - - signin_metrics::LogSignout(signout_source_metric, signout_delete_metric); - if (!IsAuthenticated()) { - return; - } - - // TODO(crbug.com/887756): Consider moving this higher up, or document why - // the above blocks are exempt from the |signout_decision| early return. - if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) { - DVLOG(1) << "Ignoring attempt to sign out while signout disallowed"; - return; - } - - AccountInfo account_info = GetAuthenticatedAccountInfo(); - const std::string account_id = GetAuthenticatedAccountId(); - const std::string username = account_info.email; - const base::Time signin_time = - base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromMicroseconds( - signin_client()->GetPrefs()->GetInt64(prefs::kSignedInTime))); - ClearAuthenticatedAccountId(); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId); - signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); - signin_client()->GetPrefs()->ClearPref(prefs::kSignedInTime); - - // Determine the duration the user was logged in and log that to UMA. - if (!signin_time.is_null()) { - base::TimeDelta signed_in_duration = base::Time::Now() - signin_time; - UMA_HISTOGRAM_COUNTS_1M("Signin.SignedInDurationBeforeSignout", - signed_in_duration.InMinutes()); - } - - // Revoke all tokens before sending signed_out notification, because there - // may be components that don't listen for token service events when the - // profile is not connected to an account. - switch (remove_option) { - case RemoveAccountsOption::kRemoveAllAccounts: - VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out, " - << "IsSigninAllowed: " << IsSigninAllowed(); - token_service()->RevokeAllCredentials( - signin_metrics::SourceForRefreshTokenOperation:: - kSigninManager_ClearPrimaryAccount); - break; - case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError: - if (token_service()->RefreshTokenHasError(account_id)) - token_service()->RevokeCredentials( - account_id, signin_metrics::SourceForRefreshTokenOperation:: - kSigninManager_ClearPrimaryAccount); - break; - case RemoveAccountsOption::kKeepAllAccounts: - // Do nothing. - break; - } - - FireGoogleSignedOut(account_info); -} - void SigninManager::FinalizeInitBeforeLoadingRefreshTokens( PrefService* local_state) { // local_state can be null during unit tests. @@ -201,9 +103,11 @@ void SigninManager::SetSigninAllowed(bool allowed) { } void SigninManager::OnSigninAllowedPrefChanged() { - if (!IsSigninAllowed() && IsAuthenticated()) + if (!IsSigninAllowed() && IsAuthenticated()) { + VLOG(0) << "IsSigninAllowed() set to false, signing out the user"; SignOut(signin_metrics::SIGNOUT_PREF_CHANGED, signin_metrics::SignoutDelete::IGNORE_METRIC); + } } // static @@ -250,12 +154,6 @@ void SigninManager::FireGoogleSigninSucceeded() { } } -void SigninManager::FireGoogleSignedOut(const AccountInfo& account_info) { - if (observer_ != nullptr) { - observer_->GoogleSignedOut(account_info); - } -} - void SigninManager::OnRefreshTokensLoaded() { token_service()->RemoveObserver(this); diff --git a/chromium/components/signin/core/browser/signin_manager.h b/chromium/components/signin/core/browser/signin_manager.h index 5efd7f4bfc2..c70c9d93b6b 100644 --- a/chromium/components/signin/core/browser/signin_manager.h +++ b/chromium/components/signin/core/browser/signin_manager.h @@ -30,16 +30,13 @@ #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_member.h" -#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" -#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_manager_base.h" #include "components/signin/core/browser/signin_metrics.h" #include "net/cookies/canonical_cookie.h" -class GaiaCookieManagerService; class PrefService; namespace identity { @@ -49,16 +46,6 @@ class IdentityManager; class SigninManager : public SigninManagerBase, public OAuth2TokenService::Observer { public: - // Used to remove accounts from the token service and the account tracker. - enum class RemoveAccountsOption { - // Do not remove accounts. - kKeepAllAccounts, - // Remove all the accounts. - kRemoveAllAccounts, - // Removes the authenticated account if it is in authentication error. - kRemoveAuthenticatedAccountIfInError - }; - // This is used to distinguish URLs belonging to the special web signin flow // running in the special signin process from other URLs on the same domain. // We do not grant WebUI privilieges / bindings to this process or to URLs of @@ -78,30 +65,6 @@ class SigninManager : public SigninManagerBase, // are actually SigninManager instances. static SigninManager* FromSigninManagerBase(SigninManagerBase* manager); - // Signs a user out, removing the preference, erasing all keys - // associated with the authenticated user, and canceling all auth in progress. - // On mobile and on desktop pre-DICE, this also removes all accounts from - // Chrome by revoking all refresh tokens. - // On desktop with DICE enabled, this will remove the authenticated account - // from Chrome only if it is in authentication error. No other accounts are - // removed. - void SignOut(signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric); - - // Signs a user out, removing the preference, erasing all keys - // associated with the authenticated user, and canceling all auth in progress. - // It removes all accounts from Chrome by revoking all refresh tokens. - void SignOutAndRemoveAllAccounts( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric); - - // Signs a user out, removing the preference, erasing all keys - // associated with the authenticated user, and canceling all auth in progress. - // Does not remove the accounts from the token service. - void SignOutAndKeepAllAccounts( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric); - // On platforms where SigninManager is responsible for dealing with // invalid username policy updates, we need to check this during // initialization and sign the user out. @@ -121,14 +84,6 @@ class SigninManager : public SigninManagerBase, // Sets whether sign-in is allowed or not. void SetSigninAllowed(bool allowed); - protected: - // The sign out process which is started by SigninClient::PreSignOut() - virtual void OnSignoutDecisionReached( - signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option, - SigninClient::SignoutDecision signout_decision); - private: friend class identity::IdentityManager; FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, Prohibited); @@ -137,26 +92,15 @@ class SigninManager : public SigninManagerBase, // Send all observers |GoogleSigninSucceeded| notifications. void FireGoogleSigninSucceeded(); - // Send all observers |GoogleSignedOut| notifications. - void FireGoogleSignedOut(const AccountInfo& account_info); - // OAuth2TokenService::Observer: void OnRefreshTokensLoaded() override; - // Starts the sign out process. - void StartSignOut(signin_metrics::ProfileSignout signout_source_metric, - signin_metrics::SignoutDelete signout_delete_metric, - RemoveAccountsOption remove_option); - void OnSigninAllowedPrefChanged(); void OnGoogleServicesUsernamePatternChanged(); // Returns true if the passed username is allowed by policy. bool IsAllowedUsername(const std::string& username) const; - // Object used to use the token to push a GAIA cookie into the cookie jar. - GaiaCookieManagerService* cookie_manager_service_; - // Helper object to listen for changes to signin preferences stored in non- // profile-specific local prefs (like kGoogleServicesUsernamePattern). PrefChangeRegistrar local_state_pref_registrar_; @@ -164,8 +108,6 @@ class SigninManager : public SigninManagerBase, // Helper object to listen for changes to the signin allowed preference. BooleanPrefMember signin_allowed_; - signin::AccountConsistencyMethod account_consistency_; - base::WeakPtrFactory<SigninManager> weak_pointer_factory_; DISALLOW_COPY_AND_ASSIGN(SigninManager); diff --git a/chromium/components/signin/core/browser/signin_manager_base.cc b/chromium/components/signin/core/browser/signin_manager_base.cc index e4af9362091..37ee80721d5 100644 --- a/chromium/components/signin/core/browser/signin_manager_base.cc +++ b/chromium/components/signin/core/browser/signin_manager_base.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/memory/ref_counted.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -16,6 +17,7 @@ #include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_pref_names.h" @@ -27,11 +29,14 @@ SigninManagerBase::SigninManagerBase( SigninClient* client, ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service) + AccountTrackerService* account_tracker_service, + GaiaCookieManagerService* cookie_manager_service, + signin::AccountConsistencyMethod account_consistency) : client_(client), token_service_(token_service), account_tracker_service_(account_tracker_service), initialized_(false), + account_consistency_(account_consistency), weak_pointer_factory_(this) { DCHECK(client_); DCHECK(account_tracker_service_); @@ -218,10 +223,17 @@ void SigninManagerBase::SetAuthenticatedAccountId( // Commit authenticated account info immediately so that it does not get lost // if Chrome crashes before the next commit interval. client_->GetPrefs()->CommitPendingWrite(); + + if (observer_) { + observer_->AuthenticatedAccountSet(info); + } } void SigninManagerBase::ClearAuthenticatedAccountId() { authenticated_account_id_.clear(); + if (observer_) { + observer_->AuthenticatedAccountCleared(); + } } bool SigninManagerBase::IsAuthenticated() const { @@ -237,3 +249,108 @@ void SigninManagerBase::ClearObserver() { DCHECK(observer_); observer_ = nullptr; } + +#if !defined(OS_CHROMEOS) +void SigninManagerBase::SignOut( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric) { + RemoveAccountsOption remove_option = + (account_consistency_ == signin::AccountConsistencyMethod::kDice) + ? RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError + : RemoveAccountsOption::kRemoveAllAccounts; + StartSignOut(signout_source_metric, signout_delete_metric, remove_option); +} + +void SigninManagerBase::SignOutAndRemoveAllAccounts( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric) { + StartSignOut(signout_source_metric, signout_delete_metric, + RemoveAccountsOption::kRemoveAllAccounts); +} + +void SigninManagerBase::SignOutAndKeepAllAccounts( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric) { + StartSignOut(signout_source_metric, signout_delete_metric, + RemoveAccountsOption::kKeepAllAccounts); +} + +void SigninManagerBase::StartSignOut( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric, + RemoveAccountsOption remove_option) { + signin_client()->PreSignOut( + base::BindOnce(&SigninManagerBase::OnSignoutDecisionReached, + base::Unretained(this), signout_source_metric, + signout_delete_metric, remove_option), + signout_source_metric); +} + +void SigninManagerBase::OnSignoutDecisionReached( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric, + RemoveAccountsOption remove_option, + SigninClient::SignoutDecision signout_decision) { + DCHECK(IsInitialized()); + + signin_metrics::LogSignout(signout_source_metric, signout_delete_metric); + if (!IsAuthenticated()) { + return; + } + + // TODO(crbug.com/887756): Consider moving this higher up, or document why + // the above blocks are exempt from the |signout_decision| early return. + if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) { + DVLOG(1) << "Ignoring attempt to sign out while signout disallowed"; + return; + } + + AccountInfo account_info = GetAuthenticatedAccountInfo(); + const std::string account_id = GetAuthenticatedAccountId(); + const std::string username = account_info.email; + const base::Time signin_time = + base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromMicroseconds( + signin_client()->GetPrefs()->GetInt64(prefs::kSignedInTime))); + ClearAuthenticatedAccountId(); + signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain); + signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId); + signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId); + signin_client()->GetPrefs()->ClearPref(prefs::kSignedInTime); + + // Determine the duration the user was logged in and log that to UMA. + if (!signin_time.is_null()) { + base::TimeDelta signed_in_duration = base::Time::Now() - signin_time; + UMA_HISTOGRAM_COUNTS_1M("Signin.SignedInDurationBeforeSignout", + signed_in_duration.InMinutes()); + } + + // Revoke all tokens before sending signed_out notification, because there + // may be components that don't listen for token service events when the + // profile is not connected to an account. + switch (remove_option) { + case RemoveAccountsOption::kRemoveAllAccounts: + VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out"; + token_service()->RevokeAllCredentials( + signin_metrics::SourceForRefreshTokenOperation:: + kSigninManager_ClearPrimaryAccount); + break; + case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError: + if (token_service()->RefreshTokenHasError(account_id)) + token_service()->RevokeCredentials( + account_id, signin_metrics::SourceForRefreshTokenOperation:: + kSigninManager_ClearPrimaryAccount); + break; + case RemoveAccountsOption::kKeepAllAccounts: + // Do nothing. + break; + } + + FireGoogleSignedOut(account_info); +} + +void SigninManagerBase::FireGoogleSignedOut(const AccountInfo& account_info) { + if (observer_ != nullptr) { + observer_->GoogleSignedOut(account_info); + } +} +#endif // !defined(OS_CHROMEOS) diff --git a/chromium/components/signin/core/browser/signin_manager_base.h b/chromium/components/signin/core/browser/signin_manager_base.h index 6d04878c264..9297ca097fe 100644 --- a/chromium/components/signin/core/browser/signin_manager_base.h +++ b/chromium/components/signin/core/browser/signin_manager_base.h @@ -33,10 +33,13 @@ #include "base/observer_list.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_member.h" +#include "components/signin/core/browser/account_consistency_method.h" #include "components/signin/core/browser/account_info.h" +#include "components/signin/core/browser/signin_client.h" #include "google_apis/gaia/google_service_auth_error.h" class AccountTrackerService; +class GaiaCookieManagerService; class PrefRegistrySimple; class PrefService; class ProfileOAuth2TokenService; @@ -53,6 +56,14 @@ class SigninManagerBase { // Called when the currently signed-in user for a user has been signed out. virtual void GoogleSignedOut(const AccountInfo& account_info) {} + // Called during the signin as soon as + // SigninManagerBase::authenticated_account_id_ is set. + virtual void AuthenticatedAccountSet(const AccountInfo& account_info) {} + + // Called during the signout as soon as + // SigninManagerBase::authenticated_account_id_ is cleared. + virtual void AuthenticatedAccountCleared() {} + protected: virtual ~Observer() {} @@ -73,11 +84,25 @@ class SigninManagerBase { #endif SigninManagerBase(SigninClient* client, ProfileOAuth2TokenService* token_service, - AccountTrackerService* account_tracker_service); + AccountTrackerService* account_tracker_service, + GaiaCookieManagerService* cookie_manager_service, + signin::AccountConsistencyMethod account_consistency); #if !defined(OS_CHROMEOS) public: #endif +#if !defined(OS_CHROMEOS) + // Used to remove accounts from the token service and the account tracker. + enum class RemoveAccountsOption { + // Do not remove accounts. + kKeepAllAccounts, + // Remove all the accounts. + kRemoveAllAccounts, + // Removes the authenticated account if it is in authentication error. + kRemoveAuthenticatedAccountIfInError + }; +#endif + virtual ~SigninManagerBase(); // Registers per-profile prefs. @@ -122,6 +147,34 @@ class SigninManagerBase { void SetObserver(Observer* observer); void ClearObserver(); + // Signout API surfaces (not supported on ChromeOS, where signout is not + // permitted). +#if !defined(OS_CHROMEOS) + // Signs a user out, removing the preference, erasing all keys + // associated with the authenticated user, and canceling all auth in progress. + // On mobile and on desktop pre-DICE, this also removes all accounts from + // Chrome by revoking all refresh tokens. + // On desktop with DICE enabled, this will remove the authenticated account + // from Chrome only if it is in authentication error. No other accounts are + // removed. + void SignOut(signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric); + + // Signs a user out, removing the preference, erasing all keys + // associated with the authenticated user, and canceling all auth in progress. + // It removes all accounts from Chrome by revoking all refresh tokens. + void SignOutAndRemoveAllAccounts( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric); + + // Signs a user out, removing the preference, erasing all keys + // associated with the authenticated user, and canceling all auth in progress. + // Does not remove the accounts from the token service. + void SignOutAndKeepAllAccounts( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric); +#endif + protected: SigninClient* signin_client() const { return client_; } @@ -160,6 +213,23 @@ class SigninManagerBase { // SigninManagerBase. friend class SigninManager; +#if !defined(OS_CHROMEOS) + // Starts the sign out process. + void StartSignOut(signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric, + RemoveAccountsOption remove_option); + + // The sign out process which is started by SigninClient::PreSignOut() + void OnSignoutDecisionReached( + signin_metrics::ProfileSignout signout_source_metric, + signin_metrics::SignoutDelete signout_delete_metric, + RemoveAccountsOption remove_option, + SigninClient::SignoutDecision signout_decision); + + // Send all observers |GoogleSignedOut| notifications. + void FireGoogleSignedOut(const AccountInfo& account_info); +#endif + SigninClient* client_; // The ProfileOAuth2TokenService instance associated with this object. Must @@ -167,6 +237,7 @@ class SigninManagerBase { ProfileOAuth2TokenService* token_service_; AccountTrackerService* account_tracker_service_; + bool initialized_; // Account id after successful authentication. @@ -175,6 +246,8 @@ class SigninManagerBase { // The list of callbacks notified on shutdown. base::CallbackList<void()> on_shutdown_callback_list_; + signin::AccountConsistencyMethod account_consistency_; + base::WeakPtrFactory<SigninManagerBase> weak_pointer_factory_; DISALLOW_COPY_AND_ASSIGN(SigninManagerBase); diff --git a/chromium/components/signin/core/browser/test_signin_client.cc b/chromium/components/signin/core/browser/test_signin_client.cc index 237beae6d9b..3a74d1ada5c 100644 --- a/chromium/components/signin/core/browser/test_signin_client.cc +++ b/chromium/components/signin/core/browser/test_signin_client.cc @@ -12,8 +12,11 @@ #include "services/network/test/test_cookie_manager.h" #include "testing/gtest/include/gtest/gtest.h" -TestSigninClient::TestSigninClient(PrefService* pref_service) - : pref_service_(pref_service), +TestSigninClient::TestSigninClient( + PrefService* pref_service, + network::TestURLLoaderFactory* test_url_loader_factory) + : test_url_loader_factory_(test_url_loader_factory), + pref_service_(pref_service), are_signin_cookies_allowed_(true), network_calls_delayed_(false), is_signout_allowed_(true), @@ -37,7 +40,7 @@ void TestSigninClient::PreSignOut( scoped_refptr<network::SharedURLLoaderFactory> TestSigninClient::GetURLLoaderFactory() { - return test_url_loader_factory_.GetSafeWeakWrapper(); + return GetTestURLLoaderFactory()->GetSafeWeakWrapper(); } network::mojom::CookieManager* TestSigninClient::GetCookieManager() { @@ -46,6 +49,25 @@ network::mojom::CookieManager* TestSigninClient::GetCookieManager() { return cookie_manager_.get(); } +network::TestURLLoaderFactory* TestSigninClient::GetTestURLLoaderFactory() { + if (test_url_loader_factory_) + return test_url_loader_factory_; + + if (!default_test_url_loader_factory_) { + default_test_url_loader_factory_ = + std::make_unique<network::TestURLLoaderFactory>(); + } + + return default_test_url_loader_factory_.get(); +} + +void TestSigninClient::OverrideTestUrlLoaderFactory( + network::TestURLLoaderFactory* factory) { + DCHECK(!default_test_url_loader_factory_); + DCHECK(!test_url_loader_factory_); + test_url_loader_factory_ = factory; +} + std::string TestSigninClient::GetProductVersion() { return ""; } void TestSigninClient::SetNetworkCallsDelayed(bool value) { @@ -70,6 +92,10 @@ bool TestSigninClient::AreSigninCookiesAllowed() { return are_signin_cookies_allowed_; } +bool TestSigninClient::AreSigninCookiesDeletedOnExit() { + return false; +} + void TestSigninClient::AddContentSettingsObserver( content_settings::Observer* observer) { } @@ -88,10 +114,9 @@ void TestSigninClient::DelayNetworkCall(base::OnceClosure callback) { std::unique_ptr<GaiaAuthFetcher> TestSigninClient::CreateGaiaAuthFetcher( GaiaAuthConsumer* consumer, - gaia::GaiaSource source, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) { + gaia::GaiaSource source) { return std::make_unique<GaiaAuthFetcher>(consumer, source, - url_loader_factory); + GetURLLoaderFactory()); } void TestSigninClient::PreGaiaLogout(base::OnceClosure callback) { diff --git a/chromium/components/signin/core/browser/test_signin_client.h b/chromium/components/signin/core/browser/test_signin_client.h index 103cf5999e4..2347b86a978 100644 --- a/chromium/components/signin/core/browser/test_signin_client.h +++ b/chromium/components/signin/core/browser/test_signin_client.h @@ -27,7 +27,9 @@ class PrefService; // part of its interface. class TestSigninClient : public SigninClient { public: - TestSigninClient(PrefService* pref_service); + TestSigninClient( + PrefService* pref_service, + network::TestURLLoaderFactory* test_url_loader_factory = nullptr); ~TestSigninClient() override; // SigninClient implementation that is specialized for unit tests. @@ -57,9 +59,12 @@ class TestSigninClient : public SigninClient { cookie_manager_ = std::move(cookie_manager); } - network::TestURLLoaderFactory* test_url_loader_factory() { - return &test_url_loader_factory_; - } + // Returns |test_url_loader_factory_| if it is specified. Otherwise, lazily + // creates a default factory and returns it. + network::TestURLLoaderFactory* GetTestURLLoaderFactory(); + + // Pass a TestURLLoader factory to use instead of the default one. + void OverrideTestUrlLoaderFactory(network::TestURLLoaderFactory* factory); void set_are_signin_cookies_allowed(bool value) { are_signin_cookies_allowed_ = value; @@ -79,6 +84,7 @@ class TestSigninClient : public SigninClient { bool IsFirstRun() const override; base::Time GetInstallDate() override; bool AreSigninCookiesAllowed() override; + bool AreSigninCookiesDeletedOnExit() override; void AddContentSettingsObserver( content_settings::Observer* observer) override; void RemoveContentSettingsObserver( @@ -86,14 +92,14 @@ class TestSigninClient : public SigninClient { void DelayNetworkCall(base::OnceClosure callback) override; std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher( GaiaAuthConsumer* consumer, - gaia::GaiaSource source, - scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) - override; + gaia::GaiaSource source) override; void PreGaiaLogout(base::OnceClosure callback) override; void SetReadyForDiceMigration(bool ready) override; private: - network::TestURLLoaderFactory test_url_loader_factory_; + std::unique_ptr<network::TestURLLoaderFactory> + default_test_url_loader_factory_; + network::TestURLLoaderFactory* test_url_loader_factory_; PrefService* pref_service_; std::unique_ptr<network::mojom::CookieManager> cookie_manager_; 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 45d33c881e4..0a2e3d0648f 100644 --- a/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm +++ b/chromium/components/signin/ios/browser/account_consistency_service_unittest.mm @@ -130,13 +130,13 @@ class AccountConsistencyServiceTest : public PlatformTest { HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); web_view_load_expection_count_ = 0; - signin_client_.reset(new TestSigninClient(&prefs_)); + signin_client_.reset( + new TestSigninClient(&prefs_, &test_url_loader_factory_)); identity_test_env_.reset(new identity::IdentityTestEnvironment( - &test_url_loader_factory_, &prefs_, + /*test_url_loader_factory=*/nullptr, &prefs_, signin::AccountConsistencyMethod::kDisabled, signin_client_.get())); settings_map_ = new HostContentSettingsMap( - &prefs_, false /* incognito_profile */, false /* guest_profile */, - false /* store_last_modified */, + &prefs_, false /* is_off_the_record */, false /* store_last_modified */, false /* migrate_requesting_and_top_level_origin_settings */); cookie_settings_ = new content_settings::CookieSettings(settings_map_.get(), &prefs_, ""); |